// Generated by CoffeeScript 2.7.0
(function() {
  var CombinedStream, DEFLATE_END, DEFLATE_END_LENGTH, DeflateCRC32Stream, DeflatePartStream, GZIP_HEADER, crcUtils;

  crcUtils = require('@balena/node-crc-utils');

  CombinedStream = require('combined-stream');

  ({DeflateCRC32Stream} = require('crc32-stream'));

  // gzip header
  exports.GZIP_HEADER = GZIP_HEADER = Buffer.from([0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]);

  // DEFLATE ending block
  exports.DEFLATE_END = DEFLATE_END = Buffer.from([0x03, 0x00]);

  exports.DEFLATE_END_LENGTH = DEFLATE_END_LENGTH = DEFLATE_END.length;

  // Use the logic briefly described here by the author of zlib library:
  // http://stackoverflow.com/questions/14744692/concatenate-multiple-zlib-compressed-data-streams-into-a-single-stream-efficient#comment51865187_14744792
  // to generate deflate streams that can be concatenated into a gzip stream
  DeflatePartStream = class DeflatePartStream extends DeflateCRC32Stream {
    constructor() {
      super(...arguments);
      this.buf = Buffer.alloc(0);
    }

    push(chunk) {
      if (chunk !== null) {
        if (chunk.length >= DEFLATE_END_LENGTH) {
          // got another large enough chunk, previous chunk is safe to send
          super.push(this.buf);
          return this.buf = chunk;
        } else {
          return this.buf = Buffer.concat([this.buf, chunk]);
        }
      } else {
        // got null signalling end of stream
        // inspect last chunk for DEFLATE_END marker and remove it
        if (this.buf.length >= DEFLATE_END_LENGTH && this.buf.slice(-DEFLATE_END_LENGTH).equals(DEFLATE_END)) {
          this.buf = this.buf.slice(0, -DEFLATE_END_LENGTH);
        }
        super.push(this.buf);
        return super.push(null);
      }
    }

    end() {
      return this.flush(() => {
        return super.end();
      });
    }

    metadata() {
      return {
        crc: this.digest().readUInt32BE(0),
        len: this.size(),
        zLen: this.size(true)
      };
    }

  };

  exports.createDeflatePart = function() {
    return new DeflatePartStream();
  };

  exports.createGzipFromParts = function(parts) {
    var i, len, len1, out, stream;
    out = CombinedStream.create();
    // write the header
    out.append(GZIP_HEADER);
    for (i = 0, len1 = parts.length; i < len1; i++) {
      ({stream} = parts[i]);
      // write all middle parts
      out.append(stream);
    }
    // write ending DEFLATE part
    out.append(DEFLATE_END);
    // write CRC
    out.append(crcUtils.crc32_combine_multi(parts).combinedCrc32);
    // write length
    len = Buffer.alloc(4);
    len.writeUInt32LE(parts.map(function(p) {
      return p.len;
    }).reduce(function(a, b) {
      return a + b;
    }), 0);
    out.append(len);
    // calculate compressed size. Add 10 byte header, 2 byte DEFLATE ending block, 8 byte footer
    out.zLen = parts.map(function(p) {
      return p.zLen;
    }).reduce(function(a, b) {
      return a + b;
    }) + 20;
    // return stream
    return out;
  };

}).call(this);
