"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var defaultSize = 8192;
var contentLength = "Content-Length: ";
var contentLengthSize = Buffer.byteLength(contentLength, "utf8");
var blank = Buffer.from(" ", "utf8")[0];
var backslashR = Buffer.from("\r", "utf8")[0];
var backslashN = Buffer.from("\n", "utf8")[0];
var ProtocolBuffer = /** @class */ (function () {
    function ProtocolBuffer() {
        this.index = 0;
        this.buffer = Buffer.allocUnsafe(defaultSize);
    }
    ProtocolBuffer.prototype.append = function (data) {
        var toAppend = null;
        if (Buffer.isBuffer(data)) {
            toAppend = data;
        }
        else {
            toAppend = Buffer.from(data, "utf8");
        }
        if (this.buffer.length - this.index >= toAppend.length) {
            toAppend.copy(this.buffer, this.index, 0, toAppend.length);
        }
        else {
            var newSize = (Math.ceil((this.index + toAppend.length) / defaultSize) + 1) *
                defaultSize;
            if (this.index === 0) {
                this.buffer = Buffer.allocUnsafe(newSize);
                toAppend.copy(this.buffer, 0, 0, toAppend.length);
            }
            else {
                this.buffer = Buffer.concat([this.buffer.slice(0, this.index), toAppend], newSize);
            }
        }
        this.index += toAppend.length;
    };
    ProtocolBuffer.prototype.tryReadContentLength = function () {
        var result = -1;
        var current = 0;
        // we are utf8 encoding...
        while (current < this.index &&
            (this.buffer[current] === blank ||
                this.buffer[current] === backslashR ||
                this.buffer[current] === backslashN)) {
            current++;
        }
        if (this.index < current + contentLengthSize) {
            return result;
        }
        current += contentLengthSize;
        var start = current;
        while (current < this.index && this.buffer[current] !== backslashR) {
            current++;
        }
        if (current + 3 >= this.index ||
            this.buffer[current + 1] !== backslashN ||
            this.buffer[current + 2] !== backslashR ||
            this.buffer[current + 3] !== backslashN) {
            return result;
        }
        var data = this.buffer.toString("utf8", start, current);
        result = parseInt(data);
        this.buffer = this.buffer.slice(current + 4);
        this.index = this.index - (current + 4);
        return result;
    };
    ProtocolBuffer.prototype.tryReadContent = function (length) {
        if (this.index < length) {
            return null;
        }
        var result = this.buffer.toString("utf8", 0, length);
        var sourceStart = length;
        while (sourceStart < this.index &&
            (this.buffer[sourceStart] === backslashR ||
                this.buffer[sourceStart] === backslashN)) {
            sourceStart++;
        }
        this.buffer.copy(this.buffer, 0, sourceStart);
        this.index = this.index - sourceStart;
        return result;
    };
    return ProtocolBuffer;
}());
var Reader = /** @class */ (function () {
    function Reader(readable, onData, onError) {
        var _this = this;
        this.onData = onData;
        this.onError = onError;
        this.buffer = new ProtocolBuffer();
        this.nextMessageLength = -1;
        readable.on("data", function (data) { return _this.onLengthData(data); });
    }
    Reader.prototype.onLengthData = function (data) {
        try {
            this.buffer.append(data);
            while (true) {
                if (this.nextMessageLength === -1) {
                    this.nextMessageLength = this.buffer.tryReadContentLength();
                    if (this.nextMessageLength === -1) {
                        return;
                    }
                }
                var msg = this.buffer.tryReadContent(this.nextMessageLength);
                if (msg === null) {
                    return;
                }
                this.nextMessageLength = -1;
                var json = JSON.parse(msg);
                this.onData(json);
            }
        }
        catch (error) {
            this.onError(error);
        }
    };
    return Reader;
}());
exports.Reader = Reader;
