// A [trie](https://en.wikipedia.org/wiki/Trie) data structure that holds
// object keys weakly, yet can also hold non-object keys, unlike the
// native `WeakMap`.
// If no makeData function is supplied, the looked-up data will be an empty,
// null-prototype Object.
const defaultMakeData = () => Object.create(null);
// Useful for processing arguments objects as well as arrays.
const { forEach, slice } = Array.prototype;
const { hasOwnProperty } = Object.prototype;
export class Trie {
    constructor(weakness = true, makeData = defaultMakeData) {
        this.weakness = weakness;
        this.makeData = makeData;
    }
    lookup() {
        return this.lookupArray(arguments);
    }
    lookupArray(array) {
        let node = this;
        forEach.call(array, key => node = node.getChildTrie(key));
        return hasOwnProperty.call(node, "data")
            ? node.data
            : node.data = this.makeData(slice.call(array));
    }
    peek() {
        return this.peekArray(arguments);
    }
    peekArray(array) {
        let node = this;
        for (let i = 0, len = array.length; node && i < len; ++i) {
            const map = node.mapFor(array[i], false);
            node = map && map.get(array[i]);
        }
        return node && node.data;
    }
    remove() {
        return this.removeArray(arguments);
    }
    removeArray(array) {
        let data;
        if (array.length) {
            const head = array[0];
            const map = this.mapFor(head, false);
            const child = map && map.get(head);
            if (child) {
                data = child.removeArray(slice.call(array, 1));
                if (!child.data && !child.weak && !(child.strong && child.strong.size)) {
                    map.delete(head);
                }
            }
        }
        else {
            data = this.data;
            delete this.data;
        }
        return data;
    }
    getChildTrie(key) {
        const map = this.mapFor(key, true);
        let child = map.get(key);
        if (!child)
            map.set(key, child = new Trie(this.weakness, this.makeData));
        return child;
    }
    mapFor(key, create) {
        return this.weakness && isObjRef(key)
            ? this.weak || (create ? this.weak = new WeakMap : void 0)
            : this.strong || (create ? this.strong = new Map : void 0);
    }
}
function isObjRef(value) {
    switch (typeof value) {
        case "object":
            if (value === null)
                break;
        // Fall through to return true...
        case "function":
            return true;
    }
    return false;
}
//# sourceMappingURL=index.js.map