import { FieldSchema } from '@ephox/boulder';
import { Arr, Fun, Optional } from '@ephox/katamari';
import * as Boxes from '../../alien/Boxes';
import * as ComponentStructure from '../../alien/ComponentStructure';
import * as Fields from '../../data/Fields';
import * as Layout from '../../positioning/layout/Layout';
import * as Dismissal from '../../sandbox/Dismissal';
import * as Reposition from '../../sandbox/Reposition';
import { Positioning } from '../behaviour/Positioning';
import { Receiving } from '../behaviour/Receiving';
import { Representing } from '../behaviour/Representing';
import { Sandboxing } from '../behaviour/Sandboxing';
import * as SketchBehaviours from '../component/SketchBehaviours';
import * as SystemEvents from '../events/SystemEvents';
import * as Sketcher from './Sketcher';
import { tieredMenu as TieredMenu } from './TieredMenu';
const makeMenu = (detail, menuSandbox, placementSpec, menuSpec, getBounds) => {
    const lazySink = () => detail.lazySink(menuSandbox);
    const layouts = menuSpec.type === 'horizontal' ? { layouts: {
            onLtr: () => Layout.belowOrAbove(),
            onRtl: () => Layout.belowOrAboveRtl()
        } } : {};
    const isFirstTierSubmenu = (triggeringPaths) => triggeringPaths.length === 2; // primary and first tier menu === 2 items
    const getSubmenuLayouts = (triggeringPaths) => isFirstTierSubmenu(triggeringPaths) ? layouts : {};
    return TieredMenu.sketch({
        dom: {
            tag: 'div'
        },
        data: menuSpec.data,
        markers: menuSpec.menu.markers,
        highlightImmediately: menuSpec.menu.highlightImmediately,
        onEscape: () => {
            // Note for the future: this should possibly also call detail.onHide
            Sandboxing.close(menuSandbox);
            detail.onEscape.map((handler) => handler(menuSandbox));
            return Optional.some(true);
        },
        onExecute: () => {
            return Optional.some(true);
        },
        onOpenMenu: (tmenu, menu) => {
            Positioning.positionWithinBounds(lazySink().getOrDie(), menu, placementSpec, getBounds());
        },
        onOpenSubmenu: (tmenu, item, submenu, triggeringPaths) => {
            const sink = lazySink().getOrDie();
            Positioning.position(sink, submenu, {
                anchor: {
                    type: 'submenu',
                    item,
                    ...getSubmenuLayouts(triggeringPaths)
                }
            });
        },
        onRepositionMenu: (tmenu, primaryMenu, submenuTriggers) => {
            const sink = lazySink().getOrDie();
            Positioning.positionWithinBounds(sink, primaryMenu, placementSpec, getBounds());
            Arr.each(submenuTriggers, (st) => {
                const submenuLayouts = getSubmenuLayouts(st.triggeringPath);
                Positioning.position(sink, st.triggeredMenu, {
                    anchor: { type: 'submenu', item: st.triggeringItem, ...submenuLayouts }
                });
            });
        }
    });
};
const factory = (detail, spec) => {
    const isPartOfRelated = (sandbox, queryElem) => {
        const related = detail.getRelated(sandbox);
        return related.exists((rel) => ComponentStructure.isPartOf(rel, queryElem));
    };
    const setContent = (sandbox, thing) => {
        // Keep the same location, and just change the content.
        Sandboxing.setContent(sandbox, thing);
    };
    const showAt = (sandbox, thing, placementSpec) => {
        showWithin(sandbox, thing, placementSpec, Optional.none());
    };
    const showWithin = (sandbox, thing, placementSpec, boxElement) => {
        showWithinBounds(sandbox, thing, placementSpec, () => boxElement.map((elem) => Boxes.box(elem)));
    };
    const showWithinBounds = (sandbox, thing, placementSpec, getBounds) => {
        const sink = detail.lazySink(sandbox).getOrDie();
        Sandboxing.openWhileCloaked(sandbox, thing, () => Positioning.positionWithinBounds(sink, sandbox, placementSpec, getBounds()));
        Representing.setValue(sandbox, Optional.some({
            mode: 'position',
            config: placementSpec,
            getBounds
        }));
    };
    // TODO AP-191 write a test for showMenuAt
    const showMenuAt = (sandbox, placementSpec, menuSpec) => {
        showMenuWithinBounds(sandbox, placementSpec, menuSpec, Optional.none);
    };
    const showMenuWithinBounds = (sandbox, placementSpec, menuSpec, getBounds) => {
        const menu = makeMenu(detail, sandbox, placementSpec, menuSpec, getBounds);
        Sandboxing.open(sandbox, menu);
        Representing.setValue(sandbox, Optional.some({
            mode: 'menu',
            menu
        }));
    };
    const hide = (sandbox) => {
        if (Sandboxing.isOpen(sandbox)) {
            Representing.setValue(sandbox, Optional.none());
            Sandboxing.close(sandbox);
        }
    };
    const getContent = (sandbox) => Sandboxing.getState(sandbox);
    const reposition = (sandbox) => {
        if (Sandboxing.isOpen(sandbox)) {
            Representing.getValue(sandbox).each((state) => {
                switch (state.mode) {
                    case 'menu':
                        Sandboxing.getState(sandbox).each(TieredMenu.repositionMenus);
                        break;
                    case 'position':
                        const sink = detail.lazySink(sandbox).getOrDie();
                        Positioning.positionWithinBounds(sink, sandbox, state.config, state.getBounds());
                        break;
                }
            });
        }
    };
    const apis = {
        setContent,
        showAt,
        showWithin,
        showWithinBounds,
        showMenuAt,
        showMenuWithinBounds,
        hide,
        getContent,
        reposition,
        isOpen: Sandboxing.isOpen
    };
    return {
        uid: detail.uid,
        dom: detail.dom,
        behaviours: SketchBehaviours.augment(detail.inlineBehaviours, [
            Sandboxing.config({
                isPartOf: (sandbox, data, queryElem) => {
                    return ComponentStructure.isPartOf(data, queryElem) || isPartOfRelated(sandbox, queryElem);
                },
                getAttachPoint: (sandbox) => {
                    return detail.lazySink(sandbox).getOrDie();
                },
                onOpen: (sandbox) => {
                    detail.onShow(sandbox);
                },
                onClose: (sandbox) => {
                    detail.onHide(sandbox);
                }
            }),
            Representing.config({
                store: {
                    mode: 'memory',
                    initialValue: Optional.none()
                }
            }),
            Receiving.config({
                channels: {
                    ...Dismissal.receivingChannel({
                        isExtraPart: spec.isExtraPart,
                        ...detail.fireDismissalEventInstead.map((fe) => ({ fireEventInstead: { event: fe.event } })).getOr({})
                    }),
                    ...Reposition.receivingChannel({
                        ...detail.fireRepositionEventInstead.map((fe) => ({ fireEventInstead: { event: fe.event } })).getOr({}),
                        doReposition: reposition
                    })
                }
            })
        ]),
        eventOrder: detail.eventOrder,
        apis
    };
};
const InlineView = Sketcher.single({
    name: 'InlineView',
    configFields: [
        FieldSchema.required('lazySink'),
        Fields.onHandler('onShow'),
        Fields.onHandler('onHide'),
        FieldSchema.optionFunction('onEscape'),
        SketchBehaviours.field('inlineBehaviours', [Sandboxing, Representing, Receiving]),
        FieldSchema.optionObjOf('fireDismissalEventInstead', [
            FieldSchema.defaulted('event', SystemEvents.dismissRequested())
        ]),
        FieldSchema.optionObjOf('fireRepositionEventInstead', [
            FieldSchema.defaulted('event', SystemEvents.repositionRequested())
        ]),
        FieldSchema.defaulted('getRelated', Optional.none),
        FieldSchema.defaulted('isExtraPart', Fun.never),
        FieldSchema.defaulted('eventOrder', Optional.none)
    ],
    factory,
    apis: {
        showAt: (apis, component, anchor, thing) => {
            apis.showAt(component, anchor, thing);
        },
        showWithin: (apis, component, anchor, thing, boxElement) => {
            apis.showWithin(component, anchor, thing, boxElement);
        },
        showWithinBounds: (apis, component, anchor, thing, bounds) => {
            apis.showWithinBounds(component, anchor, thing, bounds);
        },
        showMenuAt: (apis, component, anchor, menuSpec) => {
            apis.showMenuAt(component, anchor, menuSpec);
        },
        showMenuWithinBounds: (apis, component, anchor, menuSpec, bounds) => {
            apis.showMenuWithinBounds(component, anchor, menuSpec, bounds);
        },
        hide: (apis, component) => {
            apis.hide(component);
        },
        isOpen: (apis, component) => apis.isOpen(component),
        getContent: (apis, component) => apis.getContent(component),
        setContent: (apis, component, thing) => {
            apis.setContent(component, thing);
        },
        reposition: (apis, component) => {
            apis.reposition(component);
        }
    }
});
export { InlineView };
//# sourceMappingURL=InlineView.js.map