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

declare global {
  interface Generator<T = unknown, TReturn = any, TNext = unknown> {
    /**
     * Returns distinct elements from a sequence by using the default equality comparer to compare values.
     * @returns A generator that contains distinct elements from the source sequence.
     */
    distinct(
      this: Generator<number, TReturn, TNext>
    ): Generator<number, TReturn, TNext>;
    /**
     * Returns distinct elements from a sequence by using the default equality comparer to compare values.
     * @returns A generator that contains distinct elements from the source sequence.
     */
    distinct(
      this: Generator<string, TReturn, TNext>
    ): Generator<string, TReturn, TNext>;
    /**
     * Returns distinct elements from a sequence by using the default equality comparer to compare values.
     * @returns A generator that contains distinct elements from the source sequence.
     */
    distinct(
      this: Generator<boolean, TReturn, TNext>
    ): Generator<boolean, TReturn, TNext>;
    /**
     * Returns distinct elements from a sequence by using a specified comparitor to compare values.
     * @param comparitor A comparitor to compare values.
     * @returns A generator that contains distinct elements from the source sequence.
     */
    distinct(comparitor: (a: T, b: T) => boolean): Generator<T, TReturn, TNext>;
  }

  interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> {
    /**
     * Returns distinct elements from a sequence by using the default equality comparer to compare values.
     * @returns A generator that contains distinct elements from the source sequence.
     */
    distinct(
      this: AsyncGenerator<number, TReturn, TNext>
    ): AsyncGenerator<number, TReturn, TNext>;
    /**
     * Returns distinct elements from a sequence by using the default equality comparer to compare values.
     * @returns A generator that contains distinct elements from the source sequence.
     */
    distinct(
      this: AsyncGenerator<string, TReturn, TNext>
    ): AsyncGenerator<string, TReturn, TNext>;
    /**
     * Returns distinct elements from a sequence by using the default equality comparer to compare values.
     * @returns A generator that contains distinct elements from the source sequence.
     */
    distinct(
      this: AsyncGenerator<boolean, TReturn, TNext>
    ): AsyncGenerator<boolean, TReturn, TNext>;
    /**
     * Returns distinct elements from a sequence by using a specified comparitor to compare values.
     * @param comparitor A comparitor to compare values.
     * @returns A generator that contains distinct elements from the source sequence.
     */
    distinct(
      comparitor: (a: T, b: T) => Promised<boolean>
    ): AsyncGenerator<T, TReturn, TNext>;
  }
}

Build(
  "distinct",
  function* (comparitor = (a: unknown, b: unknown) => a === b) {
    const existing: unknown[] = [];
    for (const item of this) {
      let valid = true;
      for (const part of existing) {
        if (comparitor(item, part)) {
          valid = false;
          break;
        }
      }

      if (valid) {
        existing.push(item);
        yield item;
      }
    }
  },
  async function* (comparitor = (a: unknown, b: unknown) => a === b) {
    const existing: unknown[] = [];
    for await (const item of this) {
      let valid = true;
      for (const part of existing) {
        if (await Promise.resolve(comparitor(item, part))) {
          valid = false;
          break;
        }
      }

      if (valid) {
        existing.push(item);
        yield item;
      }
    }
  }
);
