/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { forbiddenFieldKindIdentifier, } from "../../core";
import { fail } from "../../util";
import { Multiplicity, allowsTreeSchemaIdentifierSuperset, referenceFreeFieldChangeRebaser, brandedFieldKind, } from "../modular-schema";
import { sequenceFieldChangeHandler } from "../sequence-field";
import { noChangeCodecFamily } from "./defaultFieldChangeCodecs";
import { optionalChangeHandler, optionalFieldEditor } from "./optionalField";
/**
 * ChangeHandler that only handles no-op / identity changes.
 */
export const noChangeHandler = {
    rebaser: referenceFreeFieldChangeRebaser({
        compose: (changes) => 0,
        invert: (changes) => 0,
        rebase: (change, over) => 0,
    }),
    codecsFactory: () => noChangeCodecFamily,
    editor: { buildChildChange: (index, change) => fail("Child changes not supported") },
    intoDelta: (change, deltaFromChild) => [],
    isEmpty: (change) => true,
};
/**
 * 0 or 1 items.
 */
export const optional = brandedFieldKind("Optional", Multiplicity.Optional, optionalChangeHandler, (types, other) => (other.kind.identifier === sequence.identifier ||
    other.kind.identifier === optional.identifier) &&
    allowsTreeSchemaIdentifierSuperset(types, other.types), new Set([]));
export const valueFieldEditor = {
    ...optionalFieldEditor,
    set: (newContent, id) => optionalFieldEditor.set(newContent, false, id),
};
export const valueChangeHandler = {
    ...optional.changeHandler,
    editor: valueFieldEditor,
};
/**
 * Exactly one item.
 */
export const value = brandedFieldKind("Value", Multiplicity.Value, valueChangeHandler, (types, other) => (other.kind.identifier === sequence.identifier ||
    other.kind.identifier === value.identifier ||
    other.kind.identifier === optional.identifier ||
    other.kind.identifier === nodeKey.identifier) &&
    allowsTreeSchemaIdentifierSuperset(types, other.types), new Set());
/**
 * 0 or more items.
 */
export const sequence = brandedFieldKind("Sequence", Multiplicity.Sequence, sequenceFieldChangeHandler, (types, other) => other.kind.identifier === sequence.identifier &&
    allowsTreeSchemaIdentifierSuperset(types, other.types), 
// TODO: add normalizer/importers for handling ops from other kinds.
new Set([]));
/**
 * Exactly one identifier.
 */
export const nodeKey = brandedFieldKind("NodeKey", Multiplicity.Value, noChangeHandler, (types, other) => (other.kind.identifier === sequence.identifier ||
    other.kind.identifier === value.identifier ||
    other.kind.identifier === optional.identifier ||
    other.kind.identifier === nodeKey.identifier) &&
    allowsTreeSchemaIdentifierSuperset(types, other.types), new Set());
/**
 * Exactly 0 items.
 *
 * Using Forbidden makes what types are listed for allowed in a field irrelevant
 * since the field will never have values in it.
 *
 * Using Forbidden is equivalent to picking a kind that permits empty (like sequence or optional)
 * and having no allowed types (or only never types).
 * Because of this, its possible to express everything constraint wise without Forbidden,
 * but using Forbidden can be more semantically clear than optional with no allowed types.
 *
 * For view schema, this can be useful if you need to:
 * - run a specific out of schema handler when a field is present,
 * but otherwise are ignoring or tolerating (ex: via extra fields) unmentioned fields.
 * - prevent a specific field from being used as an extra field
 * (perhaps for some past of future compatibility reason)
 * - keep a field in a schema for metadata purposes
 * (ex: for improved error messaging, error handling or documentation)
 * that is not used in this specific version of the schema (ex: to document what it was or will be used for).
 *
 * For stored schema, this can be useful if you need to:
 * - have a field which can have its schema updated to Optional or Sequence of any type.
 * - to exclude a field from extra fields
 * - for the schema system to use as a default for fields which aren't declared
 * (ex: when updating a field that did not exist into one that does)
 *
 * See {@link emptyField} for a constant, reusable field using Forbidden.
 */
export const forbidden = brandedFieldKind(forbiddenFieldKindIdentifier, Multiplicity.Forbidden, noChangeHandler, 
// All multiplicities other than Value support empty.
(types, other) => fieldKinds.get(other.kind.identifier)?.multiplicity !== Multiplicity.Value, new Set());
/**
 * Default field kinds by identifier
 */
export const fieldKinds = new Map([value, optional, sequence, nodeKey, forbidden].map((s) => [s.identifier, s]));
/**
 * Default FieldKinds with their editor types erased.
 * @public
 */
export const FieldKinds = { value, optional, sequence, nodeKey, forbidden };
//# sourceMappingURL=defaultFieldKinds.js.map