/** @internal NOTE: Internal APIs. Subject to change. Use of these APIs in production applications is not supported. */
import '../MapExtensions';
/** Biderctional map. Its a normal map but by using the {@link reverse} property it is possible to perform lookups
 *  in the "reverse" direction.
 */
export class BidirectionalMap<K, T> implements Map<K, T> {
    private _byKey = new Map<K, T>();
    private _byVal = new Map<T, K>();
    private readonly _reverse: BidirectionalMap<T, K>;

    public constructor(parent?: BidirectionalMap<T, K>) {
        if (parent === undefined) {
            this._reverse = new BidirectionalMap<T, K>(this);
            this._reverse._byKey = this._byVal;
            this._reverse._byVal = this._byKey;
        } else {
            this._reverse = parent;
        }
    }

    public get reverse(): Map<T, K> {
        return this._reverse;
    }

    getOrAdd(key: K, factory: (key: K) => T): T {
        const ret = this.getOrAdd(key, (_key) => {
            const v = factory(_key);
            this._byVal.getOrAdd(v, () => _key);
            return v;
        });
        return ret;
    }

    /** See {@link Map} */
    clear(): void {
        this._byKey.clear();
        this._byVal.clear();
    }
    /** See {@link Map} */
    delete(key: K): boolean {
        const v = this._byKey.get(key);
        if (v) {
            this._byKey.delete(key);
            this._byVal.delete(v);
        }
        return v !== undefined;
    }
    /** See {@link Map} */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
    forEach(callbackfn: (value: T, key: K, map: Map<K, T>) => void, thisArg?: any): void {
        this._byKey.forEach(callbackfn, thisArg);
    }
    /** See {@link Map} */
    get(key: K): T | undefined {
        return this._byKey.get(key);
    }
    /** See {@link Map} */
    has(key: K): boolean {
        return this._byKey.has(key);
    }
    /** See {@link Map} */
    set(key: K, value: T): this {
        this._byKey.set(key, value);
        this._byVal.set(value, key);
        return this;
    }
    /** See {@link Map} */
    get size(): number {
        return this._byKey.size;
    }
    /** See {@link Map} */
    [Symbol.iterator](): MapIterator<[K, T]> {
        return this._byKey[Symbol.iterator]();
    }
    /** See {@link Map} */
    entries(): MapIterator<[K, T]> {
        return this._byKey.entries();
    }
    /** See {@link Map} */
    keys(): MapIterator<K> {
        return this._byKey.keys();
    }
    /** See {@link Map} */
    values(): MapIterator<T> {
        return this._byKey.values();
    }
    /** See {@link Map} */
    get [Symbol.toStringTag](): string {
        return this._byKey[Symbol.toStringTag];
    }
}
