/*
 * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
 */

// ***************************************************************************
// IMPORTANT: Because the media repository and the glass and javafx-common
// projects in the rt-closed repository are built independently from one
// another, there are three separate copies of this file:
//
// rt-closed: javafx-common/src/com/sun/javafx/runtime/NativeLibLoader.java
// rt-closed: glass/glass-mat/src/com/sun/glass/utils/NativeLibLoader.java
// media: jfxmedia/src/com/sun/media/jfxmediaimpl/NativeLibLoader.java
//
// All three files must be modified whenever a bug fix or enhancement is made.
// The only difference between the copies is the package name: a one line diff.
// ***************************************************************************

package com.sun.glass.utils;

import java.io.File;
import java.net.URI;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashSet;

public class NativeLibLoader {
    private static final HashSet<String> loaded = new HashSet<String>();

    public static synchronized void loadLibrary(String libname) {
        if (!loaded.contains(libname)) {
            loadLibraryInternal(libname);
            loaded.add(libname);
        }
    }

    private static boolean verbose = false;
    private static File libDir = null;
    private static String libPrefix = "";
    private static String libSuffix = "";
    private static String libSuffixAlt = null;

    static {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            public Object run() {
                verbose = Boolean.getBoolean("javafx.verbose");
                return null;
            }
        });
    }

    private static void loadLibraryInternal(String libraryName) {
        // Look for the library in the same directory as the jar file
        // containing this class.
        // If that fails, then try System.loadLibrary as a last resort.
        try {
            loadLibraryFullPath(libraryName);
        } catch (UnsatisfiedLinkError ex) {
            // Try System.loadLibrary as a last resort. If it succeeds, then
            // print a warning. If it fails, rethrow the exception from
            // the earlier System.load()
            try {
                System.loadLibrary(libraryName);
                if (verbose) {
                    System.err.println("WARNING: " + ex.toString());
                    System.err.println("    using System.loadLibrary("
                            + libraryName + ") as a fallback");
                }
            } catch (UnsatisfiedLinkError ex2) {
                // Rethrow original exception
                throw ex;
            }
        }
    }

    /**
     * Load the native library from the same directory as the jar file
     * containing this class.
     */
    private static void loadLibraryFullPath(String libraryName) {
        try {
            if (libDir == null) {
                // Get the URL for this class, if it is a jar URL, then get the
                // filename associated with it.
                String theClassFile = "NativeLibLoader.class";
                Class theClass = NativeLibLoader.class;
                String classUrlString = theClass.getResource(theClassFile).toString();
                if (!classUrlString.startsWith("jar:file:") || classUrlString.indexOf("!") == -1){
                    throw new UnsatisfiedLinkError("Invalid URL for class: " + classUrlString);
                }
                // Strip out the "jar:" and everything after and including the "!"
                String tmpStr = classUrlString.substring(4, classUrlString.lastIndexOf("!"));
                // Strip everything after the last "/" or "\" to get rid of the jar filename
                int lastIndexOfSlash = Math.max(tmpStr.lastIndexOf("/"), tmpStr.lastIndexOf("\\"));

                // Set the native directory based on the OS
                String osName = System.getProperty("os.name");
                final boolean is64bit =
                    System.getProperty("sun.arch.data.model", "").startsWith("64");
                String relativeDir = null;
                if (osName.startsWith("Windows")) {
                    relativeDir = "../bin";
                } else if (osName.startsWith("Mac")) {
                    relativeDir = ".";
                } else if (osName.startsWith("Linux")) {
                    if (is64bit) {
                        relativeDir = "amd64";
                    } else {
                        relativeDir = "i386";
                    }
                }

                // Location of native libraries relative to jar file
                String libDirUrlString = tmpStr.substring(0, lastIndexOfSlash)
                        + "/" + relativeDir;
                libDir = new File(new URI(libDirUrlString).getPath());

                // Set the lib prefix and suffix based on the OS
                if (osName.startsWith("Windows")) {
                    // BEGIN : RT-12198 : Workaround for NetBeans 32/64 support
                    if (is64bit) {
                        File libDir64 = new File(libDir, "64bit");
                        if (libDir64.isDirectory()) {
                            libDir = libDir64;
                        }
                    }
                    // END : RT-12198 : Workaround for NetBeans 32/64 support

                    libPrefix = "";
                    libSuffix = ".dll";
                } else if (osName.startsWith("Mac")) {
                    libPrefix = "lib";
                    libSuffix = ".dylib";
                    libSuffixAlt = ".jnilib";
                } else if (osName.startsWith("Linux")) {
                    libPrefix = "lib";
                    libSuffix = ".so";
                }
            }

            File libFile = new File(libDir, libPrefix + libraryName + libSuffix);
            String libFileName = libFile.getCanonicalPath();
            try {
                System.load(libFileName);
            } catch(UnsatisfiedLinkError ex) {
                if (libSuffixAlt != null) {
                    try {
                        libFile = new File(libDir, libPrefix + libraryName + libSuffixAlt);
                        libFileName = libFile.getAbsolutePath();
                        System.load(libFileName);
                    } catch (UnsatisfiedLinkError ex2) {
                        throw ex;
                    }
                } else {
                    throw ex;
                }
            }
        } catch (Exception e) {
            // Throw UnsatisfiedLinkError for best compatibility with System.loadLibrary()
            throw (UnsatisfiedLinkError) new UnsatisfiedLinkError().initCause(e);
        }
    }
}
