export default class HashMap<T> {
    private valueMap: Map<string, T> = new Map();
    private keyMap: Map<string, object> = new Map();

    private hashCode: (key: object) => string;

    public constructor(hashCode: (key: object) => string) {
        this.hashCode = hashCode;
    }

    public clear(): void {
        this.valueMap.clear();
        this.keyMap.clear();
    }

    public delete(key: object): boolean {
        if (key !== undefined) {
            let hc = this.hashCode(key);
            this.keyMap.delete(hc);
            return this.valueMap.delete(hc);
        }
    }

    public get(key: object): T | undefined {
        if (key !== undefined) {
            return this.valueMap.get(this.hashCode(key));
        } else {
            return undefined;
        }
    }

    public has(key: object): boolean {
        if (key !== undefined) {
            return this.valueMap.has(this.hashCode(key));
        } else {
            return undefined;
        }
    }

    public set(key: object, value: T): this {
        if (key !== undefined) {
            let hc = this.hashCode(key);
            this.valueMap.set(hc, value);
            this.keyMap.set(hc, key);
        }
        return this;
    }

    public updateKey(oldKey: object, newKey: object): boolean {
        if(this.has(oldKey)) {
            let oldHc = this.hashCode(oldKey);
            let value = this.valueMap.get(oldHc);

            // delete old references
            this.valueMap.delete(oldHc);
            this.keyMap.delete(oldHc);

            // set store new key value pair
            this.set(newKey, value);

            return true;
        } else {
            return false;
        }
    }

    public size(): number {
        return this.valueMap.size;
    }

    // public entries(): IterableIterator<[HashKey, T]> {
    //     return new HashKeyEntriesIter<T>(this.internalMap.entries(), this.keyMap);
    // }

    public keys(): IterableIterator<object> {
        return this.keyMap.values();
    }

    public values(): IterableIterator<T> {
        return this.valueMap.values();
    }

    public [Symbol.toStringTag]: string;
}

export class HashSet extends HashMap<object> {
    public add(value: object): HashSet {
        if (value) {
            this.set(value, value);
        }
        return this;
    }
}