// 
// Decompiled by Procyon v0.6.0
// 

package org.fusesource.jansi.internal;

import java.net.URL;
import java.util.Properties;
import java.util.List;
import java.util.LinkedList;
import java.util.Random;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.nio.file.CopyOption;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.io.IOException;
import java.io.InputStream;
import java.io.FilenameFilter;
import java.io.File;

public class JansiLoader
{
    private static boolean loaded;
    private static String nativeLibraryPath;
    private static String nativeLibrarySourceUrl;
    
    public static synchronized boolean initialize() {
        if (!JansiLoader.loaded) {
            final Thread cleanup = new Thread(JansiLoader::cleanup, "cleanup");
            cleanup.setPriority(1);
            cleanup.setDaemon(true);
            cleanup.start();
        }
        try {
            loadJansiNativeLibrary();
        }
        catch (final Exception e) {
            if (!Boolean.parseBoolean(System.getProperty("jansi.graceful", "true"))) {
                throw new RuntimeException("Unable to load jansi native library. You may want set the `jansi.graceful` system property to true to be able to use Jansi on your platform", e);
            }
        }
        return JansiLoader.loaded;
    }
    
    public static String getNativeLibraryPath() {
        return JansiLoader.nativeLibraryPath;
    }
    
    public static String getNativeLibrarySourceUrl() {
        return JansiLoader.nativeLibrarySourceUrl;
    }
    
    private static File getTempDir() {
        return new File(System.getProperty("jansi.tmpdir", System.getProperty("java.io.tmpdir")));
    }
    
    static void cleanup() {
        final String tempFolder = getTempDir().getAbsolutePath();
        final File dir = new File(tempFolder);
        final File[] nativeLibFiles = dir.listFiles(new FilenameFilter() {
            private final String searchPattern = "jansi-" + JansiLoader.getVersion();
            
            @Override
            public boolean accept(final File dir, final String name) {
                return name.startsWith(this.searchPattern) && !name.endsWith(".lck");
            }
        });
        if (nativeLibFiles != null) {
            for (final File nativeLibFile : nativeLibFiles) {
                final File lckFile = new File(nativeLibFile.getAbsolutePath() + ".lck");
                if (!lckFile.exists()) {
                    try {
                        nativeLibFile.delete();
                    }
                    catch (final SecurityException e) {
                        System.err.println("Failed to delete old native lib" + e.getMessage());
                    }
                }
            }
        }
    }
    
    private static int readNBytes(final InputStream in, final byte[] b) throws IOException {
        int n = 0;
        int count;
        for (int len = b.length; n < len; n += count) {
            count = in.read(b, n, len - n);
            if (count <= 0) {
                break;
            }
        }
        return n;
    }
    
    private static String contentsEquals(final InputStream in1, final InputStream in2) throws IOException {
        final byte[] buffer1 = new byte[8192];
        final byte[] buffer2 = new byte[8192];
        do {
            final int numRead1 = readNBytes(in1, buffer1);
            final int numRead2 = readNBytes(in2, buffer2);
            if (numRead1 > 0) {
                if (numRead2 <= 0) {
                    return "EOF on second stream but not first";
                }
                if (numRead2 != numRead1) {
                    return "Read size different (" + numRead1 + " vs " + numRead2 + ")";
                }
                continue;
            }
            else {
                if (numRead2 > 0) {
                    return "EOF on first stream but not second";
                }
                return null;
            }
        } while (Arrays.equals(buffer1, buffer2));
        return "Content differs";
    }
    
