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

declare global {
  interface Generator<T = unknown, TReturn = any, TNext = unknown> {
    sequence_equal(
      this: Generator<number, TReturn, TNext>,
      that: Generator<number, TReturn, TNext> | number[]
    ): boolean;
    sequence_equal(
      this: Generator<string, TReturn, TNext>,
      that: Generator<string, TReturn, TNext> | string[]
    ): boolean;
    sequence_equal(
      this: Generator<boolean, TReturn, TNext>,
      that: Generator<boolean, TReturn, TNext> | boolean[]
    ): boolean;
    sequence_equal(
      that: Generator<T, TReturn, TNext> | T[],
      comparitor: (a: T, b: T) => boolean
    ): boolean;
  }

  interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> {
    sequence_equal(
      this: AsyncGenerator<number, TReturn, TNext>,
      that:
        | AsyncGenerator<number, TReturn, TNext>
        | Generator<number, TReturn, TNext>
        | number[]
    ): Promise<boolean>;
    sequence_equal(
      this: AsyncGenerator<string, TReturn, TNext>,
      that:
        | AsyncGenerator<string, TReturn, TNext>
        | Generator<string, TReturn, TNext>
        | string[]
    ): Promise<boolean>;
    sequence_equal(
      this: AsyncGenerator<boolean, TReturn, TNext>,
      that:
        | AsyncGenerator<boolean, TReturn, TNext>
        | Generator<boolean, TReturn, TNext>
        | boolean[]
    ): Promise<boolean>;
    sequence_equal(
      that:
        | AsyncGenerator<T, TReturn, TNext>
        | Generator<T, TReturn, TNext>
        | T[],
      comparitor: (a: T, b: T) => boolean
    ): Promise<boolean>;
  }
}

Build(
  "sequence_equal",
  function (that, comparitor = (a: unknown, b: unknown) => 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 (!comparitor(this_part.value, that_part.value)) {
        return false;
      }

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

    return true;
  },
  async function (that, comparitor = (a: unknown, b: unknown) => 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 (!comparitor(this_part.value, that_part.value)) {
        return false;
      }

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

    return true;
  }
);
