/**
 * This file provides type definitions for use with the Flow type checker.
 *
 * @flow
 */

export type DispatcherPayload = Object;
declare export class Payload {
    type: mixed;
}
declare export class CompletedPayload extends Payload {
    value: ?mixed;
}
declare export class DidExecutedPayload extends Payload {
    value: ?mixed;
}
declare export class ErrorPayload extends Payload {
    error: Error | mixed;
}
declare export class WillExecutedPayload extends Payload {
    args: Array<mixed>;
}
// Internal class
declare export class DispatcherPayloadMeta {
    useCase: ?UseCase;
    dispatcher: ?Dispatcher | UseCase;
    parentUseCase: ?UseCase;
    timeStamp: number;
    isTrusted: boolean;
}

declare export class StoreGroup extends Dispatcher {
    constructor(stateStoreMapping: {[string]: Store}): void;
    getState(): Object;
    emitChange(): void;
    onChange(handler: (stores: Array<Store>) => mixed): Function;
    release(): void;
}

declare export class Context {
    constructor({ dispatcher: Dispatcher, store: Store|StoreGroup }): void;
    getState(): mixed;
    onChange(onChangeHandler: (changingStores: Array<Store>) => mixed): Function;
    useCase(useCase: UseCase): UseCaseExecutor;
    onWillExecuteEachUseCase(handler: (payload: WillExecutedPayload, meta: DispatcherPayloadMeta) => mixed): Function;
    onDispatch(handler: (payload: DispatcherPayload|Payload, meta: DispatcherPayloadMeta) => mixed): Function;
    onDidExecuteEachUseCase(handler: (payload: DidExecutedPayload, meta: DispatcherPayloadMeta) => mixed): Function;
    onCompleteExecuteEachUseCase(handler: (payload: CompletedPayload, meta: DispatcherPayloadMeta) => mixed): Function;
    onErrorDispatch(handler: (payload: ErrorPayload, meta: DispatcherPayloadMeta) => mixed): Function;
    release(): void;
}


declare export class Dispatcher extends events$EventEmitter {
    static isDispatcher(maybeDispatcher: mixed): boolean;

    onDispatch(payloadHandler: (payload: DispatcherPayload) => mixed): Function;
    dispatch(payload: DispatcherPayload|Payload): void;
    pipe(toDispatcher: Dispatcher): Function;
}

declare export class Store extends Dispatcher {
    static isStore(maybeStore: mixed): boolean;

    shouldStateUpdate(prevState:Object, nextState:Object): boolean;
    receivePayload(payload: DispatcherPayload): void;
    getState(): Object;
    setState(newState: Object): void;
    onChange(stores: Array<Store>): Function;
    emitChange(): void;
}

declare export class UseCase extends Dispatcher {
    static isUseCase(maybeUseCase: mixed): boolean;

    id: string;
    name: string;
    context(): UseCaseContext;
    onError(errorHandler: (error: Error) => mixed): Function;
    throwError(error: Error): void;
}

declare export class UseCaseContext {
    useCase(useCase: UseCase): UseCaseExecutor;
}

declare export class UseCaseExecutor {
    onWillExecuteEachUseCase(handler: (payload: WillExecutedPayload, meta: DispatcherPayloadMeta) => mixed): void;
    onDidExecuteEachUseCase(handler: (payload: DidExecutedPayload, meta: DispatcherPayloadMeta) => mixed): void;
    onCompleteExecuteEachUseCase(handler: (payload: CompletedPayload, meta: DispatcherPayloadMeta) => mixed): Function;
    execute(args?: Array<mixed>): Promise<void>;
    release(): void;
}
