import { dew as _readerForDewDew } from "./reader/readerFor.dew.js";
import { dew as _utilsDewDew } from "./utils.dew.js";
import { dew as _compressedObjectDewDew } from "./compressedObject.dew.js";
import { dew as _crc32DewDew } from "./crc32.dew.js";
import { dew as _utf8DewDew } from "./utf8.dew.js";
import { dew as _compressionsDewDew } from "./compressions.dew.js";
import { dew as _supportDewDew } from "./support.dew.js";
var exports = {},
    _dewExec = false;
export function dew() {
  if (_dewExec) return exports;
  _dewExec = true;

  var readerFor = _readerForDewDew();

  var utils = _utilsDewDew();

  var CompressedObject = _compressedObjectDewDew();

  var crc32fn = _crc32DewDew();

  var utf8 = _utf8DewDew();

  var compressions = _compressionsDewDew();

  var support = _supportDewDew();

  var MADE_BY_DOS = 0x00;
  var MADE_BY_UNIX = 0x03;
  /**
   * Find a compression registered in JSZip.
   * @param {string} compressionMethod the method magic to find.
   * @return {Object|null} the JSZip compression object, null if none found.
   */

  var findCompression = function (compressionMethod) {
    for (var method in compressions) {
      if (!compressions.hasOwnProperty(method)) {
        continue;
      }

      if (compressions[method].magic === compressionMethod) {
        return compressions[method];
      }
    }

    return null;
  }; // class ZipEntry {{{

  /**
   * An entry in the zip file.
   * @constructor
   * @param {Object} options Options of the current file.
   * @param {Object} loadOptions Options for loading the stream.
   */


  function ZipEntry(options, loadOptions) {
    this.options = options;
    this.loadOptions = loadOptions;
  }

  ZipEntry.prototype = {
    /**
     * say if the file is encrypted.
     * @return {boolean} true if the file is encrypted, false otherwise.
     */
    isEncrypted: function () {
      // bit 1 is set
      return (this.bitFlag & 0x0001) === 0x0001;
    },

    /**
     * say if the file has utf-8 filename/comment.
     * @return {boolean} true if the filename/comment is in utf-8, false otherwise.
     */
    useUTF8: function () {
      // bit 11 is set
      return (this.bitFlag & 0x0800) === 0x0800;
    },

    /**
     * Read the local part of a zip file and add the info in this object.
     * @param {DataReader} reader the reader to use.
     */
    readLocalPart: function (reader) {
      var compression, localExtraFieldsLength; // we already know everything from the central dir !
      // If the central dir data are false, we are doomed.
      // On the bright side, the local part is scary  : zip64, data descriptors, both, etc.
      // The less data we get here, the more reliable this should be.
      // Let's skip the whole header and dash to the data !

      reader.skip(22); // in some zip created on windows, the filename stored in the central dir contains \ instead of /.
      // Strangely, the filename here is OK.
      // I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes
      // or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators...
      // Search "unzip mismatching "local" filename continuing with "central" filename version" on
      // the internet.
      //
      // I think I see the logic here : the central directory is used to display
      // content and the local directory is used to extract the files. Mixing / and \
      // may be used to display \ to windows users and use / when extracting the files.
      // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394

      this.fileNameLength = reader.readInt(2);
      localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir
      // the fileName is stored as binary data, the handleUTF8 method will take care of the encoding.

      this.fileName = reader.readData(this.fileNameLength);
      reader.skip(localExtraFieldsLength);

      if (this.compressedSize === -1 || this.uncompressedSize === -1) {
        throw new Error("Bug or corrupted zip : didn't get enough information from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)");
      }

      compression = findCompression(this.compressionMethod);

      if (compression === null) {
        // no compression found
        throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")");
      }

      this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize));
    },

    /**
     * Read the central part of a zip file and add the info in this object.
     * @param {DataReader} reader the reader to use.
     */
    readCentralPart: function (reader) {
      this.versionMadeBy = reader.readInt(2);
      reader.skip(2); // this.versionNeeded = reader.readInt(2);

      this.bitFlag = reader.readInt(2);
      this.compressionMethod = reader.readString(2);
      this.date = reader.readDate();
      this.crc32 = reader.readInt(4);
      this.compressedSize = reader.readInt(4);
      this.uncompressedSize = reader.readInt(4);
      var fileNameLength = reader.readInt(2);
      this.extraFieldsLength = reader.readInt(2);
      this.fileCommentLength = reader.readInt(2);
      this.diskNumberStart = reader.readInt(2);
      this.internalFileAttributes = reader.readInt(2);
      this.externalFileAttributes = reader.readInt(4);
      this.localHeaderOffset = reader.readInt(4);

      if (this.isEncrypted()) {
        throw new Error("Encrypted zip are not supported");
      } // will be read in the local part, see the comments there


      reader.skip(fileNameLength);
      this.readExtraFields(reader);
      this.parseZIP64ExtraField(reader);
      this.fileComment = reader.readData(this.fileCommentLength);
    },

    /**
     * Parse the external file attributes and get the unix/dos permissions.
     */
    processAttributes: function () {
      this.unixPermissions = null;
      this.dosPermissions = null;
      var madeBy = this.versionMadeBy >> 8; // Check if we have the DOS directory flag set.
      // We look for it in the DOS and UNIX permissions
      // but some unknown platform could set it as a compatibility flag.

      this.dir = this.externalFileAttributes & 0x0010 ? true : false;

      if (madeBy === MADE_BY_DOS) {
        // first 6 bits (0 to 5)
        this.dosPermissions = this.externalFileAttributes & 0x3F;
      }

      if (madeBy === MADE_BY_UNIX) {
        this.unixPermissions = this.externalFileAttributes >> 16 & 0xFFFF; // the octal permissions are in (this.unixPermissions & 0x01FF).toString(8);
      } // fail safe : if the name ends with a / it probably means a folder


      if (!this.dir && this.fileNameStr.slice(-1) === '/') {
        this.dir = true;
      }
    },

    /**
     * Parse the ZIP64 extra field and merge the info in the current ZipEntry.
     * @param {DataReader} reader the reader to use.
     */
    parseZIP64ExtraField: function (reader) {
      if (!this.extraFields[0x0001]) {
        return;
      } // should be something, preparing the extra reader


      var extraReader = readerFor(this.extraFields[0x0001].value); // I really hope that these 64bits integer can fit in 32 bits integer, because js
      // won't let us have more.

      if (this.uncompressedSize === utils.MAX_VALUE_32BITS) {
        this.uncompressedSize = extraReader.readInt(8);
      }

      if (this.compressedSize === utils.MAX_VALUE_32BITS) {
        this.compressedSize = extraReader.readInt(8);
      }

      if (this.localHeaderOffset === utils.MAX_VALUE_32BITS) {
        this.localHeaderOffset = extraReader.readInt(8);
      }

      if (this.diskNumberStart === utils.MAX_VALUE_32BITS) {
        this.diskNumberStart = extraReader.readInt(4);
      }
    },

    /**
     * Read the central part of a zip file and add the info in this object.
     * @param {DataReader} reader the reader to use.
     */
    readExtraFields: function (reader) {
      var end = reader.index + this.extraFieldsLength,
          extraFieldId,
          extraFieldLength,
          extraFieldValue;

      if (!this.extraFields) {
        this.extraFields = {};
      }

      while (reader.index + 4 < end) {
        extraFieldId = reader.readInt(2);
        extraFieldLength = reader.readInt(2);
        extraFieldValue = reader.readData(extraFieldLength);
        this.extraFields[extraFieldId] = {
          id: extraFieldId,
          length: extraFieldLength,
          value: extraFieldValue
        };
      }

      reader.setIndex(end);
    },

    /**
     * Apply an UTF8 transformation if needed.
     */
    handleUTF8: function () {
      var decodeParamType = support.uint8array ? "uint8array" : "array";

      if (this.useUTF8()) {
        this.fileNameStr = utf8.utf8decode(this.fileName);
        this.fileCommentStr = utf8.utf8decode(this.fileComment);
      } else {
        var upath = this.findExtraFieldUnicodePath();

        if (upath !== null) {
          this.fileNameStr = upath;
        } else {
          // ASCII text or unsupported code page
          var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName);
          this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray);
        }

        var ucomment = this.findExtraFieldUnicodeComment();

        if (ucomment !== null) {
          this.fileCommentStr = ucomment;
        } else {
          // ASCII text or unsupported code page
          var commentByteArray = utils.transformTo(decodeParamType, this.fileComment);
          this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray);
        }
      }
    },

    /**
     * Find the unicode path declared in the extra field, if any.
     * @return {String} the unicode path, null otherwise.
     */
    findExtraFieldUnicodePath: function () {
      var upathField = this.extraFields[0x7075];

      if (upathField) {
        var extraReader = readerFor(upathField.value); // wrong version

        if (extraReader.readInt(1) !== 1) {
          return null;
        } // the crc of the filename changed, this field is out of date.


        if (crc32fn(this.fileName) !== extraReader.readInt(4)) {
          return null;
        }

        return utf8.utf8decode(extraReader.readData(upathField.length - 5));
      }

      return null;
    },

    /**
     * Find the unicode comment declared in the extra field, if any.
     * @return {String} the unicode comment, null otherwise.
     */
    findExtraFieldUnicodeComment: function () {
      var ucommentField = this.extraFields[0x6375];

      if (ucommentField) {
        var extraReader = readerFor(ucommentField.value); // wrong version

        if (extraReader.readInt(1) !== 1) {
          return null;
        } // the crc of the comment changed, this field is out of date.


        if (crc32fn(this.fileComment) !== extraReader.readInt(4)) {
          return null;
        }

        return utf8.utf8decode(extraReader.readData(ucommentField.length - 5));
      }

      return null;
    }
  };
  exports = ZipEntry;
  return exports;
}