import { Operation } from 'fast-json-patch';
import { Command, CompoundCommandImpl, CoreCommandStack, CoreCommandStackSubscription, DirtyStateChangedCallback, EditingContext, EditingContextChangedCallback, MaybePromise, RedoAnalysis, UndoAnalysis, UndoRedoOp } from '../core';
/** Any operation that can be performed on a command. */
export type CommandOp = 'execute' | UndoRedoOp;
export interface WorkingCopyManager<K = string> {
    /**
     * Prepare a new edit session in which working copies will be required.
     */
    open(modelId: K[]): void;
    /**
     * Whether the working copy manager has an open session.
     */
    isOpen(modelId: K[]): boolean;
    /**
     * Get a working copy of the model identified by the given ID, if it
     * exists. Until changes are committed or the working copies are
     * reset by a call to {@link open}, the same working copy must be
     * supplied for all requests for the same ID.
     */
    getWorkingCopy(modelId: K): object | undefined;
    /**
     * Get the current public state of the model identified by the given ID, if it
     * exists. **Note** that this **is not a working copy**.
     */
    getModel(modelId: K): object | undefined;
    /**
     * Optional method to follow up changes
     * performed on models with further changes to be executed in
     * isolation, not on the command history.
     * The purpose of such changes must only be to ensure integrity of
     * dependencies between properties of the models changed in this
     * `commandResult`.
     * The hook, if defined, is called following every execution, undo,
     * and redo of any command, and so must be prepared to logically
     * invert changes that it had provided previously to whatever extent
     * makes sense for its models. It is for this reason that the changes
     * provided by this hook are not recorded in the history, because
     * the hook is invoked also on undo/redo, not just on the original
     * execution of every command.
     */
    createFollowUpCommand?(commandResult: Map<Command<K>, Operation[]>): MaybePromise<Command<K> | undefined>;
    /**
     * Commit the current working copy state back to the model storage,
     * with a summary of the changes that are being committed.
     *
     * @param result a map of commands to the operations that were executed
     * @param modelIds the model IDs that were opened
     */
    commit(result: Map<Command<K>, Operation[]>, modelIds: K[]): void;
    /**
     * Discard the current working copy state and close.
     */
    cancel(modelIds: K[]): void;
}
export declare class CoreCommandStackImpl<K = string> implements CoreCommandStack<K> {
    private workingCopyManager;
    /** The most recent of all commands executed, in temporal order, in all editing contexts. */
    private _top?;
    /**
     * A pointer for each editing context to the top of its undo stack, keyed by editing context ID.
     * Use key type `string`, not `EditingContext`, to be explicit in case of any future evolution
     * of the editing context as a complex type with a string identifier.
     */
    private _undoEntries;
    /**
     * A pointer for each editing context to the top of its redo stack, keyed by editing context ID.
     * @see {@link _undoEntries}
     */
    private _redoEntries;
    /**
     * A pointer to the stack entry that is the last command executed or redone in each context
     * at the time that context was last marked saved.
     * If a context does not have an entry then either it was never saved or it was saved when
     * no commands have been executed/redone, which makes no difference.
     * If the mapping is the flush token, then the context was dirty at the time it was flushed.
     */
    private _savepoints;
    /**
     * Subscriptions by editing context, with the `null` key track subscriptions to all contexts.
     */
    private _subscriptions;
    /**
     * Exclusively execute all public operations on the stack.
     */
    private _exclusive;
    /**
     * Operations to run after committing a working copy transaction.
     */
    private postCommitOperations;
    /**
     * Constructor of the core command stack.
     *
     * @param notify a call-back that receives the notification of changes
     *   applied to one or more models, to broadcast to subscriptions or
     *   to process otherwise
     * @param createFollowUpCommand  Optional hook to follow up changes
     *   performed on models with further changes to be executed in
     *   isolation, not on the command history.
     *   The purpose of such changes must only be to ensure integrity of
     *   dependencies between properties of the models changed in this
     *   `commandResult`.
     *   The hook, if defined, is called following every execution, undo,
     *   and redo of any command, and so must be prepared to logically
     *   invert changes that it had provided previously to whatever extent
     *   makes sense for its models. It is for this reason that the changes
     *   provided by this hook are not recorded in the history, because
     *   the hook is invoked also on undo/redo, not just on the original
     *   execution of every command.
     */
    constructor(workingCopyManager: WorkingCopyManager<K>);
    execute(command: Command<K>, ...contexts: EditingContext[]): Promise<Map<Command<K>, Operation[]> | undefined>;
    /**
     * Perform an `operation` on a `command`.
     *
     * @param operation whether to execute, undo, or redo the `command`
     * @param command the command to execute, undo, or redo
     * @return a description of the model changes resulting from the execution, undo, or redo of the `command`
     */
    private perform;
    executeAndAppend(appendedContext: EditingContext, command: Command<K>, ...contexts: EditingContext[]): Promise<Map<Command<K>, Operation[]> | undefined>;
    undo(context: EditingContext, withDependencies?: boolean): Promise<Map<Command<K>, Operation[]> | undefined>;
    /**
     * Revert the command on the top of the undo or redo stack of an editing `context`
     * according to the indicated `op`eration.
     *
     * @param context the editing context to undo or redo
     * @param op whether to revert the top of the undo or the redo stack of the `context`
     * @param withDependencies whether first to revert dependencies of the `context` as necessary
     *
     * @see {@link undo}
     * @see {@link redo}
     */
    private performUndoRedo;
    /**
     * Merge two stack entries and remove the one that was merged into the other from
     * the history as now it is included in the other.
     *
     * @param into the stack entry into which to merge the other
     * @param from the other entry to merge `into` the first
     * @param forOp whether the merge is for undo or redo, which determines the order
     *   in which the commands of the `from` entry are merged `into` the other's commands
     * @returns the result of the merge, which is just the `into` entry that now has been increased
     *
     * @see {@link StackEntry.merge}
     */
    private merge;
    redo(context: EditingContext, withDependencies?: boolean): Promise<Map<Command<K>, Operation[]> | undefined>;
    /**
     * Perform an `operation` on command(s) in my history that themselves modify working
     * copies of models supplied by the given working copy manager.
     * On successful conclusion of the `operation` the working copies that it used are committed
     * to the model manager; on failure they are just abandoned to retain the prior state of
     * all managed models.
     * In the event that the working copies are committed, all post-commit operations gathered
     * during the `operation` are executed; otherwise they too are abandoned.
     *
     * @param workingCopyManager the model manager's working-copy manager
     * @param operation the model operation to perform on working copies
     */
    protected withWorkingCopies<K>(workingCopyManager: WorkingCopyManager<K>, operation: () => Promise<Map<Command<K>, Operation[]> | undefined>, modelIds: K[]): Promise<Map<Command<K>, Operation[]> | undefined>;
    /**
     * Gather an operation to run when model working copies are committed
     * to the model manager.
     *
     * @param closer an operation to run on working copy commit
     */
    protected afterCommit(closer: () => unknown): void;
    /**
     * Perform an `operation` on command(s) in my history with tracking and subsequent notification
     * of changes in the dirty state of models. Upon successful completion of the `operation` and
     * committing of its working copies to the model manager, dirty state change subscriptions
     * will be notified.
     *
     * @param contexts the editing contexts in which the `operation` is performed
     * @param operation the model operation to perform that will potentially change dirty states
     */
    protected withDirtyNotification<T>(contexts: EditingContext[], operation: () => Promise<T>): Promise<T>;
    /**
     * Perform all post-change processing steps, including at least
     *
     * 1. Run the follow-up hook, if defined
     * 2. Run the notify hook
     *
     * if and only if the `commandResult` is defined.
     *
     * @param commandResult the result of a command execution, undo, or redo
     * @param the processed command result
     */
    private postChange;
    /**
     * Call my follow-up hook, if defined, and return the results of executing the
     * command that it provides.
     *
     * @param commandResult the command result to inject into the follow-up hook
     * @returns the results from the follow-up hook's provided command, if any
     */
    private processFollowUp;
    canExecute(command: Command<K>, ...contexts: string[]): Promise<boolean>;
    canUndo(context: EditingContext, withDependencies?: boolean): Promise<boolean>;
    canRedo(context: EditingContext, withDependencies?: boolean): Promise<boolean>;
    getUndoCommand(context: EditingContext): Command<K> | undefined;
    getRedoCommand(context: EditingContext): Command<K> | undefined;
    flush(context: EditingContext): Command<K>[];
    /**
     * Add the stack entry for a command that has been executed in one or more contexts.
     *
     * @param entry the stack entry to add
     */
    private executed;
    /**
     * Merge a command that has been executed into the stack-entry for the command that it appended.
     *
     * @param appendedEntry the entry for the command that was appended to
     * @param newEntry an entry for the command that was appended
     */
    private appended;
    /**
     * Flush all redo stacks for the editing contexts of an `entry`.
     * This is used after executing a command whose `entry` this is.
     *
     * @param entry an entry that has just been executed
     */
    private flushRedo;
    /**
     * Flush the redo stack starting from a given entry all the way down to the bottom.
     * All editing contexts of the starting entry are flushed from that point.
     * Thus some contexts may still have some older redo entries remaining.
     *
     * @param startingAt the top of the redo stack to flush
     */
    private flushRedoFrom;
    /**
     * Push a new entry onto the stack for a command that was executed.
     *
     * @param entry an entry to push onto the stack
     * @returns the `entry` that was pushed
     */
    private push;
    /**
     * Remove an entry from wherever in the stack it occurs. So, no strictly speaking
     * a "pop" which is a LIFO operation, but it's the converse of our {@link push}.
     *
     * @param entry an entry to remove from the stack
     * @returns the stack entry that remains at the point where the `entry` was removed,
     *  if the `entry` was not the very first in the temporal order
     */
    private pop;
    /**
     * Update the undo pointers for all editing contexts of an `entry` whose
     * command has been undone.
     *
     * @param entry the entry for a command that was undone
     */
    private undone;
    /**
     * Update the redo pointers for all editing contexts of an `entry` whose
     * command has been redone.
     *
     * @param entry the entry for a command that was redone
     */
    private redone;
    /**
     * Get the entry for the command that is at the top of an editing context's
     * undo stack or redo stack, that being the next command to undo or redo in
     * the context.
     *
     * @param editingContext the editing context
     * @param op whether to retrieve the undo or the redo entry
     * @returns the entry for the command at the top of the context's undo or redo stack
     */
    private getEntry;
    /**
     * Assert that a command is executable.
     * Precondition for executing it on the stack.
     *
     * @param command a command to be executed
     * @param contexts the editing contexts in which it is to be executed
     * @throws if the `command` cannot be executed
     */
    private checkExecute;
    /**
     * Test whether an operation on some `command` would be permitted in its current state.
     *
     * @param condition whether the `canExecute`, `canUndo`, or `canRedo` condition of the command is to be tested
     * @param command the command to be tested
     * @return the result of the test of the `command`'s current state under the `condition`
     */
    private test;
    /**
     * Assert that a command is revertible for undo or redo.
     * Precondition for undoing or redoing it on the stack.
     *
     * @param entry the entry for a command to be reverted
     * @param op the undo/redo operation to be validated
     * @param withDependencies whether to allow dependencies in the undo
     * @throws if the command of the `entry` cannot be reverted
     * @see {@link canUndoRedo}
     */
    private checkUndoRedo;
    /**
     * Query whether a command is revertible for undo or redo.
     *
     * A command may intrinsically be non-revertible or it may not be revertible because
     * one of its contexts has a command that would need to be reverted before it.
     *
     * @param entry the entry for a command to be revered
     * @param op whether the reversion is an undo or a redo
     * @param withDependencies whether to allow dependencies in the undo. Default `false`
     * @returns whether the command of the `entry` can be reverted
     */
    private canUndoRedo;
    analyzeUndo(context: string): Promise<UndoAnalysis>;
    /**
     * Compute the detailed analysis of the undoability or redoability of an editing `context`.
     *
     * @param context the editing context to analyze
     * @param op whether to analyze the feasibility of `undo` or `redo` of the `context`
     * @return the detailed analysis report
     */
    private analyzeUndoRedo;
    /**
     * Get the stack entries encoding dependencies, if any, of the given editing `context`
     * for undo or redo.
     *
     * @param context an editing context for which to get dependencies
     * @param op whether the analysis is for `undo` or for `redo`
     * @return the stack entries, or an empty array if none, that must be undone or
     *   redone in other contexts before the given `context` can be undone or redone
     */
    private getDependencyEntries;
    analyzeRedo(context: string): Promise<RedoAnalysis>;
    markSaved(editingContext: EditingContext): void;
    isDirty(editingContext: string): boolean;
    getDirtyModelIds(editingContext: EditingContext): K[];
    getEditingContexts(): EditingContext[];
    subscribe(editingContext?: string | undefined): CoreCommandStackSubscription<K>;
    /**
     * Invoke the `onContextChanged` call-backs of subscriptions that have it.
     *
     * @param args the call-back arguments to pass along
     */
    protected notifyCommandStackChanged(...args: Parameters<EditingContextChangedCallback<K>>): void;
    /**
     * Get all subscriptions pertaining to the given editing context.
     *
     * @param editingContext an editing context
     * @returns the subscriptions targeting the context specifically and all contexts generally
     */
    private getSubscriptions;
    /**
     * Invoke the `onDirtyStateChanged` call-backs of subscriptions that have it.
     *
     * @param args the call-back arguments to pass along
     */
    protected notifyDirtyStateChanged(...args: Parameters<DirtyStateChangedCallback<K>>): void;
}
/**
 * The command stack is a doubly-linked list, in temporal order of their original execution, of
 * commands and their associated editing contexts.
 * This class implements a node in that list.
 */
