/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { Adapters, FieldKey, TreeNodeStoredSchema, TreeFieldStoredSchema, TreeNodeSchemaIdentifier, TreeTypeSet, ValueSchema, TreeStoredSchema, StoredSchemaCollection } from "../../core/index.js";
import { MakeNominal, Named, Assume } from "../../util/index.js";
import { FieldKinds } from "../default-schema/index.js";
import { FlexFieldKind, FullSchemaPolicy } from "../modular-schema/index.js";
import { LazyItem } from "./flexList.js";
/**
 * @internal
 */
export interface FlexObjectNodeFields {
    readonly [key: string]: FlexFieldSchema;
}
/**
 * @internal
 */
export type NormalizeObjectNodeFields<T extends FlexObjectNodeFields> = {
    readonly [Property in keyof T]: NormalizeField<T[Property]>;
};
/**
 * A placeholder to use in {@link https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints | extends constraints} when using the real type breaks compilation of some recursive types due to {@link https://github.com/microsoft/TypeScript/issues/55758 | a design limitation of TypeScript}.
 *
 * These extends constraints only serve as documentation:
 * to avoid breaking compilation, this type has to not actually enforce anything, and thus is just `unknown`.
 * Therefore the type safety is the responsibility of the user of the API.
 * @beta
 */
export type Unenforced<_DesiredExtendsConstraint> = unknown;
/**
 * T must extend TreeSchemaSpecification.
 * This can not be enforced using TypeScript since doing so breaks recursive type support.
 * See note on SchemaBuilder.fieldRecursive.
 * @internal
 */
export declare abstract class TreeNodeSchemaBase<const out Name extends string = string, const out Specification = unknown> {
    readonly builder: Named<string>;
    readonly name: TreeNodeSchemaIdentifier<Name>;
    readonly info: Specification;
    readonly stored: TreeNodeStoredSchema;
    protected _typeCheck?: MakeNominal;
    protected constructor(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, info: Specification, stored: TreeNodeStoredSchema);
    abstract getFieldSchema(field: FieldKey): FlexFieldSchema;
}
/**
 * @internal
 */
export declare class FlexMapNodeSchema<const out Name extends string = string, const out Specification extends Unenforced<FlexMapFieldSchema> = FlexMapFieldSchema> extends TreeNodeSchemaBase<Name, Specification> {
    get mapFields(): FlexMapFieldSchema;
    protected _typeCheck2?: MakeNominal;
    static create<const Name extends string, const Specification extends FlexMapFieldSchema>(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, specification: Specification): FlexMapNodeSchema<Name, Specification>;
    getFieldSchema(field: FieldKey): FlexMapFieldSchema;
}
/**
 * @internal
 */
export declare class LeafNodeSchema<const out Name extends string = string, const out Specification extends Unenforced<ValueSchema> = ValueSchema> extends TreeNodeSchemaBase<Name, Specification> {
    get leafValue(): ValueSchema;
    protected _typeCheck2?: MakeNominal;
    static create<const Name extends string, const Specification extends ValueSchema>(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, specification: Specification): LeafNodeSchema<Name, Specification>;
    getFieldSchema(field: FieldKey): FlexFieldSchema;
}
/**
 * @internal
 */
export declare class FlexObjectNodeSchema<const out Name extends string = string, const out Specification extends Unenforced<FlexObjectNodeFields> = FlexObjectNodeFields> extends TreeNodeSchemaBase<Name, Specification> {
    readonly objectNodeFieldsObject: NormalizeObjectNodeFields<Assume<Specification, FlexObjectNodeFields>>;
    readonly objectNodeFields: ReadonlyMap<FieldKey, FlexFieldSchema>;
    protected _typeCheck2?: MakeNominal;
    static create<const Name extends string, const Specification extends FlexObjectNodeFields>(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, specification: Specification): FlexObjectNodeSchema<Name, Specification>;
    private constructor();
    getFieldSchema(field: FieldKey): FlexFieldSchema;
}
/**
 * TODO: remove or replace (or subclass) this with more specific types, like "List".
 * @internal
 */
export declare class FlexFieldNodeSchema<Name extends string = string, Specification extends Unenforced<FlexFieldSchema> = FlexFieldSchema> extends TreeNodeSchemaBase<Name, Specification> {
    protected _typeCheck2?: MakeNominal;
    static create<const Name extends string, const Specification extends FlexFieldSchema>(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, specification: Specification): FlexFieldNodeSchema<Name, Specification>;
    private constructor();
    getFieldSchema(field?: FieldKey): FlexFieldSchema;
}
/**
 * @internal
 * @privateRemarks
 * This could be an exhaustive union, or just the common base type.
 * Using just the base type prevents exhaustive matching, which has both pros and cons.
 *
 * For now this is using just the base type since the union is causing issues with schema aware typing, likely due to it being a union and thus distributing over extends clauses.
 */
