"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Parse = void 0;
exports.createParseClass = createParseClass;
exports.createParse = createParse;
const zlib_1 = __importDefault(require("zlib"));
const stream_base_1 = require("./stream.base.js");
/**
 * Creates an InflateRaw stream using Node.js native zlib.
 */
function createInflateRaw() {
    return zlib_1.default.createInflateRaw();
}
const dataDescriptorSignature = stream_base_1.DATA_DESCRIPTOR_SIGNATURE_BYTES;
function createParseClass(createInflateRawFn) {
    return class Parse extends stream_base_1.PullStream {
        constructor(opts = {}) {
            super();
            this._driverState = {};
            this._opts = opts;
            const io = {
                pull: async (length) => this.pull(length),
                pullUntil: async (pattern, includeEof) => this.pull(pattern, includeEof),
                stream: (length) => this.stream(length),
                streamUntilDataDescriptor: () => this._streamUntilValidatedDataDescriptor(),
                setDone: () => {
                    this.end();
                    this.push(null);
                }
            };
            const emitter = {
                emitEntry: (entry) => {
                    this.emit("entry", entry);
                },
                pushEntry: (entry) => {
                    this.push(entry);
                },
                pushEntryIfPiped: (entry) => {
                    const state = this._readableState;
                    if (state.pipesCount || (state.pipes && state.pipes.length)) {
                        this.push(entry);
                    }
                },
                emitCrxHeader: header => {
                    this.crxHeader = header;
                    this.emit("crx-header", header);
                },
                emitError: err => {
                    this.emit("error", err);
                },
                emitClose: () => {
                    this.emit("close");
                }
            };
            // Parse records as data arrives. Only emit `close` when parsing is complete.
            (0, stream_base_1.runParseLoop)(this._opts, io, emitter, createInflateRawFn, this._driverState, (data) => zlib_1.default.inflateRawSync(data)).catch((e) => {
                if (!this.__emittedError || this.__emittedError !== e) {
                    this.emit("error", e);
                }
                this.emit("close");
            });
        }
        /**
         * Stream file data until we reach a DATA_DESCRIPTOR record boundary.
         */
        _streamUntilValidatedDataDescriptor() {
            return (0, stream_base_1.streamUntilValidatedDataDescriptor)({
                source: {
                    getLength: () => this._queue.length,
                    read: (length) => this._queue.read(length),
                    peekChunks: (length) => this._queue.peekChunks(length),
                    discard: (length) => this._queue.discard(length),
                    indexOfPattern: (pattern, startIndex) => this._queue.indexOfPattern(pattern, startIndex),
                    peekUint32LE: (offset) => this._queue.peekUint32LE(offset),
                    isFinished: () => this.finished,
                    onDataAvailable: (cb) => {
                        this.on("chunk", cb);
                        return () => this.removeListener("chunk", cb);
                    },
                    maybeReleaseWriteCallback: () => this._maybeReleaseWriteCallback()
                },
                dataDescriptorSignature
            });
        }
        promise() {
            return new Promise((resolve, reject) => {
                const done = () => resolve();
                this.on("end", done);
                this.on("close", done);
                this.on("error", reject);
            });
        }
    };
}
const BaseParse = createParseClass(createInflateRaw);
class Parse extends BaseParse {
}
exports.Parse = Parse;
function createParse(opts) {
    return new Parse(opts);
}
