"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 logger_1 = require("../../logger");
var logger = logger_1.default('KaitaiStreamWriter');
var KaitaiStreamWriter = /** @class */ (function (_super) {
    __extends(KaitaiStreamWriter, _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 KaitaiStreamWriter(arrayBuffer, byteOffset, isBigEndian) {
        if (byteOffset === void 0) { byteOffset = 0; }
        if (isBigEndian === void 0) { isBigEndian = true; }
        var _this = _super.call(this, arrayBuffer, byteOffset, isBigEndian) || this;
        _this.bitsLeft = 8;
        _this.bits = 0;
        return _this;
    }
    ;
    KaitaiStreamWriter.create = function (size) {
        return new KaitaiStreamWriter(new ArrayBuffer(size));
    };
    Object.defineProperty(KaitaiStreamWriter.prototype, "toBytes", {
        get: function () {
            if (this.bitsLeft < 8 && this.bitsLeft > 0) {
                this._flushBits();
            }
            var a = new Uint8Array(this.pos);
            for (var i = 0; i < this.pos; i++)
                a[i] = this._dataView.getUint8(i);
            return a;
        },
        enumerable: true,
        configurable: true
    });
    KaitaiStreamWriter.prototype.write = function (value, type, size) {
        this._checkSize(size);
        var methodName = "write" + type.toUpperCase();
        if (type !== 'b') {
            size /= 8;
        }
        else {
            value = value === true ? 1 : (value === false ? 0 : value);
        }
        if (!(methodName in this)) {
            throw new Error("Invalid write type: type=" + type + ". Method name: " + methodName);
        }
        // console.debug(`write ${methodName} on ${size} bytes/bits `, value);
        return this[methodName](value, size);
    };
    KaitaiStreamWriter.prototype.writeU = function (value, byteSize) {
        this._checkSize(byteSize);
        switch (byteSize) {
            case 4:
                this._dataView.setUint32(this.pos, value, this.isLittleEndian);
                break;
            case 2:
                this._dataView.setUint16(this.pos, value, this.isLittleEndian);
                break;
            case 1:
                this._dataView.setUint8(this.pos, value);
                break;
            default:
                throw new Error("Unsupported unsigned size: " + byteSize);
        }
        this.pos += byteSize;
        return this;
    };
    KaitaiStreamWriter.prototype.writeF = function (value, byteSize) {
        this._checkSize(byteSize);
        switch (byteSize) {
            case 4:
                this._dataView.setFloat32(this.pos, value, this.isLittleEndian);
                break;
            case 8:
                this._dataView.setFloat64(this.pos, value, this.isLittleEndian);
                break;
            default:
                throw new Error("Illegal float size: " + byteSize);
        }
        this.pos += byteSize;
        return this;
    };
    KaitaiStreamWriter.prototype.writeS = function (value, byteSize) {
        this._checkSize(byteSize);
        switch (byteSize) {
            case 4:
                this._dataView.setInt32(this.pos, value, this.isLittleEndian);
                break;
            case 2:
                this._dataView.setInt16(this.pos, value, this.isLittleEndian);
                break;
            case 1:
                this._dataView.setInt8(this.pos, value);
                break;
            default:
                throw new Error("Illegal int size: " + byteSize);
        }
        this.pos += byteSize;
        return this;
    };
    Object.defineProperty(KaitaiStreamWriter.prototype, "pos", {
        get: function () {
            return this._pos;
        },
        set: function (v) {
            this._pos = v;
            this.bitsLeft = 8;
        },
        enumerable: true,
        configurable: true
    });
    KaitaiStreamWriter.prototype._flushBitsLeftIfNeeded = function () {
        if (this.bitsLeft <= 0) {
            this._flushBits();
        }
    };
    KaitaiStreamWriter.prototype._flushBits = function () {
        // console.debug('_flushBits() value: ', this.bits)
        this.writeU(this.bits, 1);
        this.bitsLeft = 8;
        this.bits = 0;
    };
    KaitaiStreamWriter.prototype._setBit = function (value) {
        this._flushBitsLeftIfNeeded();
        var bitMask = 1 << (this.bitsLeft - 1);
        if (value) {
            this.bits |= bitMask;
        }
        else {
            this.bits &= (~bitMask);
        }
        this.bitsLeft -= 1;
        // console.debug(`_setBit(${value}) bitsLeft:`, this.bitsLeft, '    bits: ', this.bits)
        this._flushBitsLeftIfNeeded();
        return this;
    };
    KaitaiStreamWriter.prototype.writeB = function (v, n) {
        if (n === void 0) { n = 1; }
        v = v === true ? 1 : (v === false ? 0 : v);
        for (var i = n - 1; i >= 0; i--) {
            this._setBit((v >> i) & 0x1 ? true : false);
        }
        return this;
    };
    KaitaiStreamWriter.prototype.forwardBits = function (n) {
        this.writeB(0, n);
        return this;
    };
    /**
     *
     * @param v true or false
     * @param n number of bits
     */
    KaitaiStreamWriter.prototype.writeBitsBoolean = function (v, n) {
        return this.writeB(v, n);
    };
    KaitaiStreamWriter.prototype.writeBytes = function (data, size) {
        if (size === void 0) { size = data.length; }
        this._checkSize(size);
        // console.debug(`writeBytes ${size} bytes (remaining size: ${this.byteLeft})`)
        for (var i = 0; i < size; i++) {
            // console.debug(`writeBytes ${i} = ${data[i]} (remaining size: ${this.byteLeft})`)
            this._dataView.setInt8(this.pos, data[i]);
            this.pos++;
        }
        return this;
    };
    KaitaiStreamWriter.prototype._checkSize = function (byteSize) {
        var availableSize = this.byteLeft;
        // console.debug(`_checkSize`, `request ${byteSize} bytes (available ${availableSize}; offset ${this.byteOffset})`)
        if (availableSize < byteSize) {
            var newSize = this._dataView.byteLength + (Math.max(this._dataView.byteLength, byteSize * 1.1) - this.byteLeft);
            this.resize(newSize);
            logger.debug("_checkSize", "Resizing: " + this.buffer.byteLength + ". Offset: " + this.byteOffset);
        }
    };
    KaitaiStreamWriter.prototype.resize = function (newSize) {
        var newBuffer = new Uint8Array(newSize);
        var currentData = this.toBytes;
        newBuffer.set(currentData);
        this.buffer = newBuffer.buffer;
    };
    return KaitaiStreamWriter;
}(kaitai_stream_1.KaitaiStream));
exports.KaitaiStreamWriter = KaitaiStreamWriter;
