"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var kaitai_stream_1 = require("./kaitai-stream");
var KaitaiStreamReader = /** @class */ (function (_super) {
    __extends(KaitaiStreamReader, _super);
    /**
      KaitaiStream is an implementation of Kaitai Struct API for JavaScript.
      Based on DataStream - https://github.com/kig/DataStream.js
      @param {ArrayBuffer} arrayBuffer ArrayBuffer to read from.
      @param {?Number} byteOffset Offset from arrayBuffer beginning for the KaitaiStream.
      */
    function KaitaiStreamReader(arrayBuffer, byteOffset, isBigEndian) {
        if (byteOffset === void 0) { byteOffset = 0; }
        if (isBigEndian === void 0) { isBigEndian = true; }
        return _super.call(this, arrayBuffer, byteOffset, isBigEndian) || this;
    }
    KaitaiStreamReader.fromArray = function (array) {
        // let buffer = new ArrayBuffer(array.length);
        var stream = new KaitaiStreamReader(array.buffer);
        return stream;
    };
    KaitaiStreamReader.create = function (packet) {
        return packet instanceof Uint8Array ? KaitaiStreamReader.fromArray(packet) : packet;
    };
    ;
    // ========================================================================
    // Integer numbers
    // ========================================================================
    // ------------------------------------------------------------------------
    // Signed
    // ------------------------------------------------------------------------
    KaitaiStreamReader.prototype.readS = function (size) {
        return this.read('s', size * 8);
    };
    KaitaiStreamReader.prototype.read = function (type, bitSize, isLittleEndian) {
        if (isLittleEndian === void 0) { isLittleEndian = this.isLittleEndian; }
        if (type !== 'b') {
            bitSize /= 8;
        }
        var methodType = "read" + type.toUpperCase() + (type != 'b' ? bitSize : '') + (bitSize > 1 && type != 'b' ? (isLittleEndian ? 'le' : 'be') : '');
        if (!(methodType in this)) {
            throw new Error("Invalid read type: type=" + type + ", size=" + bitSize + ", le=" + isLittleEndian + ". Method name: " + methodType);
        }
        // console.debug(`Will ${methodType} (le=${isLittleEndian}, pos=${this.pos}, size=${bitSize}, bitsLeft=${this.bitsLeft})`)
        var val = this[methodType](bitSize);
        // console.debug(`\t-Value red ${val}`)
        return val;
    };
    /**
      Reads an 8-bit signed int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readS1 = function () {
        var v = this._dataView.getInt8(this.pos);
        this.pos += 1;
        return v;
    };
    ;
    // ........................................................................
    // Big-endian
    // ........................................................................
    /**
      Reads a 16-bit big-endian signed int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readS2be = function () {
        var v = this._dataView.getInt16(this.pos);
        this.pos += 2;
        return v;
    };
    ;
    /**
      Reads a 32-bit big-endian signed int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readS4be = function () {
        var v = this._dataView.getInt32(this.pos);
        this.pos += 4;
        return v;
    };
    ;
    /**
      Reads a 64-bit big-endian unsigned int from the stream. Note that
      JavaScript does not support 64-bit integers natively, so it will
      automatically upgrade internal representation to use IEEE 754
      double precision float.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readS8be = function () {
        var v1 = this.readU4be();
        var v2 = this.readU4be();
        if ((v1 & 0x80000000) != 0) {
            // negative number
            return -(0x100000000 * (v1 ^ 0xffffffff) + (v2 ^ 0xffffffff)) - 1;
        }
        else {
            return 0x100000000 * v1 + v2;
        }
    };
    ;
    // ........................................................................
    // Little-endian
    // ........................................................................
    /**
      Reads a 16-bit little-endian signed int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readS2le = function () {
        var v = this._dataView.getInt16(this.pos, true);
        this.pos += 2;
        return v;
    };
    ;
    /**
      Reads a 32-bit little-endian signed int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readS4le = function () {
        var v = this._dataView.getInt32(this.pos, true);
        this.pos += 4;
        return v;
    };
    ;
    /**
      Reads a 64-bit little-endian unsigned int from the stream. Note that
      JavaScript does not support 64-bit integers natively, so it will
      automatically upgrade internal representation to use IEEE 754
      double precision float.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readS8le = function () {
        var v1 = this.readU4le();
        var v2 = this.readU4le();
        if ((v2 & 0x80000000) != 0) {
            // negative number
            return -(0x100000000 * (v2 ^ 0xffffffff) + (v1 ^ 0xffffffff)) - 1;
        }
        else {
            return 0x100000000 * v2 + v1;
        }
    };
    ;
    // ------------------------------------------------------------------------
    // Unsigned
    // ------------------------------------------------------------------------
    /**
     * Read unsigned
     * @param size
     */
    KaitaiStreamReader.prototype.readU = function (size) {
        return this.read('u', size * 8);
    };
    /**
      Reads an 8-bit unsigned int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readU1 = function () {
        var v = this._dataView.getUint8(this.pos);
        this.pos += 1;
        return v;
    };
    ;
    // ........................................................................
    // Big-endian
    // ........................................................................
    /**
      Reads a 16-bit big-endian unsigned int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readU2be = function () {
        var v = this._dataView.getUint16(this.pos);
        this.pos += 2;
        return v;
    };
    ;
    /**
      Reads a 32-bit big-endian unsigned int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readU4be = function () {
        var v = this._dataView.getUint32(this.pos);
        this.pos += 4;
        return v;
    };
    ;
    /**
      Reads a 64-bit big-endian unsigned int from the stream. Note that
      JavaScript does not support 64-bit integers natively, so it will
      automatically upgrade internal representation to use IEEE 754
      double precision float.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readU8be = function () {
        var v1 = this.readU4be();
        var v2 = this.readU4be();
        return 0x100000000 * v1 + v2;
    };
    ;
    // ........................................................................
    // Little-endian
    // ........................................................................
    /**
      Reads a 16-bit little-endian unsigned int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readU2le = function () {
        var v = this._dataView.getUint16(this.pos, true);
        this.pos += 2;
        return v;
    };
    ;
    /**
      Reads a 32-bit little-endian unsigned int from the stream.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readU4le = function () {
        var v = this._dataView.getUint32(this.pos, true);
        this.pos += 4;
        return v;
    };
    ;
    /**
      Reads a 64-bit little-endian unsigned int from the stream. Note that
      JavaScript does not support 64-bit integers natively, so it will
      automatically upgrade internal representation to use IEEE 754
      double precision float.
      @return {number} The read number.
     */
    KaitaiStreamReader.prototype.readU8le = function () {
        var v1 = this.readU4le();
        var v2 = this.readU4le();
        return 0x100000000 * v2 + v1;
    };
    ;
    // ========================================================================
    // Floating point numbers
    // ========================================================================
    // ------------------------------------------------------------------------
    // Big endian
    // ------------------------------------------------------------------------
    KaitaiStreamReader.prototype.readF = function (size) {
        return this.read('f', size * 8);
    };
    KaitaiStreamReader.prototype.readF4be = function () {
        var v = this._dataView.getFloat32(this.pos);
        this.pos += 4;
        return v;
    };
    ;
    KaitaiStreamReader.prototype.readF8be = function () {
        var v = this._dataView.getFloat64(this.pos);
        this.pos += 8;
        return v;
    };
    ;
    // ------------------------------------------------------------------------
    // Little endian
    // ------------------------------------------------------------------------
    KaitaiStreamReader.prototype.readF4le = function () {
        var v = this._dataView.getFloat32(this.pos, true);
        this.pos += 4;
        return v;
    };
    ;
    KaitaiStreamReader.prototype.readF8le = function () {
        var v = this._dataView.getFloat64(this.pos, true);
        this.pos += 8;
        return v;
    };
    ;
    // ------------------------------------------------------------------------
    // Unaligned bit values
    // ------------------------------------------------------------------------
    KaitaiStreamReader.prototype.readBitsBoolean = function (n) {
        return this.readBitsInt(n) !== 0;
    };
    KaitaiStreamReader.prototype.readB = function (n) {
        return this.readBitsInt(n);
    };
    KaitaiStreamReader.prototype.forwardBits = function (n) {
        this.readBitsInt(n);
        return this;
    };
    KaitaiStreamReader.prototype.readBitsInt = function (n) {
        // console.debug('readBitsInt(' + n + '): bits: ', this.bits, '  | bitsLeft: ', this.bitsLeft, '    | pos:' + this.pos, '    | buffer: ' + FormatHelper.toHexString(this.toBytes))
        // JS only supports bit operations on 32 bits
        if (n > 32)
            throw new Error("readBitsInt: the maximum supported bit length is 32 (tried to read " + n + " bits)");
        var bitsNeeded = n - this.bitsLeft;
        if (bitsNeeded > 0) {
            // console.debug(`readBitsInt bitsNeeded ${bitsNeeded} but only ${this.bitsLeft} available`)
            // 1 bit  => 1 byte
            // 8 bits => 1 byte
            // 9 bits => 2 bytes
            var bytesNeeded = Math.ceil(bitsNeeded / 8);
            var buf = this.readBytes(bytesNeeded);
            for (var i = 0; i < buf.length; i++) {
                this.bits <<= 8;
                this.bits |= buf[i];
                this.bitsLeft += 8;
            }
        }
        // raw mask with required number of 1s, starting from lowest bit
        var mask = n == 32 ? 0xffffffff : (1 << n) - 1;
        // shift mask to align with highest bits available in this.bits
        var shiftBits = this.bitsLeft - n;
        mask <<= shiftBits;
        // derive reading result
        var res = (this.bits & mask) >>> shiftBits;
        // clear top bits that we've just read => AND with 1s
        this.bitsLeft -= n;
        mask = (1 << this.bitsLeft) - 1;
        this.bits &= mask;
        return res;
    };
    // ========================================================================
    // Byte arrays
    // ========================================================================
    KaitaiStreamReader.prototype.readBytes = function (len) {
        if (len < 0) {
            len = this.byteLeft + len;
        }
        // console.debug('readBytes', len);
        return this.mapUint8Array(len);
    };
    KaitaiStreamReader.prototype.readBytesFull = function () {
        return this.mapUint8Array(this.size - this.pos);
    };
    KaitaiStreamReader.prototype.readBytesTerm = function (terminator, include, consume, eosError) {
        var blen = this.size - this.pos;
        var u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos);
        for (var i = 0; i < blen && u8[i] != terminator; i++)
            ; // find first zero byte
        if (i == blen) {
            // we've read all the buffer and haven't found the terminator
            if (eosError) {
                throw "End of stream reached, but no terminator " + terminator + " found";
            }
            else {
                return this.mapUint8Array(i);
            }
        }
        else {
            var arr;
            if (include) {
                arr = this.mapUint8Array(i + 1);
            }
            else {
                arr = this.mapUint8Array(i);
            }
            if (consume) {
                this.pos += 1;
            }
            return arr;
        }
    };
    KaitaiStreamReader.prototype.ensureFixedContents = function (expected) {
        var actual = this.readBytes(expected.length);
        if (actual.length !== expected.length) {
            throw new kaitai_stream_1.UnexpectedDataError(expected, actual);
        }
        var actLen = actual.length;
        for (var i = 0; i < actLen; i++) {
            if (actual[i] != expected[i]) {
                throw new kaitai_stream_1.UnexpectedDataError(expected, actual);
            }
        }
        return actual;
    };
    return KaitaiStreamReader;
}(kaitai_stream_1.KaitaiStream));
exports.KaitaiStreamReader = KaitaiStreamReader;
