import { Fun, Obj, Optional } from '@ephox/katamari';
import * as DropdownUtils from '../../dropdown/DropdownUtils';
import * as ButtonBase from '../../ui/common/ButtonBase';
import * as DropdownSchema from '../../ui/schema/DropdownSchema';
import { HighlightOnOpen } from '../../ui/types/TieredMenuTypes';
import { Coupling } from '../behaviour/Coupling';
import { Focusing } from '../behaviour/Focusing';
import { Keying } from '../behaviour/Keying';
import { Sandboxing } from '../behaviour/Sandboxing';
import { Toggling } from '../behaviour/Toggling';
import * as SketchBehaviours from '../component/SketchBehaviours';
import * as AlloyTriggers from '../events/AlloyTriggers';
import * as SystemEvents from '../events/SystemEvents';
import * as Sketcher from './Sketcher';
import * as TieredMenu from './TieredMenu';
const factory = (detail, components, _spec, externals) => {
    const lookupAttr = (attr) => Obj.get(detail.dom, 'attributes').bind((attrs) => Obj.get(attrs, attr));
    const switchToMenu = (sandbox) => {
        Sandboxing.getState(sandbox).each((tmenu) => {
            // This will highlight the menu AND the item
            TieredMenu.tieredMenu.highlightPrimary(tmenu);
        });
    };
    const togglePopup = (dropdownComp, onOpenSync, highlightOnOpen) => {
        return DropdownUtils.togglePopup(detail, Fun.identity, dropdownComp, externals, onOpenSync, highlightOnOpen);
    };
    const action = (component) => {
        const onOpenSync = switchToMenu;
        togglePopup(component, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(Fun.noop);
    };
    const apis = {
        expand: (comp) => {
            if (!Toggling.isOn(comp)) {
                togglePopup(comp, Fun.noop, HighlightOnOpen.HighlightNone).get(Fun.noop);
            }
        },
        open: (comp) => {
            if (!Toggling.isOn(comp)) {
                togglePopup(comp, Fun.noop, HighlightOnOpen.HighlightMenuAndItem).get(Fun.noop);
            }
        },
        refetch: (comp) => {
            // Generally, the triggers for a refetch should make it so that the
            // sandbox has been created, but it's not guaranteed, so we still handle the
            // case where there isn't yet a sandbox.
            const optSandbox = Coupling.getExistingCoupled(comp, 'sandbox');
            return optSandbox.fold(() => {
                // If we don't have a sandbox, refetch is the same as open,
                // except we return when it is completed.
                return togglePopup(comp, Fun.noop, HighlightOnOpen.HighlightMenuAndItem)
                    .map(Fun.noop);
            }, (sandboxComp) => {
                // We are intentionally not preserving the selected items when
                // triggering a refetch, and will just highlight the first item.
                // Note: this will mean that submenus will close. If we want to start
                // preserving the selected items, we can't rely on the components themselves,
                // so we'd need to use the item and menu values through Representing.
                // However, be aware that alloy menus and items often have randomised values,
                // so these might not be reliable either.
                // NOTE: We use DropdownUtils.open directly, because we want it to 'open',
                // even if it's already open. If we just used apis.open, it wouldn't do
                // anything if it was already open, which means we wouldn't see the new
                // refetched data.
                return DropdownUtils.open(detail, Fun.identity, comp, 
                // NOTE: The TieredMenu is inside the sandbox. They aren't the same component.
                sandboxComp, externals, Fun.noop, HighlightOnOpen.HighlightMenuAndItem).map(Fun.noop);
            });
        },
        isOpen: Toggling.isOn,
        close: (comp) => {
            if (Toggling.isOn(comp)) {
                togglePopup(comp, Fun.noop, HighlightOnOpen.HighlightMenuAndItem).get(Fun.noop);
            }
        },
        // If we are open, refresh the menus in the tiered menu system
        repositionMenus: (comp) => {
            if (Toggling.isOn(comp)) {
                DropdownUtils.repositionMenus(comp);
            }
        }
    };
    const triggerExecute = (comp, _se) => {
        AlloyTriggers.emitExecute(comp);
        return Optional.some(true);
    };
    return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        behaviours: SketchBehaviours.augment(detail.dropdownBehaviours, [
            Toggling.config({
                toggleClass: detail.toggleClass,
                aria: {
                    mode: 'expanded'
                }
            }),
            Coupling.config({
                others: {
                    sandbox: (hotspot) => {
                        return DropdownUtils.makeSandbox(detail, hotspot, {
                            onOpen: () => Toggling.on(hotspot),
                            onClose: () => Toggling.off(hotspot)
                        });
                    }
                }
            }),
            Keying.config({
                mode: 'special',
                onSpace: triggerExecute,
                onEnter: triggerExecute,
                onDown: (comp, _se) => {
                    if (Dropdown.isOpen(comp)) {
                        const sandbox = Coupling.getCoupled(comp, 'sandbox');
                        switchToMenu(sandbox);
                    }
                    else {
                        Dropdown.open(comp);
                    }
                    return Optional.some(true);
                },
                onEscape: (comp, _se) => {
                    if (Dropdown.isOpen(comp)) {
                        Dropdown.close(comp);
                        return Optional.some(true);
                    }
                    else {
                        return Optional.none();
                    }
                }
            }),
            Focusing.config({})
        ]),
        events: ButtonBase.events(Optional.some(action)),
        eventOrder: {
            ...detail.eventOrder,
            // Order, the button state is toggled first, so assumed !selected means close.
            [SystemEvents.execute()]: ['disabling', 'toggling', 'alloy.base.behaviour']
        },
        apis,
        domModification: {
            attributes: {
                'aria-haspopup': 'true',
                ...detail.role.fold(() => ({}), (role) => ({ role })),
                ...detail.dom.tag === 'button' ? { type: lookupAttr('type').getOr('button') } : {}
            }
        }
    };
};
const Dropdown = Sketcher.composite({
    name: 'Dropdown',
    configFields: DropdownSchema.schema(),
    partFields: DropdownSchema.parts(),
    factory,
    apis: {
        open: (apis, comp) => apis.open(comp),
        refetch: (apis, comp) => apis.refetch(comp),
        expand: (apis, comp) => apis.expand(comp),
        close: (apis, comp) => apis.close(comp),
        isOpen: (apis, comp) => apis.isOpen(comp),
        repositionMenus: (apis, comp) => apis.repositionMenus(comp)
    }
});
export { Dropdown };
//# sourceMappingURL=Dropdown.js.map