"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeKeyIndex = void 0;
const core_utils_1 = require("@fluidframework/core-utils");
const core_1 = require("../../core");
const editable_tree_2_1 = require("../editable-tree-2");
const nodeKey_1 = require("./nodeKey");
/**
 * The node key index records nodes with {@link LocalNodeKey}s and allows them to be looked up by key.
 */
class NodeKeyIndex {
    constructor(fieldKey, keys = []) {
        this.fieldKey = fieldKey;
        this.nodes = new Map(keys);
    }
    /**
     * Returns true if the given schema contains the node key type, otherwise false
     */
    static hasNodeKeyTreeSchema(schema) {
        // TODO: make TreeStoredSchema contain ViewSchema and compare by reference to nodeKeyTreeSchema.
        const treeSchema = schema.treeSchema.get(nodeKey_1.nodeKeyTreeIdentifier);
        if (treeSchema === undefined) {
            return false;
        }
        return treeSchema.leafValue === core_1.ValueSchema.String;
    }
    /**
     * Search the tree for all nodes with keys, and record them in this index for lookup.
     * This should be called each time the tree changes; each call to scan forgets all existing keys.
     * @param context - the editable tree context in which to search for node keys
     */
    // TODO: This can be optimized by responding to deltas/changes to the tree, rather than rescanning the whole tree every time
    scanKeys(context) {
        this.nodes.clear();
        if (NodeKeyIndex.hasNodeKeyTreeSchema(context.schema)) {
            for (const [id, node] of this.findKeysInField(context.root)) {
                // TODO:
                // This invariant (that there is only one node with a given key) is not enforced by tree, so it should not assert.
                // Multiple nodes (including deleted ones), might occur with the same key.
                (0, core_utils_1.assert)(!this.nodes.has(id), 0x6e1 /* Encountered duplicate node key */);
                this.nodes.set(id, node);
            }
        }
    }
    /**
     * Create a copy of this index which can be mutated without affecting this one.
     */
    clone(context) {
        const indexClone = new NodeKeyIndex(this.fieldKey);
        indexClone.scanKeys(context);
        return indexClone;
    }
    // #region ReadonlyMap interface
    forEach(callbackfn, thisArg) {
        return this.nodes.forEach(callbackfn, thisArg);
    }
    get(key) {
        return this.nodes.get(key);
    }
    has(key) {
        return this.nodes.has(key);
    }
    get size() {
        return this.nodes.size;
    }
    entries() {
        return this.nodes.entries();
    }
    keys() {
        return this.nodes.keys();
    }
    values() {
        return this.nodes.values();
    }
    [Symbol.iterator]() {
        return this.nodes[Symbol.iterator]();
    }
    // #endregion ReadonlyMap interface
    *findKeys(node) {
        const key = node.localNodeKey;
        if (key !== undefined) {
            yield [key, node];
        }
        for (const f of node[editable_tree_2_1.boxedIterator]()) {
            yield* this.findKeysInField(f);
        }
    }
    *findKeysInField(f) {
        for (const child of f[editable_tree_2_1.boxedIterator]()) {
            yield* this.findKeys(child);
        }
    }
}
exports.NodeKeyIndex = NodeKeyIndex;
//# sourceMappingURL=nodeKeyIndex.js.map