import type { Operation } from 'fast-json-patch';
import { MaybePromise } from './promises';
/**
 * An union of _command_ types that can be executed on a {@link CoreCommandStack} to edit a model.
 *
 * @template K the type of model ID used by the Model Manager
 */
export type Command<K = string> = SimpleCommand<K> | CompoundCommand<K>;
/**
 * The return result of an atomic command execution, undo, or redo is a possibly asynchronous
 * delta describing the effected model changes, or nothing.
 */
export type SimpleCommandResult = MaybePromise<Operation[] | undefined>;
/**
 * A command may optionally publish a `result` of its execution not in detailed JSON Patch terms but
 * in an abstract result type. This interface distinguishes such commands.
 */
export interface SimpleCommandWithResult<K, R> extends SimpleCommand<K> {
    /**
     * The abstract return-result of the command, available once it has been successfully executed.
     * The value is {@link PendingResult pending} until the command is executed.
     *
     * Whether the `result` value remains available or valid after the command is undone or redone
     * is not specified. In general, it is recommended only to access this result after the initial
     * execution of the command.
     *
     * The `result` property should be initialized to a {@link PendingResult} so that the command
     * may be {@linkplain isSimpleCommandWithResult recognized as providing the result} eventually
     * after execution.
     *
     * @see {@link SimpleCommand.execute}
     */
    readonly result: CommandReturnResult<R>;
}
/**
 * The specific return-result of a {@link SimpleCommandWithResult} that provides
 * an application-specific representation of the command's accomplished effect.
 */
export type CommandReturnResult<R> = ReadyResult<R> | PendingResult | FailedResult;
/**
 * Representation of a simple command result that is ready following
 * successful execution of the command.
 */
export interface ReadyResult<R> {
    status: 'ready';
    value: R;
}
/**
 * Representation of a simple command result that is not yet ready because
 * execution of the command has not been completed (or perhaps not even started).
 */
export interface PendingResult {
    status: 'pending';
}
/**
 * Representation of a simple command result that is not and will not be ready because
 * execution of the command has failed.
 */
export interface FailedResult {
    status: 'failed';
    /** An optional error message, compatible with common `Error` types. */
    error?: {
        message: string;
    };
}
/**
 * Query whether a command is a simple command publishing an abstract execution result.
 */
export declare const isSimpleCommandWithResult: <K, R>(command: Command<K>) => command is SimpleCommandWithResult<K, R>;
/**
 * Unwrap the return result of a {@link SimpleCommandWithResult}.
 *
 * @returns the return-result `value` if it is `ready`, otherwise `undefined` if it is
 *   still `pending`
 * @throws the return-result `error` if it is `failed`
 */
export declare const unwrapReturnResult: <K, R>(command: SimpleCommandWithResult<K, R>) => R | undefined;
/**
 * An operation that edits a model and may (conditionally) be undone to
 * revert its changes and redone to restore its changes.
 *
 * @template K the type of model ID used by the Model Manager
 */
