import { LiteralUnion } from 'type-fest';
import { GamepadButtonName } from '../gamepad';
import { AllKeyCodes } from './keyCodes';
import { StoreProvider } from './store';
/** @partial */
export interface InputCommandOptions {
    /** @default true */
    preventDefault?: boolean;
    /**
     * Temporary disable emitting events
     * @default false
     */
    disabled?: boolean;
}
/** resolved command from schema */
export type SchemaCommand = InputCommandOptions & {
    keys: AllKeyCodes[];
    gamepad: GamepadButtonName[];
};
export type KeysGroup = 'WASD' | 'Arrows' | 'Digits' | 'NumpadDigits' | 'AnyDigits' | 'Modifiers' | 'FKeys' | 'PageMoveKeys';
type KeyboardKey = AllKeyCodes | null;
export type SchemaCommandInput = [KeyboardKey | AllKeyCodes[], GamepadButtonName?, InputCommandOptions?] | null;
export type InputCommandsSchema = {
    [category: string]: {
        [command: string]: SchemaCommandInput;
    };
};
type GamepadButtons = GamepadButtonName[];
export type InputGroupedCommandsSchema = {
    [category: string]: {
        [groupedCommand: string]: [KeysGroup | AllKeyCodes[], GamepadButtons, InputCommandOptions?];
    };
};
export type InputSchema = {
    /**
     * By default, we're automatically listening to these keys and update movement vector. Specify null to disable for keyboard
     * @default null
     */
    movementKeymap?: 'WASD' | 'WASDArrows' | 'Arrows' | null;
};
export interface InputSchemaArg<T extends InputCommandsSchema, K extends InputGroupedCommandsSchema> extends InputSchema {
    commands: T;
    groupedCommands?: K;
}
export type MovementVector2d = {
    x: number;
    z: number;
};
export type MovementVector3d = {
    x: number;
    y: number;
    z: number;
};
type SubPath<T, Key extends keyof T> = Key extends string ? T[Key] extends Record<string, any> ? `${Key}.${Exclude<keyof T[Key], keyof any[]> & string}` : never : never;
export type AllSchemaCommands<T extends InputCommandsSchema | InputGroupedCommandsSchema> = SubPath<T, keyof T>;
export type CommandEventArgument<T extends InputCommandsSchema> = {
    command: AllSchemaCommands<T>;
};
export type GroupedCommandEventDeviceType = // TODO doc: should rely on index rather than button or key
{
    type: 'keyboard';
    key: AllKeyCodes;
} | {
    type: 'gamepad';
    gamepadIndex: number;
    button: GamepadButtonName;
};
export type GroupCommandEventArgument<K extends InputGroupedCommandsSchema> = {
    command: AllSchemaCommands<K>;
    schema: InputGroupedCommandsSchema[''][''];
    /** Keyboard key or gamepad button index */
    index: number;
} & GroupedCommandEventDeviceType;
export type SourceType = {
    /** Who emitted event: Gamepad with index or keyboard if `undefined` */
    gamepadIndex?: number;
};
export type ControEvents<T extends InputCommandsSchema, K extends InputGroupedCommandsSchema, M extends '2d' | '3d'> = {
    trigger: (arg0: CommandEventArgument<T>) => void;
    triggerGrouped: (arg0: GroupCommandEventArgument<K>) => void;
    release: (arg0: CommandEventArgument<T>) => void;
    releaseGrouped: (arg0: GroupCommandEventArgument<K>) => void;
    stickMovement: (arg0: {
        stick: 'left' | 'right';
        vector: MovementVector2d;
    }) => void;
    movementUpdate: (arg0: {
        vector: M extends '3d' ? MovementVector3d : MovementVector2d;
        soleVector: M extends '3d' ? MovementVector3d : MovementVector2d;
    } & SourceType) => void;
    /** usually to switch slots */
    mouseWheel: (arg0: {
        direction: -1 | 1;
    }) => void;
    pressedKeyOrButtonChanged: (arg0: ({
        code: AllKeyCodes;
    } | {
        gamepadIndex: number;
        button: GamepadButtonName;
    }) & {
        state: boolean;
    }) => void;
    userConfigResolve: () => void;
};
type KeyboardTarget = Record<'addEventListener' | 'removeEventListener', (...args: ['keydown' | 'keyup', (e: KeyboardEvent) => void]) => unknown>;
export interface CreateControlsSchemaOptions {
    /** To what bind events
     * @default window */
    target?: KeyboardTarget;
    /**
     * Sometimes target is dynamic, so you use global target and filter events here
     *
     * @param e undefined if is gamepad
     * @returns continue
     */
    captureEvents?: (e?: KeyboardEvent) => boolean;
    /**
     * If true, then events that fired programmatically will be ignored
     * @default false
     */
    requireTrusted?: boolean;
    /** Use additional DOM events to workaround certain bugs with keyboard
     * @default true */
    additionalEventHandlers?: boolean;
    /**
     * - `only-first-gamepad`: listen for first only connected gamepad
     * - `only-last-gamepad`: listen for last only connected gamepad
     * - `all-gamepads`: listen for buttons and sticks from all connected gamepads
     * - `none`: temporary disabling gamepads support
     * @default all-gamepads
     */
    listenGamepadsStrategy?: 'only-first-gamepad' | 'only-last-gamepad' | 'all-gamepads' | 'none';
    defaultControlOptions?: InputCommandOptions;
    /**
     * Default is precise enough
     * @default 250 */
    gamepadPollingInterval?: LiteralUnion<500 | 250, number>;
    /**
     * - all - emit buttons
     * - split -
     * @default all
     */
    /**
     * - all - sum movement from keyboard and all listening gamepads (controlled by `listenGamepadsStrategy`)
     * - split - emit independent movement vectors for keyboard and each gamepads. Useful for split-screen games
     * @default all
     */
    emitMovement?: 'all' | 'split';
    storeProvider?: StoreProvider;
}
export {};
