import { DoubleQueue } from './double-queue';
import type { Future } from './future';
export class AsyncQueue<T> {
  protected _queue: DoubleQueue<T>;
  protected _getters = new DoubleQueue<Future<T>>();

  constructor(iterable: Iterable<T> = []) {
    this._queue = new DoubleQueue(iterable);
  }

  get size() {
    return this._queue.size;
  }

  async shift(): Promise<T> {
    if (this._queue.size) {
      return this._queue.shift() as T;
    }
    const future: Future<T> = Promise.withResolvers();
    this._getters.push(future);
    return future.promise;
  }

  push(...values: T[]): this {
    for (const value of values) {
      const future = this._getters.shift();
      if (future !== undefined) {
        future.resolve(value);
      } else {
        this._queue.push(value);
      }
    }
    return this;
  }

  cancelGetters() {
    for (const future of this._getters) {
      future.reject(new AsyncQueue.CancelledError());
    }
  }
}

export namespace AsyncQueue {
  export class CancelledError extends Error {}
}
