import { FieldSchema } from '@ephox/boulder';
import { Cell, Fun, Optional } from '@ephox/katamari';
import { Coupling } from '../../api/behaviour/Coupling';
import { Focusing } from '../../api/behaviour/Focusing';
import { Highlighting } from '../../api/behaviour/Highlighting';
import { Keying } from '../../api/behaviour/Keying';
import { Representing } from '../../api/behaviour/Representing';
import { Streaming } from '../../api/behaviour/Streaming';
import { Toggling } from '../../api/behaviour/Toggling';
import * as SketchBehaviours from '../../api/component/SketchBehaviours';
import * as AlloyTriggers from '../../api/events/AlloyTriggers';
import * as Fields from '../../data/Fields';
import * as SketcherFields from '../../data/SketcherFields';
import * as PartType from '../../parts/PartType';
import * as InputBase from '../common/InputBase';
import * as TypeaheadEvents from '../composite/TypeaheadEvents';
import { attemptSelectOver, setValueFromItem } from '../typeahead/TypeaheadModel';
const schema = Fun.constant([
    FieldSchema.option('lazySink'),
    FieldSchema.required('fetch'),
    FieldSchema.defaulted('minChars', 5),
    FieldSchema.defaulted('responseTime', 1000),
    Fields.onHandler('onOpen'),
    // TODO: Remove dupe with Dropdown
    FieldSchema.defaulted('getHotspot', Optional.some),
    FieldSchema.defaulted('getAnchorOverrides', Fun.constant({})),
    FieldSchema.defaulted('layouts', Optional.none()),
    FieldSchema.defaulted('eventOrder', {}),
    // Information about what these model settings do can be found in TypeaheadTypes
    FieldSchema.defaultedObjOf('model', {}, [
        FieldSchema.defaulted('getDisplayText', (itemData) => itemData.meta !== undefined && itemData.meta.text !== undefined ? itemData.meta.text : itemData.value),
        FieldSchema.defaulted('selectsOver', true),
        FieldSchema.defaulted('populateFromBrowse', true)
    ]),
    Fields.onHandler('onSetValue'),
    Fields.onKeyboardHandler('onExecute'),
    Fields.onHandler('onItemExecute'),
    FieldSchema.defaulted('inputClasses', []),
    FieldSchema.defaulted('inputAttributes', {}),
    FieldSchema.defaulted('inputStyles', {}),
    FieldSchema.defaulted('matchWidth', true),
    FieldSchema.defaulted('useMinWidth', false),
    FieldSchema.defaulted('dismissOnBlur', true),
    Fields.markers(['openClass']),
    FieldSchema.option('initialData'),
    SketchBehaviours.field('typeaheadBehaviours', [
        Focusing, Representing, Streaming, Keying, Toggling, Coupling
    ]),
    FieldSchema.customField('lazyTypeaheadComp', () => Cell(Optional.none)),
    FieldSchema.customField('previewing', () => Cell(true))
].concat(InputBase.schema()).concat(SketcherFields.sandboxFields()));
const parts = Fun.constant([
    PartType.external({
        schema: [
            Fields.tieredMenuMarkers()
        ],
        name: 'menu',
        overrides: (detail) => {
            return {
                fakeFocus: true,
                onHighlightItem: (_tmenu, menu, item) => {
                    if (!detail.previewing.get()) {
                        // We need to use this type of reference, rather than just looking
                        // it up from the system by uid, because the input and the tieredmenu
                        // might be in different systems.
                        detail.lazyTypeaheadComp.get().each((input) => {
                            if (detail.model.populateFromBrowse) {
                                setValueFromItem(detail.model, input, item);
                            }
                        });
                    }
                    else {
                        // ASSUMPTION: Currently, any interaction with the menu via the keyboard or the mouse
                        // will firstly clear previewing mode before triggering any highlights
                        // so if we are still in previewing mode by the time we get to the highlight call,
                        // that means that the highlight was triggered NOT by the user interacting
                        // with the menu, but instead by the Highlighting API call that happens automatically
                        // when a streamed keyboard input event is updating its results. That call will
                        // try to keep any active highlight if there already was one (defaulting to first
                        // if it can't find the original), but if there wasn't an active highlight, but
                        // it is using "selectsOver", it will just highlight the first item. In this
                        // latter case, it is only doing that so that selectsOver has something to copy.
                        // So all of the complex code below is trying to handle whether we should stay
                        // in previewing mode after this highlight, and the ONLY case where we should stay
                        // in previewing mode is that we were in previewing mode, we are using selectsOver,
                        // and the selectsOver failed to succeed. In that case, to stay in previewing mode,
                        // we want to cancel the highlight that we just made via the highlighting API
                        // and reset previewing to true. Otherwise, all codepaths should set previewing
                        // to false, because now we have a valid highlight.
                        //
                        // As of 2022-08-18, the selectsOver model is not in use by TinyMCE, so
                        // this subtle interaction is unfortunately largely untested. Also, if we can't
                        // get a reference to the typeahead input by lazyTypeaheadComp, then we don't
                        // change previewing, either. Note also, that it is likely that if we checked
                        // if selectsOver would succeed before setting the highlight in the streaming
                        // response, this could might be a lot easier to follow.
                        detail.lazyTypeaheadComp.get().each((input) => {
                            attemptSelectOver(detail.model, input, item).fold(
                            // If we are in "previewing" mode and we can't select over the
                            // thing that is first, then clear the highlight.
                            // Hopefully, this doesn't cause a flicker. Find a better
                            // way to do this.
                            () => {
                                // If using "selectOver", we essentially want to cancel the highlight
                                // that was only invoked just so that we'd have something to selectOver,
                                // so we dehighlight, and then, importantly, *DON'T* clear previewing.
                                // We'll set it to be true to be explicit, although it should
                                // always be true if it reached here (unless an above function changed
                                // it)
                                if (detail.model.selectsOver) {
                                    Highlighting.dehighlight(menu, item);
                                    detail.previewing.set(true);
                                }
                                else {
                                    // Because we aren't using selectsOver mode, we now want to keep
                                    // whatever highlight we just made, and because we have a highlighted
                                    // item in the menu, we are no longer previewing.
                                    detail.previewing.set(false);
                                }
                            }, ((selectOverTextInInput) => {
                                // We have made a selection in the menu, and have selected over text
                                // in the input, so clear previewing.
                                selectOverTextInInput();
                                detail.previewing.set(false);
                            }));
                        });
                    }
                },
                // Because the focus stays inside the input, this onExecute is fired when the
                // user "clicks" on an item. The focusing behaviour should be configured
                // so that items don't get focus, but they prevent a mousedown event from
                // firing so that the typeahead doesn't lose focus. This is the handler
                // for clicking on an item. We need to close the sandbox, update the typeahead
                // to show the item clicked on, and fire an execute.
                onExecute: (_menu, item) => {
                    // Note: This will only work when the typeahead and menu are in the same system.
                    return detail.lazyTypeaheadComp.get().map((typeahead) => {
                        AlloyTriggers.emitWith(typeahead, TypeaheadEvents.itemExecute(), { item });
                        return true;
                    });
                },
                onHover: (menu, item) => {
                    // Hovering is also a user-initiated action, so previewing mode is over.
                    // TODO: Have a better API for managing state in between parts.
                    detail.previewing.set(false);
                    detail.lazyTypeaheadComp.get().each((input) => {
                        if (detail.model.populateFromBrowse) {
                            setValueFromItem(detail.model, input, item);
                        }
                    });
                }
            };
        }
    })
]);
const name = Fun.constant('Typeahead');
export { name, schema, parts };
//# sourceMappingURL=TypeaheadSchema.js.map