export interface SimpleCommand<K = string> {
    /**
     * A label for the command that may be presented to the user in an UI, used to identify it in logs, etc.
     *
     * @readonly
     */
    readonly label: string;
    /**
     * The model affected by this command.
     *
     * @readonly
     */
    readonly modelId: K;
    /**
     * Query whether any preconditions that I may have for viable execution are met.
     * On a `true` result, I guarantee that I can effect my changes on the model correctly and completely.
     * Otherwise, it is an error to attempt to {@link execute} me.
     *
     * @param model the model on which I am to be executed
     * @returns whether I am able to be executed
     */
    canExecute(model: object): MaybePromise<boolean>;
    /**
     * Query whether any preconditions that I may have for viable undo are met.
     * On a `true` result, I guarantee that I can revert my previously executed changes on the model correctly and completely.
     * Otherwise, it is an error to attempt to {@link undo} me.
     *
     * @param model the model on which I am to be undone
     * @returns whether I am able to be undone
     */
    canUndo(model: object): MaybePromise<boolean>;
    /**
     * Query whether any preconditions that I may have for viable redo are met.
     * On a `true` result, I guarantee that I can re-apply my previously undone changes on the model correctly and completely.
     * Otherwise, it is an error to attempt to {@link redo} me.
     *
     * @param model the model on which I am to be redone
     * @returns whether I am able to be redone
     */
    canRedo(model: object): MaybePromise<boolean>;
    /**
     * Perform my changes on the model.
     *
     * If the changes that I make can be expressed as a JSON patch, the result is that patch.
     * In this case, the patch is applicable to the "before state" of the model, the state it was in before I was executed.
     *
     * @param model the model on which I am to be executed
     * @returns a JSON patch describing the changes that I performed, if they can be described in those terms
     * @throws if I am {@link canExecute not executable} according to my preconditions
     */
    execute(model: object): SimpleCommandResult;
    /**
     * Revert the changes that I had previously {@link execute}d on the model.
     *
     * If the changes that I make in this reversion can be expressed as a JSON patch, the result is that patch.
     * In this case, the patch is applicable to the "before state" of the model, the state it was in before I was undone
     * and therefore by implication the state it was in after I was originally executed.
     *
     * @param model the model on which I am to be undone
     * @returns a JSON patch describing the changes that I performed, if they can be described in those terms
     * @throws if I am {@link canUndo not undoable} according to my preconditions
     */
    undo(model: object): SimpleCommandResult;
    /**
     * Restore the changes that I had previously {@link undo}ne on the model.
     *
     * If the changes that I make in this restoration can be expressed as a JSON patch, the result is that patch.
     * In this case, the patch is applicable to the "before state" of the model, the state it was in before I was redone
     * and therefore by implication the state it was in after I was originally undone.
     *
     * @param model the model on which I am to be redone
     * @returns a JSON patch describing the changes that I performed, if they can be described in those terms
     * @throws if I am {@link canRedo not redoable} according to my preconditions
     */
    redo(model: object): SimpleCommandResult;
}
/**
 * The result of a compound command is a promised (future)
 * mapping of deltas describing the effected model changes of each
 * constituent atomic command, or nothing.
 */
export type CompoundCommandResult<K = string> = Promise<Map<Command<K>, Operation[]> | undefined>;
type GetModel<K> = (modelId: K) => object | undefined;
/**
 * An operation that edits a model by composition of one or more steps implemented by commands
 * and that may (conditionally) be undone to revert its changes and redone to restore its changes.
 *
 * Compound commands are iterable, returning their constituent leaf simple commands in forward
 * execution order. Iteration is depth-first over a tree of nested compounds.
 *
 * @template K the type of model ID used by the Model Manager
 * @interface CompoundCommand
 */
