"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 internal_1 = require("@fluidframework/core-utils/internal");
const index_js_1 = require("../../core/index.js");
const index_js_2 = require("../typed-schema/index.js");
const nodeKey_js_1 = require("./nodeKey.js");
/**
 * 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.nodeSchema.get(nodeKey_js_1.nodeKeyTreeIdentifier);
        if (!(treeSchema instanceof index_js_2.LeafNodeSchema)) {
            return false;
        }
        return treeSchema.leafValue === index_js_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 flex 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, internal_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) {
        if ((0, index_js_2.schemaIsObjectNode)(node.schema)) {
            const key = node.localNodeKey;
            if (key !== undefined) {
                yield [key, node];
            }
        }
        for (const f of node.boxedIterator()) {
            yield* this.findKeysInField(f);
        }
    }
    *findKeysInField(f) {
        for (const child of f.boxedIterator()) {
            yield* this.findKeys(child);
        }
    }
}
exports.NodeKeyIndex = NodeKeyIndex;
//# sourceMappingURL=nodeKeyIndex.js.map