'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/* eslint-disable no-bitwise */
var RECORDHEADER_LENTH_FULL = 0x3f;
// null-character
var EOS = 0x00;

/**
 *
 * Constructor of SWFBuffer object
 *
 * @param {Buffer} buffer
 * @return Instance of SWFBuffer
 */

var SWFBuffer = function () {
  function SWFBuffer(buffer) {
    _classCallCheck(this, SWFBuffer);

    this.buffer = buffer;
    this.pointer = 0;
    this.position = 1;
    this.current = 0;
  }

  _createClass(SWFBuffer, [{
    key: 'incr',
    value: function incr(n) {
      this.pointer += n;
    }

    /**
     * Reads unsigned 16 or 32 Little Endian Bits
     * and advance pointer to next bits / 8 bytes
     *
     * @param {Number} bits
     * @return {Number} Value read from buffer
     */

  }, {
    key: 'readUIntLE',
    value: function readUIntLE(bits) {
      var value = 0;
      try {
        value = this.buffer['readUInt' + bits + 'LE'](this.pointer);
        this.pointer += bits / 8;
      } catch (e) {
        throw e;
      }
      return value;
    }

    /**
     * Reads unsigned 8 bit from the buffer
     *
     * @return {Number} Value read from buffer
     */

  }, {
    key: 'readUInt8',
    value: function readUInt8() {
      return this.buffer.readUInt8(this.pointer++);
    }

    /**
     * Reads 32-bit unsigned integers value encoded (1-5 bytes)
     *
     * @return {Number} 32-bit unsigned integer
     */

  }, {
    key: 'readEncodedU32',
    value: function readEncodedU32() {
      var result = this.nextByte();
      if (!(result & 0x00000080)) return result;

      result = result & 0x0000007f | this.nextByte() << 7;
      if (!(result & 0x00004000)) return result;

      result = result & 0x00003fff | this.nextByte() << 14;
      if (!(result & 0x00020000)) return result;

      result = result & 0x001fffff | this.nextByte() << 21;
      if (!(result & 0x10000000)) return result;

      result = result & 0x0fffffff | this.nextByte() << 28;
      return result;
    }

    /**
     * Reads an encoded data from buffer and returns a
     * string using the specified character set.
     *
     * @param {String} encoding - defaults to 'utf8'
     * @returns {String} Decoded string
     */

  }, {
    key: 'readString',
    value: function readString(encoding) {
      var init = this.pointer;
      while (this.readUInt8() !== EOS) {}
      return this.buffer.toString(encoding || 'utf8', init, this.pointer - 1);
    }

    /**
     * Reads RGB value
     *
     * @return {Array} Array of RGB value
     */

  }, {
    key: 'readRGB',
    value: function readRGB() {
      return [this.readUInt8(), this.readUInt8(), this.readUInt8()];
    }

    /**
     * Reads RGBA value
     *
     * @return {Array} Array of RGBA value
     */

  }, {
    key: 'readRGBA',
    value: function readRGBA() {
      var rgba = this.readRGB();
      rgba.push(this.readUInt8());
      return rgba;
    }

    /**
     * Reads RECORDHEADER from next tag in the buffer
     *
     * @return {Object} Tag code and length
     */

  }, {
    key: 'readTagCodeAndLength',
    value: function readTagCodeAndLength() {
      var n = this.readUIntLE(16);
      var tagType = n >> 6;
      var tagLength = n & RECORDHEADER_LENTH_FULL;

      if (tagLength === RECORDHEADER_LENTH_FULL) tagLength = this.readUIntLE(32);

      return { code: tagType, length: tagLength };
    }

    /**
     * Reads RECT format
     *
     * @return {Object} x, y, width and height of the RECT
     */

  }, {
    key: 'readRect',
    value: function readRect() {
      this.start();

      var NBits = this.readBits(5);
      var Xmin = this.readBits(NBits, true) / 20;
      var Xmax = this.readBits(NBits, true) / 20;
      var Ymin = this.readBits(NBits, true) / 20;
      var Ymax = this.readBits(NBits, true) / 20;

      return {
        x: Xmin,
        y: Ymin,
        width: Xmax > Xmin ? Xmax - Xmin : Xmin - Xmax,
        height: Ymax > Ymin ? Ymax - Ymin : Ymin - Ymax
      };
    }

    /**
     * Sets internal pointer to the specified position;
     *
     * @param {Number} pos
     */

  }, {
    key: 'seek',
    value: function seek(pos) {
      this.pointer = pos % this.buffer.length;
    }

    /**
     * Resets position and sets current to next Byte in buffer
     */

  }, {
    key: 'start',
    value: function start() {
      this.current = this.nextByte();
      this.position = 1;
    }

    /**
     * Gets next Byte in the buffer and Increment internal pointer
     *
     * @return {Number} Next byte in buffer
     */

  }, {
    key: 'nextByte',
    value: function nextByte() {
      return this.pointer > this.buffer.length ? null : this.buffer[this.pointer++];
    }

    /**
     * Reads b bits from current byte in buffer
     *
     * @param {Number} b
     * @return {Number} Bits read from buffer
     */

  }, {
    key: 'readBits',
    value: function readBits(b, signed) {
      var n = 0;
      var r = 0;
      var sign = signed && ++n && this.current >> 8 - this.position++ & 1 ? -1 : 1;

      while (n++ < b) {
        if (this.position > 8) this.start();

        r = (r << 1) + (this.current >> 8 - this.position++ & 1);
      }
      return sign * r;
    }
  }]);

  return SWFBuffer;
}();

exports.SWFBuffer = SWFBuffer;