/**
 * @private
 */
Ext.define('Ext.grid.plugin.grouping.DropZone', {
    extend: 'Ext.drag.Target',
 
    groups: 'gridgrouping',
 
    upIndicatorCls: Ext.baseCSSPrefix + 'grid-drag-up-indicator',
    downIndicatorCls: Ext.baseCSSPrefix + 'grid-drag-down-indicator',
    columnCls: Ext.baseCSSPrefix + 'grid-group-column',
    // panelCls: Ext.baseCSSPrefix + 'grid-group-panel-body',
    panelCls: Ext.baseCSSPrefix + 'panel-body-wrap-el',
 
    constructor: function(panel) {
        var me = this;
 
        me.columnSelector = '.' + me.columnCls;
        me.panelSelector = '.' + me.panelCls;
        me.panel = panel;
 
        me.callParent([{
            element: panel.element
        }]);
    },
 
    destroy: function() {
        var me = this;
 
        Ext.destroy(me.upIndicator, me.downIndicator);
        me.callParent();
    },
 
    getUpIndicator: function() {
        var me = this;
 
        if (!me.upIndicator) {
            me.upIndicator = Ext.getBody().createChild({
                cls: me.upIndicatorCls,
                html: " "
            });
            // eslint-disable-next-line max-len
            me.self.prototype.indicatorOffset = Math.floor((me.upIndicator.dom.offsetWidth + 1) / 2);
        }
 
        return me.upIndicator;
    },
 
    getDownIndicator: function() {
        var me = this;
 
        if (!me.downIndicator) {
            me.downIndicator = Ext.getBody().createChild({
                cls: me.downIndicatorCls,
                html: " "
            });
        }
 
        return me.downIndicator;
    },
 
    accepts: function(info) {
        var column = info.data.column;
 
        if (!column) {
            return true;
        }
 
        return column.isGroupingPanelColumn || (column.isGridColumn && column.getGroupable());
    },
 
    onDragMove: function(info) {
        if (info.valid) {
            this.positionIndicator(info);
        }
        else {
            this.hideIndicators();
        }
    },
 
    onDragLeave: function(info) {
        this.hideIndicators();
    },
 
    onDrop: function(info) {
        var data = info.data,
            column = data.column,
            source = data.sourcePanel,
            index = data.index,
            panel = this.panel;
 
        this.hideIndicators();
 
        if (!panel) {
            return;
        }
 
        panel.dragDropColumn(source, column, index);
    },
 
    getLocationPosition: function(x, target) {
        var pos = 'after',
            region = target && target.element.getRegion();
 
        if (region) {
            if ((region.right - x) <= (region.right - region.left) / 2) {
                pos = 'after';
            }
            else {
                pos = 'before';
            }
        }
 
        return pos;
    },
 
    positionIndicator: function(info) {
        var me = this,
            items = me.panel.getItems(),
            upIndicator = me.getUpIndicator(),
            downIndicator = me.getDownIndicator(),
            indOffset = me.indicatorOffset,
            index = -1,
            el, item, topXY, downXY, minX, maxX,
            topAnchor, downAnchor, pos;
 
        el = me.getCursorElement(info, me.columnCls, me.columnSelector);
 
        if (el) {
            item = el.component;
        }
        else {
            item = items.last();
 
            if (item) {
                el = item.element;
            }
            else {
                el = me.getCursorElement(info, me.panelCls, me.panelSelector);
                index = 0;
            }
        }
 
        if (!el) {
            el = me.panel.bodyWrapElement;
        }
 
        if (items.length === 0) {
            pos = 'before';
        }
        else {
            pos = me.getLocationPosition(info.cursor.current.x, item);
        }
 
        if (pos === 'before') {
            topAnchor = 'bc-tl';
            downAnchor = 'tc-bl';
        }
        else {
            topAnchor = 'bc-tr';
            downAnchor = 'tc-br';
        }
 
        topXY = upIndicator.getAlignToXY(el, topAnchor);
        downXY = downIndicator.getAlignToXY(el, downAnchor);
 
        minX = el.getX() - indOffset;
        maxX = el.getX() + el.getWidth();
        topXY[0] = Ext.Number.constrain(topXY[0], minX, maxX);
        downXY[0] = Ext.Number.constrain(downXY[0], minX, maxX);
 
        upIndicator.show();
        downIndicator.show();
        upIndicator.setXY(topXY);
        downIndicator.setXY(downXY);
 
        if (index < 0) {
            index = me.findNewIndex(item, pos, info);
        }
 
        info.setData('position', pos);
        info.setData('index', Ext.Number.constrain(index, 0, items.length));
    },
 
    findNewIndex: function(overItem, pos, info) {
        var items = this.panel.getItems(),
            index = items.indexOf(overItem);
 
        if (overItem !== info.data.column) {
            if (pos === 'after') {
                index++;
            }
            else if (index !== items.length - 1) {
                index--;
            }
        }
 
        return index;
    },
 
    hideIndicators: function() {
        this.getUpIndicator().hide();
        this.getDownIndicator().hide();
    },
 
    /**
     * Find the element that matches the cursor position and selector.
     *
     * @param info
     * @param {String} cls The class used in the selector
     * @param {String} selector The simple selector to test. See {@link Ext.dom.Query}
     * for information about simple selectors.
     * @param {Number/String/HTMLElement/Ext.dom.Element} [limit]
     * The max depth to search as a number or an element that causes the upward
     * traversal to stop and is **not** considered for inclusion as the result.
     * (defaults to 50 || document.documentElement)
     * @param {Boolean} [returnDom=false] True to return the DOM node instead of Ext.dom.Element
     * @return {Ext.dom.Element/HTMLElement} The matching DOM node (or HTMLElement if
     * _returnDom_ is _true_).  Or null if no match was found.
     */
    getCursorElement: function(info, cls, selector, limit, returnDom) {
        var pos = info.cursor.current,
            elPoint = Ext.drag.Manager.elementFromPoint(pos.x, pos.y),
            el = Ext.fly(elPoint);
 
        return el && el.hasCls(cls) ? el : (el && el.up(selector, limit, returnDom));
    }
});