/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { FieldKindIdentifier, Delta, FieldKey, Value, TaggedChange, RevisionTag } from "../../core";
import { Brand, Invariant } from "../../util";
import { ICodecFamily, IJsonCodec } from "../../codec";
import { ChangesetLocalId, CrossFieldManager } from "./crossFieldQueries";
/**
 * Functionality provided by a field kind which will be composed with other `FieldChangeHandler`s to
 * implement a unified ChangeFamily supporting documents with multiple field kinds.
 * @alpha
 */
export interface FieldChangeHandler<TChangeset, TEditor extends FieldEditor<TChangeset> = FieldEditor<TChangeset>> {
    _typeCheck?: Invariant<TChangeset>;
    rebaser: FieldChangeRebaser<TChangeset>;
    codecsFactory: (childCodec: IJsonCodec<NodeChangeset>) => ICodecFamily<TChangeset>;
    editor: TEditor;
    intoDelta(change: TChangeset, deltaFromChild: ToDelta): Delta.MarkList;
    /**
     * Returns whether this change is empty, meaning that it represents no modifications to the field
     * and could be removed from the ModularChangeset tree without changing its behavior.
     */
    isEmpty(change: TChangeset): boolean;
}
/**
 * @alpha
 */
export interface FieldChangeRebaser<TChangeset> {
    /**
     * Compose a collection of changesets into a single one.
     * Every child included in the composed change must be the result of a call to `composeChild`,
     * and should be tagged with the revision of its parent change.
     * Children which were the result of an earlier call to `composeChild` should be tagged with
     * undefined revision if later passed as an argument to `composeChild`.
     * See {@link ChangeRebaser} for more details.
     */
    compose(changes: TaggedChange<TChangeset>[], composeChild: NodeChangeComposer, genId: IdAllocator, crossFieldManager: CrossFieldManager, revisionMetadata: RevisionMetadataSource): TChangeset;
    /**
     * Amend `composedChange` with respect to new data in `crossFieldManager`.
     */
    amendCompose(composedChange: TChangeset, composeChild: NodeChangeComposer, genId: IdAllocator, crossFieldManager: CrossFieldManager, revisionMetadata: RevisionMetadataSource): TChangeset;
    /**
     * @returns the inverse of `changes`.
     * See {@link ChangeRebaser} for details.
     */
    invert(change: TaggedChange<TChangeset>, invertChild: NodeChangeInverter, reviver: NodeReviver, genId: IdAllocator, crossFieldManager: CrossFieldManager): TChangeset;
    /**
     * Amend `invertedChange` with respect to new data in `crossFieldManager`.
     */
    amendInvert(invertedChange: TChangeset, originalRevision: RevisionTag | undefined, reviver: NodeReviver, genId: IdAllocator, crossFieldManager: CrossFieldManager): TChangeset;
    /**
     * Rebase `change` over `over`.
     * See {@link ChangeRebaser} for details.
     */
    rebase(change: TChangeset, over: TaggedChange<TChangeset>, rebaseChild: NodeChangeRebaser, genId: IdAllocator, crossFieldManager: CrossFieldManager, revisionMetadata: RevisionMetadataSource): TChangeset;
    /**
     * Amend `rebasedChange` with respect to new data in `crossFieldManager`.
     */
    amendRebase(rebasedChange: TChangeset, over: TaggedChange<TChangeset>, rebaseChild: NodeChangeRebaser, genId: IdAllocator, crossFieldManager: CrossFieldManager, revisionMetadata: RevisionMetadataSource): TChangeset;
}
/**
 * Helper for creating a {@link FieldChangeRebaser} which does not need access to revision tags.
 * This should only be used for fields where the child nodes cannot be edited.
 */
export declare function referenceFreeFieldChangeRebaser<TChangeset>(data: {
    compose: (changes: TChangeset[]) => TChangeset;
    invert: (change: TChangeset, reviver: NodeReviver) => TChangeset;
    rebase: (change: TChangeset, over: TChangeset) => TChangeset;
}): FieldChangeRebaser<TChangeset>;
export declare function isolatedFieldChangeRebaser<TChangeset>(data: {
    compose: FieldChangeRebaser<TChangeset>["compose"];
    invert: FieldChangeRebaser<TChangeset>["invert"];
    rebase: FieldChangeRebaser<TChangeset>["rebase"];
}): FieldChangeRebaser<TChangeset>;
/**
 * @alpha
 */
