import { __extends, __spreadArrays } from "tslib";
import { getVNodeAttributeValue } from '../../utils/vue/directive';
import { dispatchEvent } from '../../utils/vue/events';
import { SORTABLE_NAME } from '../directive-names';
import { MDOMPlugin, MElementDomPlugin } from '../domPlugin';
import DragAndDropPlugin from '../drag-and-drop/drag-and-drop-plugin';
import { MDraggable, MDraggableEventNames } from '../drag-and-drop/draggable/draggable';
import { MDroppable, MDroppableEventNames } from '../drag-and-drop/droppable/droppable';
import { MDroppableGroup } from '../drag-and-drop/droppable/droppable-group';
import { MSortableDefaultInsertionMarkerBehavior } from './insertion-behavior';
import './sortable.scss';
export var MSortableEventNames;
(function (MSortableEventNames) {
    MSortableEventNames["OnAdd"] = "sortable:add";
    MSortableEventNames["OnMove"] = "sortable:move";
    MSortableEventNames["OnRemove"] = "sortable:remove";
})(MSortableEventNames || (MSortableEventNames = {}));
export var MSortInsertPositions;
(function (MSortInsertPositions) {
    MSortInsertPositions["Before"] = "before";
    MSortInsertPositions["In"] = "in";
    MSortInsertPositions["After"] = "after";
})(MSortInsertPositions || (MSortInsertPositions = {}));
export var MSortableClassNames;
(function (MSortableClassNames) {
    MSortableClassNames["Sortable"] = "m--is-sortable";
    MSortableClassNames["CantSort"] = "m--cant-sort";
    MSortableClassNames["SortBefore"] = "m--is-sortbefore";
    MSortableClassNames["SortIn"] = "m--is-sortin";
    MSortableClassNames["SortAfter"] = "m--is-sortafter";
    MSortableClassNames["EmptyPlaceholder"] = "m--is-sortable-placeholder";
})(MSortableClassNames || (MSortableClassNames = {}));
export var MSortableAction;
(function (MSortableAction) {
    MSortableAction["Default"] = "any";
    MSortableAction["Move"] = "move";
    MSortableAction["MoveGroup"] = "move_group";
})(MSortableAction || (MSortableAction = {}));
var MSortable = /** @class */ (function (_super) {
    __extends(MSortable, _super);
    function MSortable(element, options) {
        return _super.call(this, element, options) || this;
    }
    MSortable.prototype.doCleanUp = function () {
        this.cleanUpInsertionClasses();
    };
    MSortable.prototype.attach = function (mount) {
        var _this = this;
        this.observer = new MutationObserver(function (mutations) { return _this.manageMutation(mutations); });
        this.observer.observe(this.element, { childList: true });
        this.setOptions(this.options);
        this.attachChilds();
        if (this.options.canSort) {
            mount(function () {
                _this.element.classList.add(MSortableClassNames.Sortable);
                var plugin = MDOMPlugin.attach(MDroppable, _this.element, {
                    acceptedActions: _this.options.acceptedActions,
                    canDrop: true
                });
                plugin.addEventListener(MDroppableEventNames.OnDrop, function (event) { return _this.onDrop(event); });
                plugin.addEventListener(MDroppableEventNames.OnDragEnter, function (event) { return _this.onDragEnter(event); });
                plugin.addEventListener(MDroppableEventNames.OnDragLeave, function (event) { return _this.onDragLeave(event); });
                plugin.addEventListener(MDroppableEventNames.OnDragOver, function (event) { return _this.onDragOver(event); });
            });
        }
    };
    MSortable.prototype.update = function (options, refresh) {
        var _this = this;
        this.setOptions(options);
        if (this.options.canSort) {
            refresh(function () { return _this.attachChilds(); });
        }
    };
    MSortable.prototype.detach = function () {
        this.observer.disconnect();
        this.element.classList.remove(MSortableClassNames.Sortable);
        this.doCleanUp();
        this.detachChilds();
        MDOMPlugin.detach(MDroppable, this.element);
    };
    MSortable.prototype.manageMutation = function (mutations) {
        var refreshChild = false;
        mutations.forEach(function (mutation) {
            // when an item leave the sortable we have to clean it up.
            if (mutation.type === 'childList') {
                if (mutation.removedNodes.length) {
                    for (var i = 0; i < mutation.removedNodes.length; i++) {
                        var currentElement = mutation.removedNodes[i];
                        MDOMPlugin.detach(MDraggable, currentElement);
                        MDOMPlugin.detach(MDroppable, currentElement);
                    }
                }
                if (mutation.addedNodes) {
                    refreshChild = true;
                }
            }
        });
        if (refreshChild) {
            this.attachChilds();
        }
    };
    MSortable.prototype.setOptions = function (value) {
        if (value.canSort === undefined) {
            value.canSort = true;
        }
        if (!value.items) {
            value.items = [];
        }
        var sortableGroup = MDOMPlugin.getRecursive(MDroppableGroup, this.element);
        var acceptedActions;
        if (!value.acceptedActions) {
            acceptedActions = [MSortableAction.Default];
        }
        else {
            acceptedActions = __spreadArrays(value.acceptedActions);
        }
        acceptedActions.push(MSortableAction.Move);
        if (!sortableGroup) {
            acceptedActions.push(MSortableAction.MoveGroup);
        }
        this._options = value;
        this._options.acceptedActions = acceptedActions;
    };
    MSortable.prototype.canDrag = function (element) {
        var childDragValue = element.attributes.getNamedItem('can-drag');
        var canDragValue;
        if (!this.options.canSort) {
            canDragValue = false;
        }
        else if (childDragValue && childDragValue.value === 'false') {
            canDragValue = false;
        }
        else if (childDragValue && childDragValue.value === 'true') {
            canDragValue = true;
        }
        else {
            canDragValue = this.options.canSort;
        }
        return canDragValue;
    };
    MSortable.prototype.canDrop = function (element) {
        var childDropValue = element.attributes.getNamedItem('can-drop');
        var canDropValue;
        if (!this.options.canSort) {
            canDropValue = false;
        }
        else if (childDropValue && childDropValue.value === 'false') {
            canDropValue = false;
        }
        else if (childDropValue && childDropValue.value === 'true') {
            canDropValue = true;
        }
        else {
            canDropValue = this.options.canSort;
        }
        return canDropValue;
    };
    MSortable.prototype.attachChilds = function () {
        var _this = this;
        var itemCounter = 0;
        var sortableGroup = MDOMPlugin.getRecursive(MDroppableGroup, this.element);
        for (var i = 0; i < this.element.children.length; i++) {
            var currentElement = this.element.children[i];
            if (currentElement.classList.contains(MSortableClassNames.EmptyPlaceholder)) {
                this.attachEmptyPlaceholder(currentElement, sortableGroup ? sortableGroup.options : undefined);
            }
            else {
                var draggableGroup = MDOMPlugin.get(MDroppableGroup, currentElement);
                var grouping = !sortableGroup ? draggableGroup ? draggableGroup.options : undefined : undefined;
                var draggablePlugin = MDOMPlugin.attach(MDraggable, currentElement, {
                    action: !grouping ? MSortableAction.Move : MSortableAction.MoveGroup,
                    dragData: this.options.items[itemCounter++],
                    grouping: grouping,
                    canDrag: this.canDrag(currentElement)
                });
                draggablePlugin.removeEventListener(MDraggableEventNames.OnDragEnd);
                draggablePlugin.removeEventListener(MDraggableEventNames.OnDragStart);
                draggablePlugin.addEventListener(MDraggableEventNames.OnDragEnd, function (event) { return _this.onChildDragEnd(event); });
                draggablePlugin.addEventListener(MDraggableEventNames.OnDragStart, function (event) { return _this.onChildDragStart(event); });
                MDOMPlugin.attach(MDroppable, currentElement, {
                    acceptedActions: this.options.acceptedActions,
                    canDrop: this.canDrop(currentElement),
                    alwaysMount: true
                });
            }
        }
    };
    MSortable.prototype.detachChilds = function () {
        for (var i = 0; i < this.element.children.length; i++) {
            var currentElement = this.element.children[i];
            MDOMPlugin.detach(MDraggable, currentElement);
            MDOMPlugin.detach(MDroppable, currentElement);
        }
    };
    MSortable.prototype.attachEmptyPlaceholder = function (element, grouping) {
        if (element) {
            MDOMPlugin.attach(MDroppable, element, {
                acceptedActions: this.options.acceptedActions,
                canDrop: this.options.canSort
            });
        }
        this.emptyPlaceHolderElement = element;
        if (this.emptyPlaceHolderElement) {
            this.emptyPlaceHolderElement.style.display = this.options.items.length ? 'none' : '';
        }
    };
    MSortable.prototype.onDragEnter = function (event) {
        this.onDragIn(event);
    };
    MSortable.prototype.onDragLeave = function (event) {
        event.stopPropagation();
        var newContainer = MDroppable.currentHoverDroppable ? MDOMPlugin.getRecursive(MSortable, MDroppable.currentHoverDroppable.element) : undefined;
        if (!newContainer) {
            this.doCleanUp();
            MSortable.activeSortContainer = undefined;
        }
        if (newContainer && newContainer !== this) {
            this.doCleanUp();
        }
    };
    MSortable.prototype.onDragOver = function (event) {
        this.onDragIn(event);
    };
    MSortable.prototype.onDragIn = function (event) {
        event.stopPropagation();
        if (MSortable.activeSortContainer && MSortable.activeSortContainer !== this) {
            MSortable.activeSortContainer.doCleanUp();
        }
        MSortable.activeSortContainer = this;
        this.insertInsertionMarker(event);
    };
    MSortable.prototype.onDrop = function (event) {
        event.stopPropagation();
        var oldIndex = this.options.items.findIndex(function (item) { return item === event.dropInfo.data; });
        var isMoving = oldIndex !== -1 || event.dropInfo.action === MSortableAction.Move || event.dropInfo.action === MSortableAction.MoveGroup;
        var eventName;
        if (isMoving) {
            eventName = MSortableEventNames.OnMove;
        }
        else {
            eventName = MSortableEventNames.OnAdd;
        }
        var sortInfo = {
            canDrop: event.dropInfo.canDrop,
            data: event.dropInfo.data,
            action: event.dropInfo.action,
            oldPosition: oldIndex,
            newPosition: this.getNewPosition(event, oldIndex),
            grouping: event.dropInfo.grouping
        };
        var changed = sortInfo.oldPosition !== sortInfo.newPosition;
        if (!changed) {
            return;
        }
        var customEvent = document.createEvent('CustomEvent');
        customEvent.initCustomEvent(eventName, true, true, Object.assign(event, { sortInfo: sortInfo }));
        customEvent.sortInfo = sortInfo;
        dispatchEvent(this.element, eventName, customEvent);
        if (MSortable.fromSortContainer && MSortable.fromSortContainer !== MSortable.activeSortContainer) {
            MSortable.fromSortContainer.onRemove(event);
        }
        // TODO: review => Verify this line is useful.
        this.onChildDragEnd(event);
    };
    MSortable.prototype.onRemove = function (event) {
        var oldIndex = this.options.items.findIndex(function (item) { return item === event.dropInfo.data; });
        if (oldIndex !== -1) {
            var sortInfo = Object.assign(event.dropInfo, { oldPosition: oldIndex, newPosition: -1 });
            var customEvent = document.createEvent('CustomEvent');
            customEvent.initCustomEvent(MSortableEventNames.OnRemove, true, true, Object.assign(event, { sortInfo: sortInfo }));
            customEvent.sortInfo = sortInfo;
            dispatchEvent(this.element, MSortableEventNames.OnRemove, customEvent);
        }
    };
    MSortable.prototype.onChildDragEnd = function (event) {
        event.stopPropagation();
        if (MSortable.fromSortContainer) {
            MSortable.fromSortContainer.doCleanUp();
        }
        if (MSortable.activeSortContainer) {
            MSortable.activeSortContainer.doCleanUp();
        }
        this.doCleanUp();
        MSortable.fromSortContainer = undefined;
        MSortable.activeSortContainer = undefined;
    };
    MSortable.prototype.onChildDragStart = function (event) {
        event.stopPropagation();
        MSortable.fromSortContainer = this;
    };
    MSortable.prototype.insertInsertionMarker = function (event) {
        // Fix for firefox flickering.
        if (event.detail.target.nodeType === Node.TEXT_NODE) {
            return;
        }
        if (!MDroppable.currentHoverDroppable || !event.dropInfo.canDrop || this.isHoveringOverDraggedElement()) {
            this.doCleanUp();
            return;
        }
        var currentInsertPosition = this.getCurrentInsertPosition();
        var newInsertPosition = this.getInsertionMarkerBehavior().getInsertPosition(event);
        if (MDroppable.currentHoverDroppable === MDroppable.previousHoverContainer && currentInsertPosition === newInsertPosition) {
            return;
        }
        var element;
        if (!this.isInsertingOnChild()) {
            element = newInsertPosition === MSortInsertPositions.Before
                ? this.element.children[0] : this.element.children[this.element.children.length - 1];
        }
        else {
            element = MDroppable.currentHoverDroppable.element;
        }
        this.doCleanUp();
        var insertionClass;
        switch (newInsertPosition) {
            case MSortInsertPositions.After:
                insertionClass = MSortableClassNames.SortAfter;
                break;
            case MSortInsertPositions.Before:
                insertionClass = MSortableClassNames.SortBefore;
                break;
            case MSortInsertPositions.In:
                insertionClass = MSortableClassNames.SortIn;
                break;
        }
        var isSortingOnPreviousSibling = !!MDroppable.currentHoverDroppable &&
            !!MDraggable.currentDraggable &&
            MDroppable.currentHoverDroppable.element === MDraggable.currentDraggable.element.previousElementSibling &&
            newInsertPosition === MSortInsertPositions.After;
        var isSortingOnNextSibling = !!MDroppable.currentHoverDroppable &&
            !!MDraggable.currentDraggable &&
            MDroppable.currentHoverDroppable.element === MDraggable.currentDraggable.element.nextElementSibling &&
            newInsertPosition === MSortInsertPositions.Before;
        if (insertionClass) {
            element.classList.add(insertionClass);
            if (isSortingOnPreviousSibling || isSortingOnNextSibling) {
                element.classList.add(MSortableClassNames.CantSort);
            }
        }
    };
    MSortable.prototype.getInsertionMarkerBehavior = function () {
        /*const droppable: MDroppable = MDroppable.currentHoverDroppable as MDroppable;

        if (MDOMPlugin.get(MDroppableGroup, droppable.element)) {
            return new MSortableGroupingInsertionMarkerBehavior();
        }
*/
        return new MSortableDefaultInsertionMarkerBehavior();
    };
    MSortable.prototype.getNewPosition = function (event, oldPosition) {
        var index = 0;
        for (var i = 0; i < this.element.children.length; i++) {
            var child = this.element.children[i];
            if (child.classList.contains(MSortableClassNames.EmptyPlaceholder)) {
                index--;
            }
            if (child.classList.contains(MSortableClassNames.SortBefore)) {
                break;
            }
            if (child.classList.contains(MSortableClassNames.SortAfter)) {
                index++;
                break;
            }
            index++;
        }
        var newIndex = index < 0 ? 0 : index;
        if (oldPosition !== -1 && oldPosition < newIndex) {
            newIndex--;
        }
        return newIndex;
    };
    MSortable.prototype.isHoveringOverDraggedElement = function () {
        return MDraggable.currentDraggable !== undefined && MDroppable.currentHoverDroppable !== undefined
            && MDroppable.currentHoverDroppable.element === MDraggable.currentDraggable.element;
    };
    MSortable.prototype.isInsertingOnChild = function () {
        return MDroppable.currentHoverDroppable !== undefined && MDroppable.currentHoverDroppable.element !== this.element;
    };
    MSortable.prototype.hasItems = function () {
        return this.options.items.length > 0;
    };
    MSortable.prototype.getCurrentInsertPosition = function () {
        var mSortAfterElement = this.element.querySelector("." + MSortableClassNames.SortAfter);
        if (mSortAfterElement) {
            return MSortInsertPositions.After;
        }
        var mSortBeforeElement = this.element.querySelector("." + MSortableClassNames.SortBefore);
        if (mSortBeforeElement) {
            return MSortInsertPositions.Before;
        }
        var mSortInElement = this.element.querySelector("." + MSortableClassNames.SortIn);
        if (mSortInElement) {
            return MSortInsertPositions.In;
        }
        return MSortInsertPositions.After;
    };
    MSortable.prototype.cleanUpInsertionClasses = function () {
        var mSortAfterElement = this.element.querySelector("." + MSortableClassNames.SortAfter);
        if (mSortAfterElement) {
            mSortAfterElement.classList.remove(MSortableClassNames.SortAfter);
        }
        this.element.classList.remove(MSortableClassNames.SortAfter);
        var mSortInElement = this.element.querySelector("." + MSortableClassNames.SortIn);
        if (mSortInElement) {
            mSortInElement.classList.remove(MSortableClassNames.SortIn);
        }
        this.element.classList.remove(MSortableClassNames.SortIn);
        var mSortBeforeElement = this.element.querySelector("." + MSortableClassNames.SortBefore);
        if (mSortBeforeElement) {
            mSortBeforeElement.classList.remove(MSortableClassNames.SortBefore);
        }
        this.element.classList.remove(MSortableClassNames.SortBefore);
        this.element.classList.remove(MSortableClassNames.CantSort);
        var mCantSort = this.element.querySelector("." + MSortableClassNames.CantSort);
        if (mCantSort) {
            mCantSort.classList.remove(MSortableClassNames.CantSort);
        }
    };
    MSortable.defaultMountPoint = '__msortable__';
    return MSortable;
}(MElementDomPlugin));
export { MSortable };
var extractVnodeAttributes = function (binding, node) {
    return {
        items: getVNodeAttributeValue(node, 'items'),
        acceptedActions: getVNodeAttributeValue(node, 'accepted-actions'),
        canSort: binding.value,
        encapsulate: (binding.modifiers || {}).encapsulate || false
    };
};
export var SortableDirective = {
    inserted: function (element, binding, node) {
        MDOMPlugin.attach(MSortable, element, extractVnodeAttributes(binding, node));
    },
    update: function (element, binding, node) {
        MDOMPlugin.attach(MSortable, element, extractVnodeAttributes(binding, node));
    },
    unbind: function (element) {
        MDOMPlugin.detach(MSortable, element);
    }
};
var SortablePlugin = {
    install: function (v, _options) {
        v.use(DragAndDropPlugin);
        v.directive(SORTABLE_NAME, SortableDirective);
    }
};
export default SortablePlugin;
//# sourceMappingURL=sortable.js.map