/**
 * Implementation of a file stored inside a zip archive
 *
 * @private
 */
Ext.define('Ext.exporter.file.zip.File', {
    extend: 'Ext.Base',

    requires: [
        'Ext.overrides.exporter.util.Format'
    ],

    config: {
        path: '',
        data: null,
        dateTime: null,
        folder: false
    },

    constructor: function(config) {
        var me = this;

        me.initConfig(config);

        if (!me.getDateTime()) {
            me.setDateTime(new Date());
        }

        return me.callParent([config]);
    },

    getId: function() {
        return this.getPath();
    },

    crc32: function(input, crc) {
        var table = this.self.crcTable,
            x = 0,
            y = 0,
            b = 0,
            isArray, i, iTop;

        // this method uses code from https://github.com/Stuk/jszip

        if (typeof input === "undefined" || !input.length) {
            return 0;
        }

        isArray = (typeof input !== "string");

        if (typeof(crc) === "undefined") {
            crc = 0;
        }

        crc = crc ^ (-1);

        for (i = 0, iTop = input.length; i < iTop; i++) {
            b = isArray ? input[i] : input.charCodeAt(i);
            y = (crc ^ b) & 0xFF;
            x = table[y];
            crc = (crc >>> 8) ^ x;
        }

        return crc ^ (-1);
    },

    getHeader: function(offset) {
        var data = this.getData(),
            path = this.getPath(),
            utfName = Ext.util.Base64._utf8_encode(path),
            useUTF8 = utfName !== path,
            dateTime = this.getDateTime(),
            extraFields = '',
            unicodePathExtraField = '',
            decToHex = Ext.util.Format.decToHex,
            header = '',
            dosTime, dosDate, fileHeader, dirHeader;

        // this method uses code from https://github.com/Stuk/jszip

        dosTime = dateTime.getHours();
        dosTime = dosTime << 6;
        dosTime = dosTime | dateTime.getMinutes();
        dosTime = dosTime << 5;
        dosTime = dosTime | dateTime.getSeconds() / 2;

        dosDate = dateTime.getFullYear() - 1980;
        dosDate = dosDate << 4;
        dosDate = dosDate | (dateTime.getMonth() + 1);
        dosDate = dosDate << 5;
        dosDate = dosDate | dateTime.getDate();

        if (useUTF8) {
            unicodePathExtraField =
                // Version
                decToHex(1, 1) +
                // NameCRC32
                decToHex(this.crc32(utfName), 4) +
                // UnicodeName
                utfName;

            extraFields +=
                // Info-ZIP Unicode Path Extra Field
                "\x75\x70" +
                // size
                decToHex(unicodePathExtraField.length, 2) +
                // content
                unicodePathExtraField;
        }

        // version needed to extract
        header += "\x0A\x00";
        // general purpose bit flag
        // set bit 11 if utf8
        header += useUTF8 ? "\x00\x08" : "\x00\x00";
        // compression method
        header += "\x00\x00";
        // last mod file time
        header += decToHex(dosTime, 2);
        // last mod file date
        header += decToHex(dosDate, 2);
        // crc-32
        header += decToHex(data ? this.crc32(data) : 0, 4);
        // compressed size
        header += decToHex(data ? data.length : 0, 4);
        // uncompressed size
        header += decToHex(data ? data.length : 0, 4);
        // file name length
        header += decToHex(utfName.length, 2);
        // extra field length
        header += decToHex(extraFields.length, 2);

        fileHeader = "PK\x03\x04" + header + utfName + extraFields;

        dirHeader =
            // central file header
            "PK\x01\x02" +
            // version made by (00: DOS)
            "\x14\x00" +
            // file header (common to file and central directory)
            header +
            // file comment length
            "\x00\x00" +
            // disk number start
            "\x00\x00" +
            // internal file attributes TODO
            "\x00\x00" +
            // external file attributes
            (this.getFolder() === true ? "\x10\x00\x00\x00" : "\x00\x00\x00\x00") +
            // relative offset of local header
            decToHex(offset, 4) +
            // file name
            utfName +
            // extra field
            extraFields;

        return {
            fileHeader: fileHeader,
            dirHeader: dirHeader,
            data: data || ''
        };

    }
}, function(File) {
    var table = [],
        c, n, k;

    for (n = 0; n < 256; n++) {
        c = n;

        for (k = 0; k < 8; k++) {
            c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
        }

        table[n] = c;
    }

    File.crcTable = table;
});
