package com.reason.sdk;

import com.intellij.notification.Notifications;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.io.Decompressor;
import com.reason.ORNotification;
import com.reason.WGet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;

import static com.intellij.notification.NotificationType.ERROR;

public class SdkDownloader {

    private static final Condition<String> KEEP_OCAML_SOURCES = s -> s.endsWith(".ml") || s.endsWith(".mli") || s.endsWith(".ml4") || s.endsWith(".mll") || s.endsWith(".mly");

    private final String m_sdk;
    private final String m_major;
    private final File m_sdkHome;

    public SdkDownloader(@NotNull String major, @NotNull String minor, @NotNull String patch, @NotNull VirtualFile sdkHome) {
        String canonicalPath = sdkHome.getCanonicalPath();
        assert canonicalPath != null;
        m_sdkHome = new File(canonicalPath);
        m_sdk = major + "." + minor + "." + patch;
        m_major = major + "." + minor;
    }

    public static Task modalTask(@NotNull String major, @NotNull String minor, @NotNull String patch, @NotNull VirtualFile sdkHome, @Nullable Project project) {
        return new Task.Modal(project, "Download SDK", false) {
            @Override
            public void run(@NotNull ProgressIndicator indicator) {
                new SdkDownloader(major, minor, patch, sdkHome).run(project, indicator);
            }
        };
    }

    public void run(@Nullable Project project, @NotNull ProgressIndicator indicator) {
        String sdkFilename = "ocaml-" + m_sdk + ".tar.gz";
        File targetSdkLocation = new File(m_sdkHome, sdkFilename);
        String sdkUrl = "http://caml.inria.fr/pub/distrib/ocaml-" + m_major + "/" + sdkFilename;

        indicator.setIndeterminate(true);
        indicator.setText("Download sdk from " + sdkUrl);

        boolean isDownloaded = WGet.apply(sdkUrl, targetSdkLocation, indicator, -1.0);
        if (isDownloaded) {
            try {
                indicator.setText("uncompress sdk");
                File tarPath = uncompress(targetSdkLocation);
                FileUtil.delete(targetSdkLocation);
                indicator.setText("Untar sdk");
                new Decompressor.Tar(tarPath).filter(KEEP_OCAML_SOURCES).extract(m_sdkHome);
                FileUtil.delete(tarPath);
            } catch (IOException e) {
                Notifications.Bus.notify(new ORNotification("Sdk", "Cannot download sdk, error: " + e.getMessage(), ERROR, null), project);
            }
        }
    }

    @NotNull
    private File uncompress(@NotNull File source) throws IOException {
        byte[] buffer = new byte[1024];

        String absolutePath = source.getAbsolutePath();
        String tarName = absolutePath.substring(0, absolutePath.length() - 3);

        try (GZIPInputStream is = new GZIPInputStream(new FileInputStream(source))) {
            try (FileOutputStream out = new FileOutputStream(tarName)) {
                int len;
                while ((len = is.read(buffer)) > 0) {
                    out.write(buffer, 0, len);
                }
            }
        }

        return new File(tarName);
    }

}