import { FieldSchema, StructureSchema } from '@ephox/boulder';
import { Arr, Optional, Result } from '@ephox/katamari';
import * as EventRoot from '../alien/EventRoot';
import * as Keys from '../alien/Keys';
import * as AlloyEvents from '../api/events/AlloyEvents';
import * as NativeEvents from '../api/events/NativeEvents';
import * as SystemEvents from '../api/events/SystemEvents';
import * as FocusManagers from '../api/focus/FocusManagers';
import * as Fields from '../data/Fields';
import { inSet } from '../navigation/KeyMatch';
import * as KeyRules from '../navigation/KeyRules';
import { FocusInsideModes } from './KeyingModeTypes';
const typical = (infoSchema, stateInit, getKeydownRules, getKeyupRules, optFocusIn) => {
    const schema = () => infoSchema.concat([
        FieldSchema.defaulted('focusManager', FocusManagers.dom()),
        FieldSchema.defaultedOf('focusInside', 'onFocus', StructureSchema.valueOf((val) => Arr.contains(['onFocus', 'onEnterOrSpace', 'onApi'], val) ? Result.value(val) : Result.error('Invalid value for focusInside'))),
        Fields.output('handler', me),
        Fields.output('state', stateInit),
        Fields.output('sendFocusIn', optFocusIn)
    ]);
    const processKey = (component, simulatedEvent, getRules, keyingConfig, keyingState) => {
        const rules = getRules(component, simulatedEvent, keyingConfig, keyingState);
        return KeyRules.choose(rules, simulatedEvent.event).bind((rule) => rule(component, simulatedEvent, keyingConfig, keyingState));
    };
    const toEvents = (keyingConfig, keyingState) => {
        const onFocusHandler = keyingConfig.focusInside !== FocusInsideModes.OnFocusMode
            ? Optional.none()
            : optFocusIn(keyingConfig).map((focusIn) => AlloyEvents.run(SystemEvents.focus(), (component, simulatedEvent) => {
                focusIn(component, keyingConfig, keyingState);
                simulatedEvent.stop();
            }));
        // On enter or space on root element, if using EnterOrSpace focus mode, fire a focusIn on the component
        const tryGoInsideComponent = (component, simulatedEvent) => {
            const isEnterOrSpace = inSet(Keys.SPACE.concat(Keys.ENTER))(simulatedEvent.event);
            if (keyingConfig.focusInside === FocusInsideModes.OnEnterOrSpaceMode && isEnterOrSpace && EventRoot.isSource(component, simulatedEvent)) {
                optFocusIn(keyingConfig).each((focusIn) => {
                    focusIn(component, keyingConfig, keyingState);
                    simulatedEvent.stop();
                });
            }
        };
        const keyboardEvents = [
            AlloyEvents.run(NativeEvents.keydown(), (component, simulatedEvent) => {
                processKey(component, simulatedEvent, getKeydownRules, keyingConfig, keyingState).fold(() => {
                    // Key wasn't handled ... so see if we should enter into the component (focusIn)
                    tryGoInsideComponent(component, simulatedEvent);
                }, (_) => {
                    simulatedEvent.stop();
                });
            }),
            AlloyEvents.run(NativeEvents.keyup(), (component, simulatedEvent) => {
                processKey(component, simulatedEvent, getKeyupRules, keyingConfig, keyingState).each((_) => {
                    simulatedEvent.stop();
                });
            })
        ];
        return AlloyEvents.derive(onFocusHandler.toArray().concat(keyboardEvents));
    };
    const me = {
        schema,
        processKey,
        toEvents
    };
    return me;
};
export { typical };
//# sourceMappingURL=KeyingType.js.map