export interface CompoundCommand<K = string> extends Iterable<SimpleCommand<K>> {
    /**
     * A label for the command that may be presented to the user in an UI, used to identify it in logs, etc.
     *
     * @readonly
     */
    readonly label: string;
    /**
     * Append some number of commands to the list that I will {@link execute}.
     * Once I have been executed, my commands are frozen and may no longer be appended.
     *
     * @param commands commands to append to me
     * @returns myself, for convenience of call chaining
     * @throws if I have already been {@link execute}d
     */
    append(...commands: Command<K>[]): this;
    /**
     * Query the commands that comprise me, in the order in which they are executed.
     * The returned array is a copy and may freely be modified by the caller.
     *
     * @returns my constituent commands
     */
    getCommands(): Command<K>[];
    /**
     * Query whether any preconditions that I may have for viable execution are met.
     * At a minimum, this is a conjunction of the executability of my constituent commands.
     *
     * On a `true` result, I guarantee that I can effect my changes on the model correctly and completely.
     * Otherwise, it is an error to attempt to {@link execute} me.
     *
     * @param getModel a function providing the models on which I shall execute my sub-commands
     * @returns whether I am able to be executed
     */
    canExecute(getModel: GetModel<K>): Promise<boolean>;
    /**
     * Query whether any preconditions that I may have for viable undo are met.
     * At a minimum, this is a conjunction of the undoability of my constituent commands.
     *
     * On a `true` result, I guarantee that I can revert my previously executed changes on the model correctly and completely.
     * Otherwise, it is an error to attempt to {@link undo} me.
     *
     * @param getModel a function providing the models on which I shall undo my sub-commands
     * @returns whether I am able to be undone
     */
    canUndo(getModel: GetModel<K>): Promise<boolean>;
    /**
     * Query whether any preconditions that I may have for viable redo are met.
     * At a minimum, this is a conjunction of the redoability of my constituent commands.
     *
     * On a `true` result, I guarantee that I can re-apply my previously undone changes on the model correctly and completely.
     * Otherwise, it is an error to attempt to {@link redo} me.
     *
     * @param getModel a function providing the models on which I shall redo my sub-commands
     * @returns whether I am able to be redone
     */
    canRedo(getModel: GetModel<K>): Promise<boolean>;
    /**
     * Perform my changes on the model by execution, in forward order, of my constituent commands.
     *
     * If the changes that I make can be expressed as a JSON patch, the result is that patch.
     * In this case, the patch is applicable to the "before state" of the model, the state it was in before I was executed.
     *
     * @param getModel a function providing the models on which I shall execute my sub-commands
     * @returns a mapping of JSON patches describing the changes performed by my constituent commands,
     * if they can be described in those terms
     * @throws if I am [not executable]{@link canExecute} according to my preconditions
     */
    execute(getModel: GetModel<K>): CompoundCommandResult<K>;
    /**
     * Revert the changes that I had previously {@link execute}d on the model by undo, in reverse order, of my constituent commands.
     *
     * If the changes that I make in this reversion can be expressed as a JSON patch, the result is that patch.
     * In this case, the patch is applicable to the "before state" of the model, the state it was in before I was undone
     * and therefore by implication the state it was in after I was originally executed.
     *
     * @param getModel a function providing the models on which I shall undo my sub-commands
     * @returns a mapping of JSON patches describing the changes that I performed, if they can be described in those terms
     * @throws if I am [not undoable]{@link canUndo} according to my preconditions
     */
    undo(getModel: GetModel<K>): CompoundCommandResult<K>;
    /**
     * Restore the changes that I had previously {@link undo}ne on the model by redo, in forward order, of my constituent commands.
     *
     * If the changes that I make in this restoration can be expressed as a JSON patch, the result is that patch.
     * In this case, the patch is applicable to the "before state" of the model, the state it was in before I was redone
     * and therefore by implication the state it was in after I was originally undone.
     *
     * @param getModel a function providing the models on which I shall redo my sub-commands
     * @returns a mapping of JSON patches describing the changes that I performed, if they can be described in those terms
     * @throws if I am [not redoable]{@link canRedo} according to my preconditions
     */
    redo(getModel: GetModel<K>): CompoundCommandResult<K>;
    /**
     * Iterate my constituent commands in execution order, applying a processor to each.
     *
     * @param processor the processor function
     */
    forEach(processor: (item: SimpleCommand<K>, index: number) => void): void;
    /**
     * Iterate my constituent commands in execution order, applying a transformation to
     * each and returning the results.
     *
     * @template T the result type of the transformation function
     *
     * @param transform the transformation function
     * @returns the transformation results
     */
    map<T>(transform: (item: SimpleCommand<K>, index: number) => T): T[];
}
/**
 * An actually asynchronous {@linkplain CompoundCommandResult compound command result}.
 */
export type CommandResult<K = string> = Promise<Map<Command<K>, Operation[]> | undefined>;
/**
 * A private enumeration of the states of a `CompoundCommand`, used
 * for precondition checking on all of its operations.
 *
 * @enum State
 */
type State = 'ready' | 'executed' | 'undone';
/**
 * A basic implementation of the `CompoundCommand` interface suitable for most uses.
 *
 * @class CompoundCommandImpl
 */