    private static boolean extractAndLoadLibraryFile(final String libFolderForCurrentOS, final String libraryFileName, final String targetFolder) {
        final String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName;
        final String uuid = randomUUID();
        final String extractedLibFileName = String.format("jansi-%s-%s-%s", getVersion(), uuid, libraryFileName);
        final String extractedLckFileName = extractedLibFileName + ".lck";
        final File extractedLibFile = new File(targetFolder, extractedLibFileName);
        final File extractedLckFile = new File(targetFolder, extractedLckFileName);
        try {
            try (final InputStream in = JansiLoader.class.getResourceAsStream(nativeLibraryFilePath)) {
                if (!extractedLckFile.exists()) {
                    new FileOutputStream(extractedLckFile).close();
                }
                Files.copy(in, extractedLibFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            }
            finally {
                extractedLibFile.deleteOnExit();
                extractedLckFile.deleteOnExit();
            }
            extractedLibFile.setReadable(true);
            extractedLibFile.setWritable(true);
            extractedLibFile.setExecutable(true);
            try (final InputStream nativeIn = JansiLoader.class.getResourceAsStream(nativeLibraryFilePath);
                 final InputStream extractedLibIn = new FileInputStream(extractedLibFile)) {
                final String eq = contentsEquals(nativeIn, extractedLibIn);
                if (eq != null) {
                    throw new RuntimeException(String.format("Failed to write a native library file at %s because %s", extractedLibFile, eq));
                }
            }
            if (loadNativeLibrary(extractedLibFile)) {
                JansiLoader.nativeLibrarySourceUrl = JansiLoader.class.getResource(nativeLibraryFilePath).toExternalForm();
                return true;
            }
        }
        catch (final IOException e) {
            System.err.println(e.getMessage());
        }
        return false;
    }
    
    private static String randomUUID() {
        return Long.toHexString(new Random().nextLong());
    }
    
    private static boolean loadNativeLibrary(final File libPath) {
        if (libPath.exists()) {
            try {
                final String path = libPath.getAbsolutePath();
                System.load(path);
                JansiLoader.nativeLibraryPath = path;
                return true;
            }
            catch (final UnsatisfiedLinkError e) {
                if (!libPath.canExecute()) {
                    System.err.printf("Failed to load native library:%s. The native library file at %s is not executable, make sure that the directory is mounted on a partition without the noexec flag, or set the jansi.tmpdir system property to point to a proper location.  osinfo: %s%n", libPath.getName(), libPath, OSInfo.getNativeLibFolderPathForCurrentOS());
                }
                else {
                    System.err.printf("Failed to load native library:%s. osinfo: %s%n", libPath.getName(), OSInfo.getNativeLibFolderPathForCurrentOS());
                }
                System.err.println(e);
                return false;
            }
        }
        return false;
    }
    
    private static void loadJansiNativeLibrary() throws Exception {
        if (JansiLoader.loaded) {
            return;
        }
        final List<String> triedPaths = new LinkedList<String>();
        String jansiNativeLibraryPath = System.getProperty("library.jansi.path");
        String jansiNativeLibraryName = System.getProperty("library.jansi.name");
        if (jansiNativeLibraryName == null) {
            jansiNativeLibraryName = System.mapLibraryName("jansi");
            assert jansiNativeLibraryName != null;
            if (jansiNativeLibraryName.endsWith(".dylib")) {
                jansiNativeLibraryName = jansiNativeLibraryName.replace(".dylib", ".jnilib");
            }
        }
        if (jansiNativeLibraryPath != null) {
            final String withOs = jansiNativeLibraryPath + "/" + OSInfo.getNativeLibFolderPathForCurrentOS();
            if (loadNativeLibrary(new File(withOs, jansiNativeLibraryName))) {
                JansiLoader.loaded = true;
                return;
            }
            triedPaths.add(withOs);
            if (loadNativeLibrary(new File(jansiNativeLibraryPath, jansiNativeLibraryName))) {
                JansiLoader.loaded = true;
                return;
            }
            triedPaths.add(jansiNativeLibraryPath);
        }
        final String packagePath = JansiLoader.class.getPackage().getName().replace('.', '/');
        jansiNativeLibraryPath = String.format("/%s/native/%s", packagePath, OSInfo.getNativeLibFolderPathForCurrentOS());
        final boolean hasNativeLib = hasResource(jansiNativeLibraryPath + "/" + jansiNativeLibraryName);
        if (hasNativeLib) {
            final String tempFolder = getTempDir().getAbsolutePath();
            if (extractAndLoadLibraryFile(jansiNativeLibraryPath, jansiNativeLibraryName, tempFolder)) {
                JansiLoader.loaded = true;
                return;
            }
            triedPaths.add(jansiNativeLibraryPath);
        }
        final String javaLibraryPath = System.getProperty("java.library.path", "");
        for (final String ldPath : javaLibraryPath.split(File.pathSeparator)) {
            if (!ldPath.isEmpty()) {
                if (loadNativeLibrary(new File(ldPath, jansiNativeLibraryName))) {
                    JansiLoader.loaded = true;
                    return;
                }
                triedPaths.add(ldPath);
            }
        }
        throw new Exception(String.format("No native library found for os.name=%s, os.arch=%s, paths=[%s]", OSInfo.getOSName(), OSInfo.getArchName(), String.join(File.pathSeparator, triedPaths)));
    }
    
    private static boolean hasResource(final String path) {
        return JansiLoader.class.getResource(path) != null;
    }
    
    public static int getMajorVersion() {
        final String[] c = getVersion().split("\\.");
        return (c.length > 0) ? Integer.parseInt(c[0]) : 1;
    }
    
    public static int getMinorVersion() {
        final String[] c = getVersion().split("\\.");
        return (c.length > 1) ? Integer.parseInt(c[1]) : 0;
    }
    
    public static String getVersion() {
        final URL versionFile = JansiLoader.class.getResource("/org/fusesource/jansi/jansi.properties");
        String version = "unknown";
        try {
            if (versionFile != null) {
                final Properties versionData = new Properties();
                versionData.load(versionFile.openStream());
                version = versionData.getProperty("version", version);
                version = version.trim().replaceAll("[^0-9.]", "");
            }
        }
        catch (final IOException e) {
            System.err.println(e);
        }
        return version;
    }
    
    static {
        JansiLoader.loaded = false;
    }
}
