package net.rubygrapefruit.platform.internal;

import net.rubygrapefruit.platform.*;
import net.rubygrapefruit.platform.Process;

public abstract class Platform {
    private static Platform platform;

    public static Platform current() {
        synchronized (Platform.class) {
            if (platform == null) {
                String osName = getOperatingSystem().toLowerCase();
                String arch = getArchitecture();
                if (osName.contains("windows")) {
                    platform = new Windows();
                } else if (osName.contains("linux")) {
                    platform = new Linux();
                } else if (osName.contains("os x") && (arch.equals("i386") || arch.equals("x86_64"))) {
                    platform = new OsX();
                } else if (osName.contains("sunos")) {
                    platform = new Solaris();
                } else {
                    platform = new Unsupported();
                }
            }
            return platform;
        }
    }

    public boolean isWindows() {
        return false;
    }

    public <T extends NativeIntegration> T get(Class<T> type) {
        throw new NativeIntegrationUnavailableException(String.format("Native integration %s is not supported on this operating system (%s %s)",
                type.getSimpleName(), getOperatingSystem(), getArchitecture()));
    }

    public abstract String getLibraryName() throws NativeIntegrationUnavailableException;

    private static class Windows extends Platform {
        @Override
        public boolean isWindows() {
            return true;
        }

        @Override
        public String getLibraryName() {
            if (getArchitecture().equals("x86")) {
                return "native-platform-windows-i386.dll";
            }
            if (getArchitecture().equals("amd64")) {
                return "native-platform-windows-amd64.dll";
            }
            throw new NativeIntegrationUnavailableException(String.format(
                    "Native integration is not available for this architecture (%s) on Windows.", getArchitecture()));
        }

        @Override
        public <T extends NativeIntegration> T get(Class<T> type) {
            if (type.equals(Process.class)) {
                return type.cast(new DefaultProcess());
            }
            if (type.equals(Terminals.class)) {
                return type.cast(new WindowsTerminals());
            }
            if (type.equals(SystemInfo.class)) {
                return type.cast(new DefaultSystemInfo());
            }
            if (type.equals(FileSystems.class)) {
                return type.cast(new PosixFileSystems());
            }
            return super.get(type);
        }
    }

    private static abstract class Posix extends Platform {
        @Override
        public <T extends NativeIntegration> T get(Class<T> type) {
            if (type.equals(PosixFile.class)) {
                return type.cast(new DefaultPosixFile());
            }
            if (type.equals(Process.class)) {
                return type.cast(new DefaultProcess());
            }
            if (type.equals(Terminals.class)) {
                return type.cast(new TerminfoTerminals());
            }
            if (type.equals(SystemInfo.class)) {
                return type.cast(new DefaultSystemInfo());
            }
            return super.get(type);
        }
    }

    private abstract static class Unix extends Posix {
    }

    private static class Linux extends Unix {
        @Override
        public <T extends NativeIntegration> T get(Class<T> type) {
            if (type.equals(FileSystems.class)) {
                return type.cast(new PosixFileSystems());
            }
            return super.get(type);
        }

        @Override
        public String getLibraryName() {
            if (getArchitecture().equals("amd64")) {
                return "libnative-platform-linux-amd64.so";
            }
            if (getArchitecture().equals("i386") || getArchitecture().equals("x86")) {
                return "libnative-platform-linux-i386.so";
            }
            throw new NativeIntegrationUnavailableException(String.format(
                    "Native integration is not available for this architecture (%s) on Linux.", getArchitecture()));
        }
    }

    private static String getArchitecture() {
        return System.getProperty("os.arch");
    }

    private static class Solaris extends Unix {
        @Override
        public String getLibraryName() {
            return "libnative-platform-solaris.so";
        }
    }

    private static class OsX extends Posix {
        @Override
        public <T extends NativeIntegration> T get(Class<T> type) {
            if (type.equals(FileSystems.class)) {
                return type.cast(new PosixFileSystems());
            }
            return super.get(type);
        }

        @Override
        public String getLibraryName() {
            return "libnative-platform-osx-universal.dylib";
        }
    }

    private static class Unsupported extends Platform {
        public String getLibraryName() {
            throw new NativeIntegrationUnavailableException(String.format(
                    "Native integration is not available for this operating system (%s %s)", getOperatingSystem(),
                    getArchitecture()));
        }
    }

    private static String getOperatingSystem() {
        return System.getProperty("os.name");
    }
}

@@ -738,16 +738,40 @@
 x86_64%22)
+ %7C%7C arch.equals(%22amd64%22)
 )) %7B%0D%0A  

Support os arch 'amd64' on osx as it is used in openjdk and oracles jdk for formally known 'x86_64' architecture