"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PacketLengthParser = void 0;
const stream_1 = require("stream");
/**
 * A transform stream that decodes packets with a delimiter and length of payload
 * specified within the data stream.
 * @extends Transform
 * @summary Decodes packets of the general form:
 *       [delimiter][len][payload0] ... [payload0 + len]
 *
 * The length field can be up to 4 bytes and can be at any offset within the packet
 *       [delimiter][header0][header1][len0][len1[payload0] ... [payload0 + len]
 *
 * The offset and number of bytes of the length field need to be provided in options
 * if not 1 byte immediately following the delimiter.
 */
class PacketLengthParser extends stream_1.Transform {
    buffer;
    start;
    opts;
    constructor(options = {}) {
        super(options);
        const { delimiter = [0xaa], delimiterBytes = 1, packetOverhead = 2, lengthBytes = 1, lengthOffset = 1, maxLen = 0xff } = options;
        this.opts = {
            delimiter: [].concat(delimiter),
            delimiterBytes,
            packetOverhead,
            lengthBytes,
            lengthOffset,
            maxLen,
        };
        this.buffer = Buffer.alloc(0);
        this.start = false;
    }
    _transform(chunk, encoding, cb) {
        for (let ndx = 0; ndx < chunk.length; ndx++) {
            const byte = chunk[ndx];
            if (true === this.start) {
                this.buffer = Buffer.concat([this.buffer, Buffer.from([byte])]);
                if (this.buffer.length >= this.opts.lengthOffset + this.opts.lengthBytes) {
                    const len = this.buffer.readUIntLE(this.opts.lengthOffset, this.opts.lengthBytes);
                    if (this.buffer.length == len + this.opts.packetOverhead || len > this.opts.maxLen) {
                        this.push(this.buffer);
                        this.buffer = Buffer.alloc(0);
                        this.start = false;
                    }
                }
            }
            else {
                this.buffer = Buffer.concat([Buffer.from([byte]), this.buffer]);
                if (this.buffer.length === this.opts.delimiterBytes) {
                    const delimiter = this.buffer.readUIntLE(0, this.opts.delimiterBytes);
                    if (this.opts.delimiter.includes(delimiter)) {
                        this.start = true;
                        this.buffer = Buffer.from([...this.buffer].reverse());
                    }
                    else {
                        this.buffer = Buffer.from(this.buffer.subarray(1, this.buffer.length));
                    }
                }
            }
        }
        cb();
    }
    _flush(cb) {
        this.push(this.buffer);
        this.buffer = Buffer.alloc(0);
        cb();
    }
}
exports.PacketLengthParser = PacketLengthParser;
