/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { keyFromSymbol, symbolFromKey } from "./globalFieldKeySymbol";
/**
 * Derives the scope using the type of `key`.
 */
export function scopeFromKey(key) {
    return isGlobalFieldKey(key)
        ? ["globalFields" /* global */, keyFromSymbol(key)]
        : ["fields" /* local */, key];
}
/**
 * Derives the scope using the type of `key`.
 * @alpha
 */
export function isGlobalFieldKey(key) {
    return typeof key === "symbol";
}
/**
 * Get a field from `node`, optionally modifying the tree to create it if missing.
 */
export function getGenericTreeField(node, key, createIfMissing) {
    const [scope, keyString] = scopeFromKey(key);
    const children = getGenericTreeFieldMap(node, scope, createIfMissing);
    // Do not just read field and check for undefined: see warning on FieldMapObject.
    if (Object.prototype.hasOwnProperty.call(children, keyString)) {
        return children[keyString];
    }
    // Handle missing field:
    if (createIfMissing === false) {
        return [];
    }
    const newField = [];
    children[keyString] = newField;
    return newField;
}
/**
 * The scope of a {@link FieldKey}.
 * @alpha
 */
export var FieldScope;
(function (FieldScope) {
    FieldScope["local"] = "fields";
    FieldScope["global"] = "globalFields";
})(FieldScope || (FieldScope = {}));
/**
 * Get a FieldMap from `node`, optionally modifying the tree to create it if missing.
 */
function getGenericTreeFieldMap(node, scope, createIfMissing) {
    let children = node[scope];
    if (children === undefined) {
        children = {};
        // Handle missing fields:
        if (createIfMissing) {
            node[scope] = children;
        }
    }
    return children;
}
/**
 * Sets a field on `node`.
 */
export function setGenericTreeField(node, key, content) {
    const [scope, keyString] = scopeFromKey(key);
    const children = getGenericTreeFieldMap(node, scope, true);
    // like `children[keyString] = content;` except safe when keyString == "__proto__".
    Object.defineProperty(children, keyString, {
        enumerable: true,
        configurable: true,
        writable: true,
        value: content,
    });
}
/**
 * @returns keys for fields of `tree`.
 */
export function genericTreeKeys(tree) {
    const local = tree["fields" /* local */];
    const global = tree["globalFields" /* global */];
    // This function is used when iterating through a tree.
    // This means that this is often called on nodes with no keys
    // (most trees are a large portion leaf nodes).
    // Therefore this function special cases empty fields objects as an optimization.
    if (local === undefined) {
        if (global === undefined) {
            return [];
        }
        return Object.keys(global).map(symbolFromKey);
    }
    if (global === undefined) {
        return Object.keys(local);
    }
    return [
        ...Object.keys(local),
        ...Object.keys(global).map(symbolFromKey),
    ];
}
/**
 * Delete a field if empty.
 * Optionally delete FieldMapObject if empty as well.
 */
export function genericTreeDeleteIfEmpty(node, key, removeMapObject) {
    const [scope, keyString] = scopeFromKey(key);
    const children = getGenericTreeFieldMap(node, scope, false);
    if (Object.prototype.hasOwnProperty.call(children, keyString)) {
        if (children[keyString].length === 0) {
            // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
            delete children[keyString];
            if (removeMapObject) {
                if (Object.keys(children).length === 0) {
                    // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
                    delete node[scope];
                }
            }
        }
    }
}
//# sourceMappingURL=treeTextFormat.js.map