export type FlexTreeNodeSchema = TreeNodeSchemaBase;
/**
 * Convert FieldSchemaSpecification | undefined into TreeFieldSchema.
 * @internal
 */
export type NormalizeField<T extends FlexFieldSchema | undefined> = T extends FlexFieldSchema ? T : FlexFieldSchema<typeof FieldKinds.forbidden, []>;
/**
 * Allow any node (as long as it meets the schema for its own type).
 * @internal
 */
export declare const Any: "Any";
/**
 * Allow any node (as long as it meets the schema for its own type).
 * @internal
 */
export type Any = typeof Any;
/**
 * Tree type, but can be wrapped in a function to allow referring to types before they are declared.
 * This makes recursive and co-recursive types possible.
 * @internal
 */
export type LazyTreeNodeSchema = FlexTreeNodeSchema | (() => FlexTreeNodeSchema);
/**
 * Types for use in fields.
 *
 * "Any" is boxed in an array to allow use as variadic parameter.
 * @internal
 */
export type FlexAllowedTypes = readonly [Any] | readonly LazyItem<FlexTreeNodeSchema>[];
/**
 * Checks if an {@link FlexAllowedTypes} is {@link (Any:type)}.
 * @internal
 */
export declare function allowedTypesIsAny(t: FlexAllowedTypes): t is readonly [Any];
/**
 * Subset of TreeFieldSchema thats legal in maps.
 * This requires empty to be a valid value for the map.
 * @internal
 */
export type FlexMapFieldSchema = FlexFieldSchema<typeof FieldKinds.optional | typeof FieldKinds.sequence>;
/**
 * 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.
 *
 * @remarks
 * `Types` here must extend `AllowedTypes`, but this cannot be enforced with an "extends" clause: see {@link Unenforced} for details.
 *
 * @typeParam TKind - The kind of field.
 * @typeParam TTypes - The types allowed by the field.
 *
 * @sealed
 * @internal
 */
export declare class FlexFieldSchema<out TKind extends FlexFieldKind = FlexFieldKind, const out TTypes extends Unenforced<FlexAllowedTypes> = FlexAllowedTypes> implements TreeFieldStoredSchema {
    readonly kind: TKind;
    readonly allowedTypes: TTypes;
    /**
     * Schema for a field which must always be empty.
     */
    static readonly empty: FlexFieldSchema<import("../default-schema/defaultFieldKinds.js").Forbidden, readonly []>;
    /**
     * Constructs a TreeFieldSchema.
     * @privateRemarks
     * Alias for the constructor, but with extends clause for the `Types` parameter that {@link FlexFieldSchema} can not have (due to recursive type issues).
     */
    static create<TKind extends FlexFieldKind, const Types extends FlexAllowedTypes>(kind: TKind, allowedTypes: Types): FlexFieldSchema<TKind, Types>;
    /**
     * Constructs a TreeFieldSchema, but missing the extends clause which breaks most recursive types.
     * @remarks
     * `Types` here must extend `AllowedTypes`, but this cannot be enforced with an "extends" clause: see {@link Unenforced} for details.
     * Prefer {@link FlexFieldSchema.create} when possible.
     */
    static createUnsafe<TKind extends FlexFieldKind, const Types extends Unenforced<FlexAllowedTypes>>(kind: TKind, allowedTypes: Types): FlexFieldSchema<TKind, Types>;
    protected _typeCheck?: MakeNominal;
    /**
     * This is computed lazily since types can be recursive, which makes evaluating this have to happen after all the schema are defined.
     */
    private readonly lazyTypes;
    /**
     * @param kind - The {@link https://en.wikipedia.org/wiki/Kind_(type_theory) | kind} of this field.
     * Determine the multiplicity, viewing and editing APIs as well as the merge resolution policy.
     * @param allowedTypes - What types of tree nodes are allowed in this field.
     */
    private constructor();
    /**
     * Types which are allowed in this field (by {@link TreeNodeSchemaIdentifier}), in a format optimized for stored schema.
     * This is the same set of types in {@link FlexFieldSchema.allowedTypes}, just in a different format.
     */
    get types(): TreeTypeSet;
    /**
     * Types which are allowed in this field.
     * This is the same set of types in {@link FlexFieldSchema.allowedTypes}, just as a set with laziness removed.
     * @privateRemarks
     * TODO:
     * 3 ways to access the allowed types are now exposed.
     * Reducing this and/or renaming the more friendly options to take the shorter name (`types`)
     * would be a good idea.
     */
    get allowedTypeSet(): AllowedTypeSet;
    /**
     * If exactly one type of child is allowed in this field, it is provided here.
     * @remarks
     * Some code paths (like unboxing and compressed tree encoding) special case schema with exactly one allowed type.
     * This field allows for simple and optimized handling of this case.
     */
    get monomorphicChildType(): FlexTreeNodeSchema | undefined;
    /**
     * Compare this schema to another.
     *
     * @returns true iff the schema are identical.
     */
    equals(other: FlexFieldSchema): boolean;
}
/**
 * Types for use in fields.
 * This representation is optimized for runtime use in view-schema.
 *
 * @remarks
 * See {@link TreeTypeSet} for a stored-schema compatible version using the {@link TreeNodeSchemaIdentifier}.
 * See {@link FlexAllowedTypes} for a compile time optimized version.
 * @internal
 */
