/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { assert } from "@fluidframework/core-utils";
import { fail } from "../util/index.js";
import { assertFlexTreeEntityNotFreed, } from "../feature-libraries/index.js";
import { RawTreeNode } from "./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();
export function getFlexNode(target, allowFreed = false) {
    const node = flexNodeMap.get(target) ?? fail("Target is not associated with a flex node");
    if (!(node instanceof RawTreeNode) && !allowFreed) {
        assertFlexTreeEntityNotFreed(node);
    }
    return node;
}
/**
 * Retrieves the flex node associated with the given target via {@link setFlexNode}, if any.
 */
export 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);
}
/**
 * Retrieves the target associated with the given flex node via {@link setFlexNode}, if any.
 */
export function tryGetFlexNodeTarget(flexNode) {
    return flexNode[targetSymbol];
}
/**
 * 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.
 */
export function setFlexNode(target, flexNode) {
    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;
}
//# sourceMappingURL=flexNode.js.map