import { Adt, Arr, Obj, Optional, Optionals } from '@ephox/katamari';
import { Class, Css, Height, SugarBody, Width } from '@ephox/sugar';
import * as Boxes from '../../alien/Boxes';
import * as OffsetOrigin from '../../alien/OffsetOrigin';
import { NuPositionCss } from '../../positioning/view/PositionCss';
const morphAdt = Adt.generate([
    { static: [] },
    { absolute: ['positionCss'] },
    { fixed: ['positionCss'] }
]);
const appear = (component, contextualInfo) => {
    const elem = component.element;
    Class.add(elem, contextualInfo.transitionClass);
    Class.remove(elem, contextualInfo.fadeOutClass);
    Class.add(elem, contextualInfo.fadeInClass);
    contextualInfo.onShow(component);
};
const disappear = (component, contextualInfo) => {
    const elem = component.element;
    Class.add(elem, contextualInfo.transitionClass);
    Class.remove(elem, contextualInfo.fadeInClass);
    Class.add(elem, contextualInfo.fadeOutClass);
    contextualInfo.onHide(component);
};
const isPartiallyVisible = (box, viewport) => box.y < viewport.bottom && box.bottom > viewport.y;
const isTopCompletelyVisible = (box, viewport) => box.y >= viewport.y;
const isBottomCompletelyVisible = (box, viewport) => box.bottom <= viewport.bottom;
const isVisibleForModes = (modes, box, viewport) => Arr.forall(modes, (mode) => {
    switch (mode) {
        case 'bottom':
            return isBottomCompletelyVisible(box, viewport);
        case 'top':
            return isTopCompletelyVisible(box, viewport);
    }
});
const getPrior = (elem, state) => state.getInitialPos().map(
// Only supports position absolute.
(pos) => Boxes.bounds(pos.bounds.x, pos.bounds.y, Width.get(elem), Height.get(elem)));
const storePrior = (elem, box, state) => {
    state.setInitialPos({
        style: Css.getAllRaw(elem),
        position: Css.get(elem, 'position') || 'static',
        bounds: box
    });
};
const revertToOriginal = (elem, box, state) => state.getInitialPos().bind((position) => {
    state.clearInitialPos();
    switch (position.position) {
        case 'static':
            return Optional.some(morphAdt.static());
        case 'absolute':
            const offsetBox = OffsetOrigin.getOffsetParent(elem).map(Boxes.box)
                .getOrThunk(() => Boxes.box(SugarBody.body()));
            return Optional.some(morphAdt.absolute(NuPositionCss('absolute', Obj.get(position.style, 'left').map((_left) => box.x - offsetBox.x), Obj.get(position.style, 'top').map((_top) => box.y - offsetBox.y), Obj.get(position.style, 'right').map((_right) => offsetBox.right - box.right), Obj.get(position.style, 'bottom').map((_bottom) => offsetBox.bottom - box.bottom))));
        default:
            return Optional.none();
    }
});
const morphToOriginal = (elem, viewport, state) => getPrior(elem, state)
    .filter((box) => isVisibleForModes(state.getModes(), box, viewport))
    .bind((box) => revertToOriginal(elem, box, state));
const morphToFixed = (elem, viewport, state) => {
    const box = Boxes.box(elem);
    if (!isVisibleForModes(state.getModes(), box, viewport)) {
        storePrior(elem, box, state);
        // Calculate the fixed position
        const winBox = Boxes.win();
        const left = box.x - winBox.x;
        const top = viewport.y - winBox.y;
        const bottom = winBox.bottom - viewport.bottom;
        // Check whether we are docking the bottom of the viewport, or the top
        const isTop = box.y <= viewport.y;
        return Optional.some(morphAdt.fixed(NuPositionCss('fixed', Optional.some(left), isTop ? Optional.some(top) : Optional.none(), Optional.none(), !isTop ? Optional.some(bottom) : Optional.none())));
    }
    else {
        return Optional.none();
    }
};
const getMorph = (component, viewport, state) => {
    const elem = component.element;
    const isDocked = Optionals.is(Css.getRaw(elem, 'position'), 'fixed');
    return isDocked ? morphToOriginal(elem, viewport, state) : morphToFixed(elem, viewport, state);
};
const getMorphToOriginal = (component, state) => {
    const elem = component.element;
    return getPrior(elem, state).bind((box) => revertToOriginal(elem, box, state));
};
export { appear, disappear, isPartiallyVisible, getMorph, getMorphToOriginal };
//# sourceMappingURL=Dockables.js.map