import { Arr } from '@ephox/katamari';
import { Classes, Css } from '@ephox/sugar';
import { applyPositionCss } from '../../positioning/view/PositionCss';
import * as Dockables from './Dockables';
const morphToStatic = (component, config, state) => {
    state.setDocked(false);
    Arr.each(['left', 'right', 'top', 'bottom', 'position'], (prop) => Css.remove(component.element, prop));
    config.onUndocked(component);
};
const morphToCoord = (component, config, state, position) => {
    const isDocked = position.position === 'fixed';
    state.setDocked(isDocked);
    applyPositionCss(component.element, position);
    const method = isDocked ? config.onDocked : config.onUndocked;
    method(component);
};
const updateVisibility = (component, config, state, viewport, morphToDocked = false) => {
    config.contextual.each((contextInfo) => {
        // Make the dockable component disappear if the context is outside the viewport
        contextInfo.lazyContext(component).each((box) => {
            const isVisible = Dockables.isPartiallyVisible(box, viewport.bounds);
            if (isVisible !== state.isVisible()) {
                state.setVisible(isVisible);
                // If morphing to docked and the context isn't visible then immediately set
                // the fadeout class and don't worry about transitioning, as the context
                // would never have been in view while docked
                if (morphToDocked && !isVisible) {
                    Classes.add(component.element, [contextInfo.fadeOutClass]);
                    contextInfo.onHide(component);
                }
                else {
                    const method = isVisible ? Dockables.appear : Dockables.disappear;
                    method(component, contextInfo);
                }
            }
        });
    });
};
const applyFixedMorph = (component, config, state, viewport, morph) => {
    // This "updateVisibility" call is potentially duplicated with the
    // call in refreshInternal for isDocked. We might want to consolidate them.
    // The difference between them is the "morphToDocked" flag.
    updateVisibility(component, config, state, viewport, true);
    morphToCoord(component, config, state, morph.positionCss);
};
const applyMorph = (component, config, state, viewport, morph) => {
    // Apply the morph result depending on its type
    switch (morph.morph) {
        case 'static': {
            return morphToStatic(component, config, state);
        }
        case 'absolute': {
            return morphToCoord(component, config, state, morph.positionCss);
        }
        case 'fixed': {
            return applyFixedMorph(component, config, state, viewport, morph);
        }
    }
};
const refreshInternal = (component, config, state) => {
    // Absolute coordinates (considers scroll)
    const viewport = config.lazyViewport(component);
    updateVisibility(component, config, state, viewport);
    Dockables.tryMorph(component, viewport, state).each((morph) => {
        applyMorph(component, config, state, viewport, morph);
    });
};
const resetInternal = (component, config, state) => {
    // Morph back to the original position
    const elem = component.element;
    state.setDocked(false);
    const viewport = config.lazyViewport(component);
    Dockables.calculateMorphToOriginal(component, viewport, state).each((staticOrAbsoluteMorph) => {
        // This code is very similar to the "applyMorph" function above. The main difference
        // is that it doesn't consider fixed position, because something that is docking
        // can't currently start with fixed position
        switch (staticOrAbsoluteMorph.morph) {
            case 'static': {
                morphToStatic(component, config, state);
                break;
            }
            case 'absolute': {
                morphToCoord(component, config, state, staticOrAbsoluteMorph.positionCss);
                break;
            }
            default:
        }
    });
    // Remove contextual visibility classes
    state.setVisible(true);
    config.contextual.each((contextInfo) => {
        Classes.remove(elem, [contextInfo.fadeInClass, contextInfo.fadeOutClass, contextInfo.transitionClass]);
        contextInfo.onShow(component);
    });
    // Apply docking again to reset the position
    refresh(component, config, state);
};
const refresh = (component, config, state) => {
    // Ensure the component is attached to the document/world, if not then do nothing as we can't
    // check if the component should be docked or not when in a detached state
    if (component.getSystem().isConnected()) {
        refreshInternal(component, config, state);
    }
};
const reset = (component, config, state) => {
    // If the component is not docked then there's no need to reset the state,
    // so only reset when docked
    if (state.isDocked()) {
        resetInternal(component, config, state);
    }
};
const forceDockWithDecision = (getDecision) => (component, config, state) => {
    const viewport = config.lazyViewport(component);
    const optMorph = Dockables.forceDockWith(component.element, viewport, state, getDecision);
    optMorph.each((morph) => {
        // ASSUMPTION: This "applyFixedMorph" sets state.setDocked to true.
        applyFixedMorph(component, config, state, viewport, morph);
    });
};
const forceDockToTop = forceDockWithDecision(Dockables.forceTopPosition);
const forceDockToBottom = forceDockWithDecision(Dockables.forceBottomPosition);
const isDocked = (component, config, state) => state.isDocked();
const setModes = (component, config, state, modes) => state.setModes(modes);
const getModes = (component, config, state) => state.getModes();
export { refresh, reset, isDocked, getModes, setModes, forceDockToTop, forceDockToBottom };
//# sourceMappingURL=DockingApis.js.map