# Source: packet/compressed.js

    // GPG4Browsers - An OpenPGP implementation in javascript
    // Copyright (C) 2011 Recurity Labs GmbH
    //
    // This library is free software; you can redistribute it and/or
    // modify it under the terms of the GNU Lesser General Public
    // License as published by the Free Software Foundation; either
    // version 2.1 of the License, or (at your option) any later version.
    //
    // This library is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    // Lesser General Public License for more details.
    //
    // You should have received a copy of the GNU Lesser General Public
    // License along with this library; if not, write to the Free Software
    // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    /** @module packet/compressed */

    var enums = require('../enums.js'),
      JXG = require('../compression/jxg.js'),
      base64 = require('../encoding/base64.js');

    /**
     * @class
     * @classdesc Implementation of the Compressed Data Packet (Tag 8)
     *
     * RFC4880 5.6:
     * The Compressed Data packet contains compressed data.  Typically, this
     * packet is found as the contents of an encrypted packet, or following
     * a Signature or One-Pass Signature packet, and contains a literal data
     * packet.
     */
    module.exports = function packet_compressed() {
      /** @type {module:packet/packetlist} */
      this.packets;
      /** @type {compression} */
      this.algorithm = 'uncompressed';

      this.compressed = null;

      /**
       * Parsing function for the packet.
       * @param {String} input Payload of a tag 8 packet
       * @param {Integer} position Position to start reading from the input string
       * @param {Integer} len length of the packet or the remaining length of
       * input at position
       * @return {module:packet/compressed} Object representation
       */
      this.read = function(bytes) {
        // One octet that gives the algorithm used to compress the packet.
        this.algorithm = enums.read(enums.compression, bytes.charCodeAt(0));

        // Compressed data, which makes up the remainder of the packet.
        this.compressed = bytes.substr(1);

        this.decompress();
      }

      this.write = function() {
        if (this.compressed == null)
          this.compress();

        return String.fromCharCode(enums.write(enums.compression, this.algorithm)) + this.compressed;
      }

      /**
       * Decompression method for decompressing the compressed data
       * read by read_packet
       * @return {String} The decompressed data
       */
      this.decompress = function() {
        var decompressed;

        switch (this.algorithm) {
          case 'uncompressed':
            decompressed = this.compressed;
            break;

          case 'zip':
            var compData = this.compressed;

            var radix = base64.encode(compData).replace(/\n/g, "");
            // no header in this case, directly call deflate
            var jxg_obj = new JXG.Util.Unzip(JXG.Util.Base64.decodeAsArray(radix));

            decompressed = unescape(jxg_obj.deflate()[0][0]);
            break;

          case 'zlib':
            //RFC 1950. Bits 0-3 Compression Method
            var compressionMethod = this.compressed.charCodeAt(0) % 0x10;

            //Bits 4-7 RFC 1950 are LZ77 Window. Generally this value is 7 == 32k window size.
            // 2nd Byte in RFC 1950 is for "FLAGs" Allows for a Dictionary
            // (how is this defined). Basic checksum, and compression level.

            if (compressionMethod == 8) { //CM 8 is for DEFLATE, RFC 1951
              // remove 4 bytes ADLER32 checksum from the end
              var compData = this.compressed.substring(0, this.compressed.length - 4);
              var radix = base64.encode(compData).replace(/\n/g, "");
              //TODO check ADLER32 checksum
              decompressed = JXG.decompress(radix);
              break;

            } else {
              throw new Error("Compression algorithm ZLIB only supports " +
                "DEFLATE compression method.");
            }
            break;

          case 'bzip2':
            // TODO: need to implement this
            throw new Error('Compression algorithm BZip2 [BZ2] is not implemented.');
            break;

          default:
            throw new Error("Compression algorithm unknown :" + this.alogrithm);
            break;
        }

        this.packets.read(decompressed);
      }

      /**
       * Compress the packet data (member decompressedData)
       * @param {Integer} type Algorithm to be used // See RFC 4880 9.3
       * @param {String} data Data to be compressed
       * @return {String} The compressed data stored in attribute compressedData
       */
      this.compress = function() {
        switch (this.algorithm) {

          case 'uncompressed':
            // - Uncompressed
            this.compressed = this.packets.write();
            break;

          case 'zip':
            // - ZIP [RFC1951]
            throw new Error("Compression algorithm ZIP [RFC1951] is not implemented.");
            break;

          case 'zlib':
            // - ZLIB [RFC1950]
            // TODO: need to implement this
            throw new Error("Compression algorithm ZLIB [RFC1950] is not implemented.");
            break;

          case 'bzip2':
            //  - BZip2 [BZ2]
            // TODO: need to implement this
            throw new Error("Compression algorithm BZip2 [BZ2] is not implemented.");
            break;

          default:
            throw new Error("Compression algorithm unknown :" + this.type);
            break;
        }
      }
    };