export type AllowedTypeSet = Any | ReadonlySet<FlexTreeNodeSchema>;
/**
 * Convert {@link FlexAllowedTypes} to {@link TreeTypeSet}.
 * @internal
 */
export declare function allowedTypesSchemaSet(t: FlexAllowedTypes): AllowedTypeSet;
/**
 * Convert {@link FlexAllowedTypes} to {@link TreeTypeSet}.
 * @internal
 */
export declare function allowedTypesToTypeSet(t: FlexAllowedTypes): TreeTypeSet;
/**
 * Schema data that can be be used to view a document.
 * Strongly typed over its rootFieldSchema.
 *
 * @remarks
 * The type of the rootFieldSchema is used to implement SchemaAware APIs.
 * Cases that do not require being compile time schema aware can omit the explicit type for it.
 *
 * @internal
 */
export interface FlexTreeSchema<out T extends FlexFieldSchema = FlexFieldSchema> extends SchemaCollection {
    /**
     * Schema for the root field which contains the whole tree.
     */
    readonly rootFieldSchema: T;
    /**
     * Extra configuration for how this schema is handled at runtime.
     */
    readonly policy: FullSchemaPolicy;
    /**
     * Compatibility information how how to interact with content who's stored schema is not directly compatible with this schema.
     */
    readonly adapters: Adapters;
}
/**
 * Converts a {@link FlexTreeSchema} into a {@link TreeStoredSchema}.
 */
export declare function intoStoredSchema(treeSchema: FlexTreeSchema): TreeStoredSchema;
/**
 * Converts a {@link SchemaCollection} into a {@link StoredSchemaCollection}.
 */
export declare function intoStoredSchemaCollection(treeSchema: SchemaCollection): StoredSchemaCollection;
/**
 * Schema data that can be be used to view a document.
 * @internal
 *
 * @privateRemarks
 * It is convenient that this can be used as a StoredSchemaCollection with no conversion.
 * There there isn't a design requirement for this however, so this extends clause can be removed later if needed.
 */
export interface SchemaCollection {
    /**
     * {@inheritdoc SchemaCollection}
     */
    readonly nodeSchema: ReadonlyMap<TreeNodeSchemaIdentifier, FlexTreeNodeSchema>;
}
/**
 * Checks if a {@link FlexTreeNodeSchema} is a {@link FlexMapNodeSchema}.
 * @internal
 */
export declare function schemaIsMap(schema: FlexTreeNodeSchema): schema is FlexMapNodeSchema;
/**
 * Checks if a {@link FlexTreeNodeSchema} is a {@link LeafNodeSchema}.
 * @internal
 */
export declare function schemaIsLeaf(schema: FlexTreeNodeSchema): schema is LeafNodeSchema;
/**
 * Checks if a {@link FlexTreeNodeSchema} is a {@link FlexFieldNodeSchema}.
 * @internal
 */
export declare function schemaIsFieldNode(schema: FlexTreeNodeSchema): schema is FlexFieldNodeSchema;
/**
 * Checks if a {@link FlexTreeNodeSchema} is a {@link FlexObjectNodeSchema}.
 * @internal
 */
export declare function schemaIsObjectNode(schema: FlexTreeNodeSchema): schema is FlexObjectNodeSchema;
//# sourceMappingURL=typedTreeSchema.d.ts.map