"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.setFlexNode = exports.tryGetFlexNodeTarget = exports.tryGetFlexNode = exports.getFlexNode = void 0;
const core_utils_1 = require("@fluidframework/core-utils");
const index_js_1 = require("../util/index.js");
const index_js_2 = require("../feature-libraries/index.js");
const rawNode_js_1 = require("./rawNode.js");
/** Associates an FlexTreeNode with a target object  */
const targetSymbol = Symbol("FlexNodeTarget");
/**
 * This is intentionally a WeakMap, rather than a private symbol (e.g. like `targetSymbol`).
 * The map behaves essentially the same, except that performing a lookup in the map will not perform a property read/get on the key object (as is the case with a symbol).
 * Since `SharedTreeNodes` are proxies with non-trivial `get` traps, this choice is meant to prevent the confusion of the lookup passing through multiple objects
 * via the trap, or the trap not properly handling the special symbol, etc.
 */
const flexNodeMap = new WeakMap();
function getFlexNode(target, allowFreed = false) {
    const node = flexNodeMap.get(target) ?? (0, index_js_1.fail)("Target is not associated with a flex node");
    if (!(node instanceof rawNode_js_1.RawTreeNode) && !allowFreed) {
        (0, index_js_2.assertFlexTreeEntityNotFreed)(node);
    }
    return node;
}
exports.getFlexNode = getFlexNode;
/**
 * Retrieves the flex node associated with the given target via {@link setFlexNode}, if any.
 */
function tryGetFlexNode(target) {
    // Calling 'WeakMap.get()' with primitives (numbers, strings, etc.) will return undefined.
    // This is in contrast to 'WeakMap.set()', which will throw a TypeError if given a non-object key.
    return flexNodeMap.get(target);
}
exports.tryGetFlexNode = tryGetFlexNode;
/**
 * Retrieves the target associated with the given flex node via {@link setFlexNode}, if any.
 */
function tryGetFlexNodeTarget(flexNode) {
    return flexNode[targetSymbol];
}
exports.tryGetFlexNodeTarget = tryGetFlexNodeTarget;
/**
 * Associate the given target object and the given flex node.
 * @returns The target object
 * @remarks
 * This creates a 1:1 mapping between the target and tree node.
 * Either can be retrieved from the other via {@link getFlexNode}/{@link tryGetFlexNode} or {@link tryGetFlexNodeTarget}.
 * If the given target is already mapped to an flex node, the existing mapping will be overwritten.
 * If the given flex node is already mapped to a different target, this function will fail.
 */
function setFlexNode(target, flexNode) {
    (0, core_utils_1.assert)(tryGetFlexNodeTarget(flexNode) === undefined, 0x7f5 /* Cannot associate an flex node with multiple targets */);
    delete flexNodeMap.get(target)?.[targetSymbol];
    flexNodeMap.set(target, flexNode);
    Object.defineProperty(flexNode, targetSymbol, { value: target, configurable: true });
    return target;
}
exports.setFlexNode = setFlexNode;
//# sourceMappingURL=flexNode.js.map