export interface FieldEditor<TChangeset> {
    /**
     * Creates a changeset which represents the given `change` to the child at `childIndex` of this editor's field.
     */
    buildChildChange(childIndex: number, change: NodeChangeset): TChangeset;
}
/**
 * The `index` represents the index of the child node in the input context.
 * The `index` should be `undefined` iff the child node does not exist in the input context (e.g., an inserted node).
 * @alpha
 */
export declare type ToDelta = (child: NodeChangeset) => Delta.Modify;
/**
 * @alpha
 */
export declare type NodeReviver = (revision: RevisionTag, index: number, count: number) => Delta.ProtoNode[];
/**
 * @alpha
 */
export declare type NodeChangeInverter = (change: NodeChangeset, index: number | undefined) => NodeChangeset;
/**
 * @alpha
 */
export declare type NodeChangeRebaser = (change: NodeChangeset | undefined, baseChange: NodeChangeset | undefined) => NodeChangeset | undefined;
/**
 * @alpha
 */
export declare type NodeChangeComposer = (changes: TaggedChange<NodeChangeset>[]) => NodeChangeset;
/**
 * Allocates a block of `count` consecutive IDs and returns the first ID in the block.
 * For convenience can be called with no parameters to allocate a single ID.
 * @alpha
 */
export declare type IdAllocator = (count?: number) => ChangesetLocalId;
/**
 * Changeset for a subtree rooted at a specific node.
 * @alpha
 */
export interface NodeChangeset extends HasFieldChanges {
    valueChange?: ValueChange;
    valueConstraint?: ValueConstraint;
}
/**
 * @alpha
 */
export interface ValueConstraint {
    value: Value;
    violated: boolean;
}
/**
 * @alpha
 */
export interface HasFieldChanges {
    fieldChanges?: FieldChangeMap;
}
/**
 * @alpha
 */
export interface ValueChange {
    /**
     * The revision in which this change occurred.
     * Undefined when it can be inferred from context.
     */
    revision?: RevisionTag;
    /**
     * Can be left unset to represent the value being cleared.
     */
    value?: Value;
}
/**
 * @alpha
 */
export interface ModularChangeset extends HasFieldChanges {
    /**
     * The numerically highest `ChangesetLocalId` used in this changeset.
     * If undefined then this changeset contains no IDs.
     */
    maxId?: ChangesetLocalId;
    /**
     * The revisions included in this changeset, ordered temporally (oldest to newest).
     * Undefined for anonymous changesets.
     * Should never be empty.
     */
    readonly revisions?: readonly RevisionInfo[];
    fieldChanges: FieldChangeMap;
    constraintViolationCount?: number;
}
/**
 * A callback that returns the index of the changeset associated with the given RevisionTag among the changesets being
 * composed or rebased. This index is solely meant to communicate relative ordering, and is only valid within the scope of the
 * compose or rebase operation.
 *
 * During composition, the index reflects the order of the changeset within the overall composed changeset that is
 * being produced.
 *
 * During rebase, the indices of the base changes are all lower than the indices of the change being rebased.
 * @alpha
 */
export declare type RevisionIndexer = (tag: RevisionTag) => number;
/**
 * @alpha
 */
export interface RevisionMetadataSource {
    readonly getIndex: RevisionIndexer;
    readonly getInfo: (tag: RevisionTag) => RevisionInfo;
}
/**
 * @alpha
 */
export interface RevisionInfo {
    readonly revision: RevisionTag;
    /**
     * When populated, indicates that the changeset is a rollback for the purpose of a rebase sandwich.
     * The value corresponds to the `revision` of the original changeset being rolled back.
     */
    readonly rollbackOf?: RevisionTag;
}
/**
 * @alpha
 */
export declare type FieldChangeMap = Map<FieldKey, FieldChange>;
/**
 * @alpha
 */
export interface FieldChange {
    fieldKind: FieldKindIdentifier;
    /**
     * If defined, `change` is part of the specified revision.
     * Undefined in the following cases:
     * A) A revision is specified on an ancestor of this `FieldChange`, in which case `change` is part of that revision.
     * B) `change` is composed of multiple revisions.
     * C) `change` is part of an anonymous revision.
     */
    revision?: RevisionTag;
    change: FieldChangeset;
}
/**
 * @alpha
 */
export declare type FieldChangeset = Brand<unknown, "FieldChangeset">;
//# sourceMappingURL=fieldChangeHandler.d.ts.map