package org.svetovid.installer;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import org.svetovid.Svetovid;
import org.svetovid.io.SvetovidProcess;
import org.svetovid.run.SvetovidProcessBuilder;
import org.svetovid.util.Version;

public final class JavaInstallation implements Comparable<JavaInstallation> {

    public static final String LIB_FILE_NAME = "svetovid-lib.jar";

    private Path location;
    private Path binLocation;
    private Path libLocation;

    private String jdkVersion;
    private String jreVersion;
    private String libVersion;

    private JavaInstallation() {
        this.location = null;
        String[] extdirs = System.getProperty("java.ext.dirs").split(File.pathSeparator);
        if (extdirs.length > 1) {
            libLocation = Paths.get(extdirs[1]);
        }
    }

    private JavaInstallation(Path location) {
        this.location = location;
        initialize();
    }

    @Override
    public int hashCode() {
        if (location == null) {
            return 0;
        }
        return location.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof JavaInstallation)) {
            return false;
        }
        Path thatLocation = ((JavaInstallation) obj).location;
        if (this.location == thatLocation) {
            return true;
        }
        if (this.location == null) {
            return false;
        }
        return this.location.equals(thatLocation);
    }

    @Override
    public int compareTo(JavaInstallation that) {
        if ((this.location == null) && (that.location == null)) {
            return 0;
        }
        if (this.location == null) {
            return -1;
        }
        if (that.location == null) {
            return 1;
        }
        return this.location.compareTo(that.location);
    }

    @Override
    public String toString() {
        if (location == null) {
            return "";
        }
        if (jdkVersion != null) {
            return "JDK " + jdkVersion;
        }
        if (jreVersion != null) {
            return "JRE " + jreVersion;
        }
        return "???";
    }

    private void initialize() {
        if (location == null) {
            return;
        }
        binLocation = libLocation = null;
        jdkVersion = jreVersion = libVersion = null;
        if (!Files.isDirectory(location)) {
            return;
        }
        binLocation = location.resolve("bin");
        if (!Files.isDirectory(binLocation)) {
            return;
        }
        Path binary = binLocation.resolve("javac");
        SvetovidProcess process = exec(binLocation, binary.toString(), "-version");
        if (process != null) {
            jdkVersion = process.err.readLine();
            jdkVersion = jdkVersion.substring(jdkVersion.indexOf(' ') + 1);
        }
        binary = binLocation.resolve("java");
        process = exec(binLocation, binary.toString(), "-version");
        if (process != null) {
            jreVersion = process.err.readLine();
            jreVersion = jreVersion.substring(jreVersion.indexOf('"') + 1, jreVersion.length() - 1);
        }
        libLocation = location.resolve("jre/lib/ext");
        if (!Files.isDirectory(libLocation)) {
            libLocation = location.resolve("lib/ext");
        }
        if (!Files.isDirectory(libLocation)) {
            return;
        }
        Path libFile = libLocation.resolve(LIB_FILE_NAME);
        if (!Files.isRegularFile(libFile)) {
            return;
        }
        try {
            ZipFile zip = new ZipFile(libFile.toString());
            try {
                ZipEntry entry = zip.getEntry("version.properties");
                if (entry != null) {
                    InputStream stream = zip.getInputStream(entry);
                    Properties properties = new Properties();
                    properties.load(stream);
                    libVersion = properties.getProperty("version");
                }
            } finally {
                zip.close();
            }
        } catch (ZipException e) {
            // Do nothing
        } catch (IOException e) {
            // Do Nothing
        }
    }

    protected static SvetovidProcess exec(String... command) {
        return exec(null, command);
    }

    protected static SvetovidProcess exec(Path directory, String... command) {
        SvetovidProcessBuilder builder = new SvetovidProcessBuilder(directory, command);
        try {
            return builder.start();
        } catch (IOException e) {
            return null;
        }
    }

    public Path getLocation() {
        return location;
    }

    public Path getBinLocation() {
        return binLocation;
    }

    public Path getLibLocation() {
        return libLocation;
    }

    public String getJdkVersion() {
        return jdkVersion;
    }

    public String getJreVersion() {
        return jreVersion;
    }

    public String getLibVersion() {
        return libVersion;
    }

    public JavaInstallationType getType() {
        if (location == null) {
            return JavaInstallationType.ALL;
        }
        if (jdkVersion != null) {
            return JavaInstallationType.JDK;
        }
        if (jreVersion != null) {
            return JavaInstallationType.JRE;
        }
        return JavaInstallationType.UNKNOWN;
    }

    public JavaInstallationVersionType getVersionType() {
        if (libVersion == null) {
            return JavaInstallationVersionType.NONE;
        }
        Version current = Svetovid.getVersion();
        Version version = new Version(libVersion);
        int cmp = version.compareTo(current);
        if (cmp == 0) {
            return JavaInstallationVersionType.EQUAL;
        }
        if (cmp < 0) {
            return JavaInstallationVersionType.OLDER;
        } else {
            return JavaInstallationVersionType.NEWER;
        }
    }

    public void refresh() {
        initialize();
    }
}

@@ -190,24 +190,74 @@
 ile.Paths;%0D%0A
+import java.util.HashMap;%0D%0Aimport java.util.Map;%0D%0A
 import java.
@@ -6544,12 +6544,641 @@
 %0D%0A    %7D%0D
+%0A%0D%0A    private static Map%3CString, JavaInstallation%3E map = new HashMap%3C%3E();%0D%0A%0D%0A    private static JavaInstallation createJavaInstallation(String path) %7B%0D%0A        Path location = Paths.get(path);%0D%0A        return createJavaInstallation(location);%0D%0A    %7D%0D%0A%0D%0A    private static JavaInstallation createJavaInstallation(Path location) %7B%0D%0A        String path = location.normalize().toString();%0D%0A        JavaInstallation installation = map.get(path);%0D%0A        if (installation == null) %7B%0D%0A            installation = new JavaInstallation(location);%0D%0A            map.put(path, installation);%0D%0A        %7D%0D%0A        return installation;%0D%0A    %7D%0D
 %0A%7D%0D%0A

Prepare index of all Java installations