importPackage(java.io);
importPackage(java.util.zip);

//@include lib_Class.js

/**
 * Utility functions for ZIP files
 *
 * @author MW, ELO Digital Office GmbH
 * @version 1.0
 *
 * @elojc
 * @eloas
 * @eloix
 */
sol.define("sol.common.ZipUtils", {
  singleton: true,

  /**
   * Compresses a local folder into a ZIP file
   * @param {java.io.File} folder Source folder
   * @param {java.io.File} zipFile Compressed file
   */
  zipFolder: function (folder, zipFile) {
    var me = this,
        fileOutputStream, zipOutputStream;
    try {
      fileOutputStream = new FileOutputStream(zipFile);
      zipOutputStream = new ZipOutputStream(fileOutputStream);
      me.compressFolder(folder, zipOutputStream, folder.path.length() + 1);
      zipOutputStream.close();
      fileOutputStream.close();
    } catch (ex) {
      throw ex;
    } finally {
      if (zipOutputStream) {
        zipOutputStream.close();
      }
      if (fileOutputStream) {
        fileOutputStream.close();
      }
    }
  },

  /**
   * @private
   * Recursive function that compresses a sub folder
   * @param {java.io.File} folder Folder
   * @param {java.io.OutputStream} zipOutputStream
   * @param {Number} prefixLength Length of the path part
   */
  compressFolder: function (folder, zipOutputStream, prefixLength) {
    var me = this,
        file, files, i, fileInputStream, zipEntryName;

    files = folder.listFiles();
    for (i = 0; i < files.length; i++) {
      file = files[i];
      if (file.isFile()) {
        zipEntryName = file.path.substring(prefixLength).replace(File.separatorChar, "/");
        zipOutputStream.putNextEntry(new ZipEntry(zipEntryName));
        try {
          fileInputStream = new FileInputStream(file);
          Packages.org.apache.commons.io.IOUtils.copy(fileInputStream, zipOutputStream);
        } catch (ignore) {
          // ignore
        } finally {
          if (fileInputStream) {
            fileInputStream.close();
            zipOutputStream.closeEntry();
          }
        }
      } else if (file.isDirectory()) {
        me.compressFolder(file, zipOutputStream, prefixLength);
      }
    }
  },

  /**
   * Unzips a zip file
   * @param {java.io.File} zipFile ZIP file path
   * @param {java.io.File} dstDir Destination directory
   * @param {Object} params Parameters
   * @param {String} [params.charset=UTF-8] Charset
   */
  unzip: function (zipFile, dstDir, params) {
    var charsetObj, fileInputStream, zipInputStream, zipEntry, fileName, newFile, fileOutputStream;

    if (!zipFile) {
      throw "'zipFile' is missing";
    }

    if (!dstDir) {
      throw "'dstDir' is missing";
    }

    params = params || {};
    params.charset = params.charset || "UTF-8";

    charsetObj = Packages.java.nio.charset.Charset.forName(params.charset);

    if (!zipFile.exists()) {
      throw "Zip file does not exist: zipFile=" + zipFile.absolutePath;
    }

    if (!dstDir.exists()) {
      dstDir.mkdirs();
    }
    fileInputStream = new java.io.FileInputStream(zipFile);
    zipInputStream = new java.util.zip.ZipInputStream(fileInputStream, charsetObj);
    zipEntry = zipInputStream.nextEntry;
    while (zipEntry) {
      fileName = zipEntry.name;
      newFile = new java.io.File(dstDir.canonicalPath + File.separator + fileName);
      new java.io.File(newFile.parent).mkdirs();
      fileOutputStream = new FileOutputStream(newFile);
      Packages.org.apache.commons.io.IOUtils.copy(zipInputStream, fileOutputStream);
      fileOutputStream.close();
      zipInputStream.closeEntry();
      zipEntry = zipInputStream.nextEntry;
    }
    zipInputStream.closeEntry();
    zipInputStream.close();
    fileInputStream.close();
  },

  bom: "\uFEFF",

  /**
   * Returns the text content of a file within a zip file
   * @param {String} zipFilePath Zip file path
   * @param {String} pathInZipFile Path in zip file
   * @param {Object} params Parameters
   * @param {String} [params.encoding=UTF-8] Encoding
   * @return {String} String
   */
  readFileInZipToString: function (zipFilePath, pathInZipFile, params) {
    var me = this,
        byteArray, javaString, text;

    params = params || {};
    params.encoding = params.encoding || "UTF-8";

    byteArray = me.readFileInZipToByteArray(zipFilePath, pathInZipFile);

    javaString = new java.lang.String(byteArray, params.encoding);
    if (javaString.startsWith(me.bom)) {
      javaString = javaString.substring(1);
    }

    text = javaString + "";

    return text;
  },

  /**
   * Returns the content of a file within a zip file as byte array
   * @param {String} zipFilePath Zip file path
   * @param {String} pathInZipFile Path in zip file
   * @return {java.lang.Byte[]} Bytes
   */
  readFileInZipToByteArray: function (zipFilePath, pathInZipFile) {
    var zipPathObj, zipFileSystem, srcFilePathObj, byteArray, stringClass, stringArr;

    if (!zipFilePath) {
      throw "Zip file path is empty";
    }

    if (!pathInZipFile) {
      throw "Path in zip file is empty";
    }

    zipPathObj = java.nio.file.Paths.get(zipFilePath);

    zipFileSystem = java.nio.file.FileSystems.newFileSystem(zipPathObj, java.lang.ClassLoader.systemClassLoader);

    stringClass = java.lang.Class.forName("java.lang.String");
    stringArr = java.lang.reflect.Array.newInstance(stringClass, 0);

    srcFilePathObj = zipFileSystem.getPath(pathInZipFile, stringArr);
    byteArray = java.nio.file.Files.readAllBytes(srcFilePathObj);
    zipFileSystem.close();

    return byteArray;
  },

  /**
   * Delete files in a folder within a ZIP file
   * @param {String} zipFilePath ZIP file path
   * @param {Object} params Parameters
   * @param {String} [params.encoding=UTF-8] Encoding of the ZIP file
   * @param {String} [params.startFolderPathInZipFile] Start folder Path in ZIP file
   * @param {String} [params.fileNamePattern=*.*] File name pattern
   * @param {Array} params.keepFileNames File names to be kept
   */
  deleteFilesInZipFileFolder: function (zipFilePath, params) {
    var zipFile, zipFileUriString, zipFileSystemProps, zipFileUri, zipFileSystem, pathMatcher, deletePath;

    if (!zipFilePath) {
      throw "Zip file path is missing";
    }

    params = params || {};
    params.encoding = params.encoding || "UTF-8";
    params.startFolderPathInZipFile = params.startFolderPathInZipFile || "/";
    params.fileNamePattern = params.fileNamePattern || "*.*";
    params.keepFileNames = params.keepFileNames || [];

    zipFileSystemProps = {
      create: "false",
      encoding: "UTF-8"
    };

    zipFile = new java.io.File(zipFilePath);
    zipFileUriString = zipFile.toURI().toURL().toExternalForm();
    zipFileUri = java.net.URI.create("jar:" + zipFileUriString);
    zipFileSystem = java.nio.file.FileSystems.newFileSystem(zipFileUri, zipFileSystemProps);
    deletePath = zipFileSystem.getPath(params.startFolderPathInZipFile);
    if (java.nio.file.Files.exists(deletePath)) {
      pathMatcher = java.nio.file.FileSystems.default.getPathMatcher("glob:" + params.fileNamePattern);
      java.nio.file.Files.walkFileTree(deletePath, new JavaAdapter(java.nio.file.SimpleFileVisitor, {
        visitFile: function (path, basicFileAttributes) {
          if (pathMatcher.matches(path.fileName) && (params.keepFileNames.indexOf(path.fileName + "") < 0)) {
            java.nio.file.Files.delete(path);
          }
          return java.nio.file.FileVisitResult.CONTINUE;
        }
      }));
    }
    zipFileSystem.close();
  }
});