/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { Lazy } from "@fluidframework/core-utils/internal";
import { UsageError } from "@fluidframework/telemetry-utils/internal";
import { isLazy } from "../feature-libraries/index.js";
import { brand, isReadonlyArray } from "../util/index.js";
/**
 * Kind of a field on a node.
 * @public
 */
export var FieldKind;
(function (FieldKind) {
    /**
     * A field which can be empty or filled.
     * @remarks
     * Allows 0 or one child.
     */
    FieldKind[FieldKind["Optional"] = 0] = "Optional";
    /**
     * A field which must always be filled.
     * @remarks
     * Only allows exactly one child.
     */
    FieldKind[FieldKind["Required"] = 1] = "Required";
    // TODO:#7734,7736: Re-enable when persisted document format is optimized
    // /**
    //  * A special field used for node identifiers.
    //  * @remarks
    //  * Only allows exactly one child.
    //  */
    // Identifier,
})(FieldKind || (FieldKind = {}));
/**
 * Kind of tree node.
 * @public
 */
export var NodeKind;
(function (NodeKind) {
    /**
     * A node which serves as a map, storing children under string keys.
     */
    NodeKind[NodeKind["Map"] = 0] = "Map";
    /**
     * A node which serves as an array, storing children in an ordered sequence.
     */
    NodeKind[NodeKind["Array"] = 1] = "Array";
    /**
     * A node which stores a heterogenous collection of children in named fields.
     * @remarks
     * Each field gets its own schema.
     */
    NodeKind[NodeKind["Object"] = 2] = "Object";
    /**
     * A node which stores a single leaf value.
     */
    NodeKind[NodeKind["Leaf"] = 3] = "Leaf";
})(NodeKind || (NodeKind = {}));
/**
 * Maps from a view key to its corresponding {@link FieldProps.key | stored key} for the provided
 * {@link ImplicitFieldSchema | field schema}.
 *
 * @remarks
 * If an explicit stored key was specified in the schema, it will be used.
 * Otherwise, the stored key is the same as the view key.
 */
export function getStoredKey(viewKey, fieldSchema) {
    return brand(getExplicitStoredKey(fieldSchema) ?? viewKey);
}
/**
 * Gets the {@link FieldProps.key | stored key} specified by the schema, if one was explicitly specified.
 * Otherwise, returns undefined.
 */
export function getExplicitStoredKey(fieldSchema) {
    return fieldSchema instanceof FieldSchema ? fieldSchema.props?.key : undefined;
}
/**
 * All policy for a specific field,
 * including functionality that does not have to be kept consistent across versions or deterministic.
 *
 * This can include policy for how to use this schema for "view" purposes, and well as how to expose editing APIs.
 * @sealed @public
 */
export class FieldSchema {
    /**
     * What types of tree nodes are allowed in this field.
     * @remarks Counterpart to {@link FieldSchema.allowedTypes}, with any lazy definitions evaluated.
     */
    get allowedTypeSet() {
        return this.lazyTypes.value;
    }
    constructor(
    /**
     * The {@link https://en.wikipedia.org/wiki/Kind_(type_theory) | kind } of this field.
     * Determines the multiplicity, viewing and editing APIs as well as the merge resolution policy.
     */
    kind, 
    /**
     * What types of tree nodes are allowed in this field.
     */
    allowedTypes, 
    /**
     * Optional properties associated with the field.
     */
    props) {
        this.kind = kind;
        this.allowedTypes = allowedTypes;
        this.props = props;
        this.lazyTypes = new Lazy(() => normalizeAllowedTypes(this.allowedTypes));
    }
}
/**
 * Normalizes a {@link ImplicitFieldSchema} to a {@link FieldSchema}.
 */
export function normalizeFieldSchema(schema) {
    return schema instanceof FieldSchema ? schema : new FieldSchema(FieldKind.Required, schema);
}
/**
 * Normalizes a {@link ImplicitAllowedTypes} to a set of {@link TreeNodeSchema}s, by eagerly evaluating any
 * lazy schema declarations.
 *
 * @remarks Note: this must only be called after all required schemas have been declared, otherwise evaluation of
 * recursive schemas may fail.
 */
export function normalizeAllowedTypes(types) {
    const normalized = new Set();
    if (isReadonlyArray(types)) {
        for (const lazyType of types) {
            normalized.add(evaluateLazySchema(lazyType));
        }
    }
    else {
        normalized.add(evaluateLazySchema(types));
    }
    return normalized;
}
function evaluateLazySchema(value) {
    const evaluatedSchema = isLazy(value) ? value() : value;
    if (evaluatedSchema === undefined) {
        throw new UsageError(`Encountered an undefined schema. This could indicate that some referenced schema has not yet been instantiated.`);
    }
    return evaluatedSchema;
}
/**
 * The type of a {@link TreeNode}.
 * For moore information about the type, use `Tree.schema(theNode)` instead.
 * @remarks
 * This symbol mainly exists on nodes to allow TypeScript to provide more accurate type checking.
 * `Tree.is` and `Tree.schema` provide a superset of this information in more friendly ways.
 *
 * This symbol should not manually be added to objects as doing so allows the object to be invalidly used where nodes are expected.
 * Instead construct a real node of the desired type using its constructor.
 * @privateRemarks
 * This prevents non-nodes from being accidentally used as nodes, as well as allows the type checker to distinguish different node types.
 * @public
 */
export const type = Symbol("TreeNode Type");
//# sourceMappingURL=schemaTypes.js.map