export declare class CompoundCommandImpl<K = string> implements CompoundCommand<K> {
    protected readonly _label: string;
    protected readonly _commands: Command<K>[];
    protected state: State;
    constructor(label: string, ...commands: Command<K>[]);
    get label(): string;
    /** Query whether I am in the "ready" (not yet executed, undone, or redone) state. */
    protected isReady(): boolean;
    canExecute(getModel: GetModel<K>): Promise<boolean>;
    /** Query whether I am in the "executed" (or redone) state. */
    protected wasExecuted(): boolean;
    canUndo(getModel: GetModel<K>): Promise<boolean>;
    /** Query whether I am in the "undone" state. */
    protected wasUndone(): boolean;
    canRedo(getModel: GetModel<K>): Promise<boolean>;
    execute(getModel: GetModel<K>): CommandResult<K>;
    undo(getModel: GetModel<K>): CommandResult<K>;
    redo(getModel: GetModel<K>): CommandResult<K>;
    append(...commands: Command<K>[]): this;
    getCommands(): Command<K>[];
    /**
     * Compute the conjunction of a possibly asynchronous `predicate` over all of my constituent commands.
     *
     * @param predicate a test to apply to each command in turn
     * @returns the conjunction of the `predicate` results for every command, or `false` if I have no commands
     */
    protected everyCommand(predicate: (command: Command<K>) => MaybePromise<boolean>): Promise<boolean>;
    /**
     * Query whether I am in some `state`.
     *
     * @param state a state to query
     * @returns whether I am in the given `state`
     */
    private inState;
    /**
     * Guard the invocation of some operation by preconditions of `state` and other
     * arbitrary conditions.
     *
     * @param op the name of the operation being guarded
     * @param state the state in which I must be for valid invocation of the operation
     * @param canDo whether other preconditions for the operation are met
     *
     * @throws if either I am not in the given `state` or `canDo` is `false`
     */
    private checkState;
    [Symbol.iterator](): Iterator<SimpleCommand<K>>;
    forEach(processor: (item: SimpleCommand<K>, index: number) => void): void;
    map<T>(transform: (item: SimpleCommand<K>, index: number) => T): T[];
    /**
     * Iterate over my commands, applying an execute/undo/redo operation on each, and collecting the results.
     *
     * The iteration order depends on my current state: if I have been executed or redone, then iteration is backwards
     * from my last subcommand to my first, because the operation that I can perform is an undo.
     * Otherwise, the order is forwards from my first subcommand to my last.
     *
     * The result is `undefined` if all of my subcommands returned an `undefined` result.
     * Otherwise, it is a mapping of subcommands to their results, for those that returned some result.
     *
     * @param operation the operation to invoke on my commands
     * @param getModel a function providing the models on which I shall execute/undo/redo my sub-commands
     * @returns the aggregate results of the `operation` over my commands, if available
     */
    private iterateCommands;
}
/**
 * Type guard determining whether a `command` is a `CompoundCommand`.
 *
 * @function
 * @param command a command
 * @returns whether the `command` is a `CompoundCommand`
 */
export declare const isCompoundCommand: <K = string>(command: Command<K>) => command is CompoundCommand<K>;
/**
 * Append zero or more commands to a `base` command.
 *
 * If the `commands` to append are none, then the `base` is returned as is.
 *
 * If the `base` command is already a compound, it is appended in place and returned.
 * Otherwise, a new `CompoundCommand` is created from all of the given commands and
 * returned.
 *
 * In all cases, the label of the result is the label of the `base` command.
 *
 * @template K the type of model ID used by the Model Manager
 * @param base the command on which to append one or more additional commands
 * @param commands commands to append
 * @returns a compound of the `base` and, `commands`, in that order
 */
export declare const append: <K = string>(base: Command<K>, ...commands: Command<K>[]) => Command<K>;
/**
 * Convert a map of (Command, Operation[]) to a map of (Model, Operation[]).

 * @param commands The map to convert (typically the result of an execute/undo/redo call)
 * @returns A map grouping result Operation[] by affected model
 */
export declare const groupByModelId: <K = string>(commands: Map<Command<K>, Operation[]>) => Map<K, Operation[]>;
export {};
//# sourceMappingURL=command.d.ts.map