package io.bit3.jsass.adapter;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.file.Files;

/**
 * This loader handle the extraction and loading of the shared library files from the jar.
 */
final class NativeLoader {
  private static final Logger LOG = LoggerFactory.getLogger(NativeLoader.class);

  private NativeLoader() {
  }

  /**
   * Load the shared libraries.
   */
  public static void loadLibrary() {
    try {
      File dir = Files.createTempDirectory("libjsass-").toFile();
      dir.deleteOnExit();

      if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
        System.load(saveLibrary(dir, "sass"));
      }

      System.load(saveLibrary(dir, "jsass"));
    } catch (Exception exception) {
      LOG.warn(exception.getMessage(), exception);
      throw new LoaderException(exception);
    }
  }

  /**
   * Find the right shared library, depending on the operating system and architecture.
   *
   * @throws UnsupportedOperationException Throw an exception if no native library for this platform
   *                                       was found.
   */
  static URL findLibraryResource(String library) {
    String osName = System.getProperty("os.name").toLowerCase();
    String osArch = System.getProperty("os.arch").toLowerCase();
    String resourceName = null;

    if (osName.startsWith("win")) {
      resourceName = determineWindowsLibrary(library, osName, osArch);
    } else if (osName.startsWith("linux")) {
      resourceName = determineLinuxLibrary(library, osName, osArch);
    } else if (osName.startsWith("mac")) {
      resourceName = determineMacLibrary(library);
    } else {
      unsupportedPlatform(osName, osArch);
    }

    URL resource = NativeLoader.class.getResource(resourceName);

    if (null == resource) {
      unsupportedPlatform(osName, osArch);
    }

    return resource;
  }

  /**
   * Determine the right windows library depending on the architecture.
   *
   * @param library The library name.
   * @param osName  The operating system name.
   * @param osArch  The system architecture.
   * @return The library resource.
   * @throws UnsupportedOperationException Throw an exception if no native library for this platform
   *                                       was found.
   */
  private static String determineWindowsLibrary(String library, String osName, String osArch) {
    String resourceName;
    String platform;
    String fileExtension = "dll";

    switch (osArch) {
      case "i386":
      case "x86":
        platform = "windows-x32";
        break;

      case "amd64":
      case "x86_64":
        platform = "windows-x64";
        break;

      default:
        throw new UnsupportedOperationException(
            "Platform " + osName + ":" + osArch + " not supported"
        );
    }

    resourceName = "/" + platform + "/" + library + "." + fileExtension;
    return resourceName;
  }

  /**
   * Determine the right linux library depending on the architecture.
   *
   * @param library The library name.
   * @param osName  The operating system name.
   * @param osArch  The system architecture.
   * @return The library resource.
   * @throws UnsupportedOperationException Throw an exception if no native library for this platform
   *                                       was found.
   */
  private static String determineLinuxLibrary(String library, String osName, String osArch) {
    String resourceName;
    String platform = null;
    String fileExtension = "so";

    switch (osArch) {
      case "amd64":
      case "x86_64":
        platform = "linux-x64";
        break;

      default:
        unsupportedPlatform(osName, osArch);
    }

    resourceName = "/" + platform + "/" + library + "." + fileExtension;
    return resourceName;
  }

  /**
   * Determine the right mac library depending on the architecture.
   *
   * @param library The library name.
   * @return The library resource.
   */
  private static String determineMacLibrary(String library) {
    String resourceName;
    String platform = "darwin";
    String fileExtension = "dylib";
    resourceName = "/" + platform + "/" + library + "." + fileExtension;
    return resourceName;
  }

  /**
   * Save the shared library in the given temporary directory.
   */
  static String saveLibrary(File dir, String library) throws IOException {
    library = "lib" + library;

    URL libraryResource = findLibraryResource(library);

    String basename = FilenameUtils.getName(libraryResource.getPath());
    File file = new File(dir, basename);
    file.deleteOnExit();

    try (
        InputStream in = libraryResource.openStream();
        OutputStream out = new FileOutputStream(file)
    ) {
      IOUtils.copy(in, out);
    }

    return file.getAbsolutePath();
  }

  private static void unsupportedPlatform(String osName, String osArch) {
    throw new UnsupportedOperationException(
        "Platform " + osName + ":" + osArch + " not supported"
    );
  }
}

@@ -1390,16 +1390,22 @@
 esource(
+final 
 String l
@@ -1402,32 +1402,40 @@
 l String library
+FileName
 ) %7B%0A    String o
@@ -1665,32 +1665,40 @@
 sLibrary(library
+FileName
 , osName, osArch
@@ -1795,16 +1795,24 @@
 (library
+FileName
 , osName
@@ -1913,16 +1913,24 @@
 (library
+FileName
 );%0A    %7D
@@ -2606,32 +2606,45 @@
 eWindowsLibrary(
+%0A      final 
 String library, 
@@ -2634,32 +2634,44 @@
  String library,
+%0A      final
  String osName, 
@@ -2661,32 +2661,44 @@
 l String osName,
+%0A      final
  String osArch) 
@@ -2687,32 +2687,35 @@
 al String osArch
+%0A  
 ) %7B%0A    String r
@@ -3680,32 +3680,45 @@
 ineLinuxLibrary(
+%0A      final 
 String library, 
@@ -3716,16 +3716,28 @@
 library,
+%0A      final
  String 
@@ -3735,32 +3735,44 @@
 l String osName,
+%0A      final
  String osArch) 
@@ -3761,32 +3761,35 @@
 al String osArch
+%0A  
 ) %7B%0A    String r
@@ -4346,16 +4346,22 @@
 Library(
+final 
 String l
@@ -4669,16 +4669,22 @@
 Library(
+final 
 File dir
@@ -4684,16 +4684,22 @@
 ile dir,
+ final
  String 
@@ -4705,16 +4705,20 @@
  library
+Name
 ) throws
@@ -4736,23 +4736,38 @@
 n %7B%0A    
-library
+String libraryFileName
  = %22lib%22
@@ -4776,16 +4776,20 @@
  library
+Name
 ;%0A%0A    U
@@ -4836,16 +4836,24 @@
 (library
+FileName
 );%0A%0A    
@@ -5232,16 +5232,22 @@
 latform(
+final 
 String o
@@ -5252,16 +5252,22 @@
  osName,
+ final
  String 

Make all method parameters in NativeLoader final.