import { Build, Promised } from "./utils";
import "./geninq";

declare global {
  interface Generator<T = unknown, TReturn = any, TNext = unknown> {
    zip<TInput>(
      that: Generator<TInput, TReturn, TNext> | TInput[]
    ): Generator<[T, TInput], TReturn, TNext>;
    zip<TInput, TResult>(
      that: Generator<TInput, TReturn, TNext> | TInput[],
      selector: (a: T, b: TInput) => TResult
    ): Generator<TResult, TReturn, TNext>;
  }

  interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> {
    zip<TInput>(
      that:
        | AsyncGenerator<TInput, TReturn, TNext>
        | Generator<TInput, TReturn, TNext>
        | TInput[]
    ): AsyncGenerator<[T, TInput], TReturn, TNext>;
    zip<TInput, TResult>(
      that:
        | AsyncGenerator<TInput, TReturn, TNext>
        | Generator<TInput, TReturn, TNext>
        | TInput[],
      selector: (a: T, b: TInput) => Promised<TResult>
    ): AsyncGenerator<TResult, TReturn, TNext>;
  }
}

Build(
  "zip",
  function* (that, selector = (a, b) => [a, b]) {
    that = Array.isArray(that) ? that.geninq() : that;
    let this_part = this.next();
    let that_part = that.next();
    while (!this_part.done || !that_part.done) {
      if (this_part.done || that_part.done)
        throw new Error("Sequences are unequal in length");
      yield selector(this_part.value, that_part.value);

      this_part = this.next();
      that_part = that.next();
    }
  },
  async function* (that, selector = (a, b) => [a, b]) {
    that = Array.isArray(that) ? that.geninq() : that;
    let this_part = await this.next();
    let that_part = await that.next();
    while (!this_part.done || !that_part.done) {
      if (this_part.done || that_part.done)
        throw new Error("Sequences are unequal in length");
      yield selector(this_part.value, that_part.value);

      this_part = await this.next();
      that_part = await that.next();
    }
  }
);