export declare class StackEntry<K = string> {
    /** The editing contexts in which a command was executed. */
    private readonly _editingContexts;
    /** The command that was executed. */
    private _command;
    /** The next command in the list (stack). */
    private _next?;
    /** The previous command in the list (stack). */
    private _previous?;
    constructor(command: Command<K>, editingContexts: EditingContext[]);
    /** Get the command that was executed. */
    get command(): Command<K>;
    /** Get the editing contexts in which the command was executed. */
    get editingContexts(): Set<EditingContext>;
    /**
     * Query whether the entry may be purged because all of its
     * editing contexts have been flushed.
     */
    get isPurgeable(): boolean;
    /**
     * Obtain the next command in the stack in temporal order,
     * regardless of editing contexts.
     */
    get next(): StackEntry<K> | undefined;
    /**
     * Obtain the next command in the stack that has the given
     * editing context.
     *
     * @param editingContext an editing context
     * @returns the next command, if any, in that context
     */
    nextIn(editingContext: EditingContext): StackEntry<K> | undefined;
    /**
     * Does this entry precede an`other` in the history?
     * An entry does not precede itself.
     *
     * @param other another stack entry
     * @returns whether the `other` is in my {@link next next chain}
     *
     * @see {@link next}
     */
    precedes(other: StackEntry<K>): boolean;
    /**
     * Obtain the previous command in the stack in temporal order,
     * regardless of editing contexts.
     */
    get previous(): StackEntry<K> | undefined;
    /**
     * Obtain the previous command in the stack that has the given
     * editing context.
     *
     * @param editingContext an editing context
     * @returns the previous command, if any, in that context
     */
    previousIn(editingContext: EditingContext): StackEntry<K> | undefined;
    /**
     * Does this entry succeed an`other` in the history?
     * An entry does not succeed itself.
     *
     * @param other another stack entry
     * @returns whether the `other` is in my {@link previous previous chain}
     *
     * @see {@link previous}
     */
    succeeds(other: StackEntry<K>): boolean;
    /**
     * Remove editing contexts (that were flushed) from a command.
     *
     * @param editingContexts the editing contexts to remove from the command
     */
    removeContext(...editingContexts: EditingContext[]): void;
    /**
     * Query whether my command is associated with the given editing context.
     *
     * @param editingContext an editing context
     * @returns whether the command has that context
     */
    hasContext(editingContext: EditingContext): boolean;
    /**
     * Insert a new entry into the stack following me, making it my new next.
     * My former next becomes its next and I become its previous.
     *
     * @param next my new next entry
     * @returns the `next` entry that was pushed
     * @throws on attempt to push the entry, itself, to make a cycle
     */
    push(next: StackEntry<K>): StackEntry<K>;
    /**
     * Remove me from the stack.
     *
     * @returns my former previous entry
     */
    pop(): StackEntry<K> | undefined;
    /**
     * Merge an `entry` into me, appending its command to mine
     * and its editing contexts to mine.
     *
     * @param entry an entry to merge into me
     * @param forOp the operation for which the compound is being prepared
     * @returns me
     */
    merge(entry: StackEntry<K>, forOp?: UndoRedoOp): this;
}
/**
 * A specialized compound command that allows appending an already-executed command
 * if the compound has already been executed but not undone.
 */
export declare class AppendableCompoundCommand<K = string> extends CompoundCommandImpl<K> {
    /** Construct an appendable compound that is initially in the _executed_ state. */
    constructor(label: string, ...commands: Command<K>[]);
    /** Construct an appendable compound that is initially in the _executed_ state. */
    constructor(label: string, initialState: CompoundCommandImpl['state'], ...commands: Command<K>[]);
    append(...commands: Command<K>[]): this;
}
/**
 * Compose a `base` command with additional `commands` for either undo.
 * **Note** that compounding commands for their initial execution does not need
 * this specialized mechanism.
 *
 * @param base a command to append to
 * @param commands commands to append to it
 * @returns some command that includes the `base` and all of the additional `commands`
 */
export declare const append: <K = string>(base: Command<K>, ...commands: Command<K>[]) => Command<K>;
export declare function getModelIds<K>(command: Command<K>): K[];
//# sourceMappingURL=core-command-stack-impl.d.ts.map