import type { MaybeArray } from './types';

export function create2DArray<T>(
  height: number,
  width: number,
  cell: T | ((x: number, y: number) => T),
): T[][] {
  return Array.from({ length: height }, (_, y) =>
    Array.from({ length: width }, (_, x) => (cell instanceof Function ? cell(x, y) : cell)),
  );
}
export function* iter2DArray<T>(grid: T[][]): Generator<[number, number, T], void, unknown> {
  for (let y = 0; y < grid.length; y++) {
    for (let x = 0; x < grid[y].length; x++) {
      yield [x, y, grid[y][x]];
    }
  }
}
export type Zip<T extends Iterable<unknown>[]> = T extends [
  infer R,
  ...infer S extends Iterable<unknown>[],
]
  ? R extends Iterable<infer U>
    ? [U, ...Zip<S>]
    : []
  : [];
export function* zip<T extends Iterable<unknown>[]>(...its: T): Generator<Zip<T>, void, unknown> {
  const iterators = its.map((it) => it[Symbol.iterator]());
  while (true) {
    const nextValues = iterators.map((it) => it.next());
    if (nextValues.some(({ done }) => done)) break;
    yield nextValues.map(({ value }) => value) as any;
  }
}
export function get2DArrayItem<T>(
  grid: T[][],
  first: number,
  second: number,
  colFirst: boolean,
): T {
  return colFirst ? grid[first][second] : grid[second][first];
}
export function ensureArray<T>(obj: MaybeArray<T, true>): T[];
export function ensureArray(obj: unknown) {
  return Array.isArray(obj) ? obj : [obj];
}
export const arrayIncludes = <T, R>(array: T[], value: R): value is T & R =>
  array.includes(value as any);
