"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAsyncQueue = createAsyncQueue;
function createAsyncQueue() {
    const values = [];
    let valuesHead = 0;
    const waiters = [];
    let waitersHead = 0;
    let done = false;
    let error = null;
    const maybeCompact = () => {
        // Prevent unbounded growth of the underlying arrays.
        if (valuesHead > 1024 && valuesHead * 2 > values.length) {
            values.splice(0, valuesHead);
            valuesHead = 0;
        }
        if (waitersHead > 1024 && waitersHead * 2 > waiters.length) {
            waiters.splice(0, waitersHead);
            waitersHead = 0;
        }
    };
    const shiftWaiter = () => {
        while (waitersHead < waiters.length) {
            const w = waiters[waitersHead];
            waiters[waitersHead] = undefined;
            waitersHead++;
            if (w) {
                maybeCompact();
                return w;
            }
        }
        maybeCompact();
        return undefined;
    };
    const shiftValue = () => {
        while (valuesHead < values.length) {
            const v = values[valuesHead];
            values[valuesHead] = undefined;
            valuesHead++;
            if (v !== undefined) {
                maybeCompact();
                return v;
            }
        }
        maybeCompact();
        return undefined;
    };
    const push = (value) => {
        if (done || error) {
            return;
        }
        const waiter = shiftWaiter();
        if (waiter) {
            waiter.resolve({ value, done: false });
        }
        else {
            values.push(value);
        }
    };
    const fail = (err) => {
        if (done || error) {
            return;
        }
        error = err;
        while (true) {
            const waiter = shiftWaiter();
            if (!waiter) {
                break;
            }
            waiter.reject(err);
        }
    };
    const close = () => {
        if (done || error) {
            return;
        }
        done = true;
        while (true) {
            const waiter = shiftWaiter();
            if (!waiter) {
                break;
            }
            waiter.resolve({ value: undefined, done: true });
        }
    };
    const iterable = {
        [Symbol.asyncIterator]() {
            return {
                next() {
                    if (error) {
                        return Promise.reject(error);
                    }
                    const value = shiftValue();
                    if (value !== undefined) {
                        return Promise.resolve({ value, done: false });
                    }
                    if (done) {
                        return Promise.resolve({ value: undefined, done: true });
                    }
                    return new Promise((resolve, reject) => waiters.push({ resolve, reject }));
                }
            };
        }
    };
    return { push, fail, close, iterable };
}
