/** @internal NOTE: Internal APIs. Subject to change. Use of these APIs in production applications is not supported. */ /** */

import { TypedArrayLike } from './bim-format-types';

export interface Lookup<T> {
    [key: string]: T;
}

export function lookupClear<T>(lookup: Lookup<T>): void {
    for (const propName of Object.keys(lookup)) {
        delete lookup[propName];
    }
}

export function lookupGetOrAdd<T>(lookup: Lookup<T>, key: string, factory: (key: string) => T): T {
    return (lookup[key] ??= factory(key));
}

export function toLookup<KEY, VAL>(
    ar: KEY[],
    keySelector: (i: KEY) => string,
    valueSelector?: (i: KEY) => VAL
): Lookup<VAL> {
    valueSelector ??= (i: KEY) => i as unknown as VAL;
    const ret: Lookup<VAL> = {};
    for (const a of ar) {
        ret[keySelector(a)] = valueSelector(a);
    }
    return ret;
}

export function toMap<T, KEY, VAL>(ar: T[], keySelector: (i: T) => KEY, valueSelector?: (i: T) => VAL): Map<KEY, VAL> {
    valueSelector = valueSelector ?? ((i: T) => i as unknown as VAL);
    return new Map<KEY, VAL>(ar.map((a) => [keySelector(a), valueSelector!(a)]));
}

export function mergeInto<T extends TypedArrayLike>(dst: T, src?: T, offsetInDst?: number): T {
    if (src && src.length > 0) dst.set(src, offsetInDst || 0); // Copy src into dst starting at pos 0. We will write new data after this position
    return dst;
}

// export function padLeft(data: number, n: number, padding: string = "0"): string {
//    const padStr = new Array<string>(n).fill(padding).join("");
//    return `${data < 0 ? -1: undefined}${padStr}${Math.abs(data)}`.slice(-n);
// }

export function distinct<T>(items: T[], selector: (i: T) => string): T[] {
    const distinct: T[] = [];
    const lookup: Lookup<boolean> = {};
    for (const item of items) {
        lookupGetOrAdd(lookup, selector(item), (_) => {
            distinct.push(item);
            return true;
        });
    }
    return distinct;
}

export function groupBy<T>(items: T[], selector: (i: T) => string): Lookup<T[]> {
    const lookup: Lookup<[]> = {};
    for (const item of items) {
        lookupGetOrAdd(lookup, selector(item), (_key) => [] as T[]).push(item);
    }
    return lookup;
}
