var compress_buffer = require("compress-buffer");
var compress_buffer_crc32 = require("compress-buffer-crc32");

var Compressor = function (configuration) {
    this._configuration = configuration;
};
    
Compressor.prototype._getCompressionRate = function(unCompressedLength, compressedLength){
    return {
        times: Math.round(unCompressedLength/compressedLength),
        percentage: Math.round((100-(100*(compressedLength/unCompressedLength)))),
        compressedLength: compressedLength,
        unCompressedLength: unCompressedLength
    };        
};

Compressor.prototype.compress = function (data, callback, compressLevel) {
    compressLevel = compressLevel || 6;
    var compressedData = compress_buffer.compress(data, compressLevel);
    callback(
        compressedData,
        this._getCompressionRate(data.length, compressedData.length)
    );
};

Compressor.prototype.uncompress = function (data, callback) {
    var compressedData = compress_buffer.uncompress(data);
    callback(
        compressedData,
        this._getCompressionRate(compressedData.length, data.length)
    );
};

Compressor.prototype.compressBlock = function (data, callback, compressLevel) {
    compressLevel = compressLevel || 6;
    var compressedData = compress_buffer_crc32.compress(data, compressLevel);
    callback(
        compressedData,
        this._getCompressionRate(data.length, compressedData.length)
    );
};

Compressor.prototype.uncompressBlock = function (data, callback) {
    var compressedData = compress_buffer_crc32.uncompress(data);
    callback(
        compressedData,
        this._getCompressionRate(compressedData.length, data.length)
    );
};

Compressor.prototype.compressBlockSync = function (data, compressLevel) {
    return compress_buffer_crc32.metaCompress(data, compressLevel);
};

Compressor.prototype.uncompressBlockSync = function (data) {
    return compress_buffer_crc32.uncompress(data);
};

Compressor.prototype.combineBlocksSync = function (blocks) {
    var crc = compress_buffer_crc32.getCrc(blocks);
    var estimatedLength = compress_buffer_crc32.estimate(blocks);
    var position = 0;

    var response = new Buffer(estimatedLength);
    compress_buffer_crc32.buffers.header.copy(response, position);
    position += compress_buffer_crc32.buffers.header.length;

    for (var i = 0, l = blocks.length; i < l; i++) {
        var stream = blocks[i];
        var pos = stream.meta.type & 7;
        var last = stream.body[stream.meta.offsets.last];

        var lastStream = (i + 1) == l;

        tmpBuf = stream.body.slice(stream.meta.offsets.left, stream.meta.offsets.right);

        if (tmpBuf[0] & 1 && !lastStream) {
            tmpBuf[0] &= ~1;
        } else if (tmpBuf[0] == 0 && !lastStream) {
            var x = 0;
            /* gasimy pierwszy bit w pierwszym bajcie w ostatnim bloku w danym strimie */
            /* przesuwamy sie po buforze ze skokiem zdefiniowanym w X */
            for (var j = 0, k = tmpBuf.length; (j < k && x < k); j++) {
                x += Compressor.META.BLOCK_HEADER_SIZE + (tmpBuf[x+1]) + (tmpBuf[x+2] << 8);
                if (tmpBuf[x] & 1) {
                    tmpBuf[x] &= ~1;
                    break;
                }
            }
        }

        tmpBuf.copy(response, position);
        position += tmpBuf.length;

        if (pos == 0 || lastStream) {
            response[position] = last;
            position++;
        } else {
            last &= ((0x100 >> pos) - 1);

            if (pos & 1) {
                response[position] = last;
                position++;

                if (pos == 1) {
                    response[position] = 0x00;
                    position++;
                }

                compress_buffer_crc32.buffers.odd.copy(response, position);
                position += compress_buffer_crc32.buffers.odd.length;
            } else {
                switch (pos) {
                    case 6:
                        response[position] = last | 0x08;
                        position++;
                        last = 0x00;
                    case 4:
                        response[position] = last | 0x20;
                        position++;
                        last = 0x00;
                    case 2:
                        response[position] = last | 0x80;
                        position++;
                        response[position] = 0x00;
                        position++;
                }
            }
        }
    }
    crc.crc.copy(response, position);
    position += crc.crc.length;

    crc.length.copy(response, position);
    position += crc.length.length;

    console.info("Compressor: estimated: %d, total: %d", estimatedLength, position, crc);
    return response.slice(0, position);
};

Compressor.META = {};
Compressor.META.HEADER = new Buffer([0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03], "binary");
Compressor.META.FOOTER = new Buffer([0x03, 0x00], "binary");
Compressor.META.BLOCK_HEADER_SIZE = 5;

exports.Compressor = Compressor;    
