import zlib from "zlib";
import { writeUint32LE } from "./utils/binary.js";
import { PullStream, runParseLoop, streamUntilValidatedDataDescriptor } from "./parse.base.js";
import { DATA_DESCRIPTOR_SIG } from "./zip-constants.js";
/**
 * Creates an InflateRaw stream using Node.js native zlib.
 */
function createInflateRaw() {
    return zlib.createInflateRaw();
}
const dataDescriptorSignature = writeUint32LE(DATA_DESCRIPTOR_SIG);
export function createParseClass(createInflateRawFn) {
    return class Parse extends 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.
            runParseLoop(this._opts, io, emitter, createInflateRawFn, this._driverState, (data) => zlib.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 streamUntilValidatedDataDescriptor({
                source: {
                    getView: () => this._queue.view(),
                    getLength: () => this._queue.length,
                    read: (length) => this._queue.read(length),
                    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);
export class Parse extends BaseParse {
}
export function createParse(opts) {
    return new Parse(opts);
}
