/**
 * Internal class that manages drag/drop for the `Dashboard`.
 * @private
 */
Ext.define('Ext.dashboard.DropZone', {
    extend: 'Ext.dd.DropTarget',
 
    ddScrollConfig: {
        vthresh: 75,
        hthresh: -1,
        animate: true,
        increment: 200
    },
 
    containerScroll: true,
 
    // This causes overflow to go hidden during the drag so that we don't cause panels to
    // wrap by triggering overflow.
    overClass: Ext.baseCSSPrefix + 'dashboard-dd-over',
 
    constructor: function(dashboard, cfg) {
        this.dashboard = dashboard;
        dashboard.body.ddScrollConfig = this.ddScrollConfig;
 
        this.callParent([dashboard.body, cfg]);
    },
 
    getOverEvent: function(dd, e, data) {
        var dashboard = this.dashboard,
            dbody = dashboard.body,
            items = dashboard.items.items,
            bodyBox = dbody.getBox(),
            count = items.length,
            xy = e.getXY(),
            orginalX = xy[0] - bodyBox.x + dbody.getScrollLeft(),
            orginalY = xy[1] - bodyBox.y + dbody.getScrollTop(),
            x = orginalX,
            y = orginalY,
            yOffset = 0,
            rowIterationIndex = 0,
            over = {
                columnIndex: 0,
                column: null,
                dashboard: dashboard,
                above: null,
                extensible: false,
                beforeAfter: 0,
                data: data,
                panel: data.panel,
                rawEvent: e,
                source: dd,
                status: this.dropAllowed
            },
            t, ht, i, k, item, w, h, childCount, childItems, childItem;
 
        for (= 0; i < count; i += 2) {
            item = items[i];
 
            if (rowIterationIndex !== item.rowIndex) {
                rowIterationIndex = item.rowIndex;
                x = orginalX;
 
                if (rowIterationIndex > 0) {
                    y -= yOffset;
                    yOffset = 0;
                }
            }
 
            w = item.lastBox.width;
            h = item.lastBox.height;
 
            if (yOffset <= h) {
                yOffset = h;
            }
            
            if (items[+ 1]) {
                // This is for splitter
                w += items[+ 1].lastBox.width;
            }
 
            if (e.within(item.el)) {
                over.columnIndex = i;
                over.column = item;
                over.extensible = this.isRowExtensible(item.rowIndex);
 
                t = Math.min(80, w * 0.2);
                over.beforeAfter = t = (over.extensible && ((< t) ? -1 : ((> w - t) ? 1 : 0)));
 
                if (!|| !over.extensible) {
                    childItems = item.items.items;
                    
                    // if we are not on an edge OR reached maxColumns
                    // (which means "insert the panel in between the columns"),
                    // we need to dig one more level down
                    for (= 0, childCount = childItems.length; k < childCount; ++k) {
                        childItem = childItems[k];
                        ht = childItem.el.getHeight();
                        
                        if (< ht / 2) {
                            // if mouse is above the current child's top, Y coord, it
                            // is considered as "above" the previous child
                            over.above = childItem;
                            
                            break;
                        }
                        
                        y -= ht;
                    }
 
                }
 
                break;
            }
 
            x -= w;
        }
 
        return over;
    },
 
    notifyOver: function(dd, e, data) {
        var me = this,
            dashboard = me.dashboard,
            hasListeners = dashboard.hasListeners,
            over = me.getOverEvent(dd, e, data),
            colEl = over.column && over.column.el,
            proxy = dd.proxy,
            aboveItem = over.above,
            width = 0,
            proxyProxy, colWidth, padding;
 
        data.lastOver = over;
 
        if ((!hasListeners.validatedrop || dashboard.fireEvent('validatedrop', over) !== false) &&
            (!hasListeners.beforedragover || dashboard.fireEvent('beforedragover', over) !== false)) { // eslint-disable-line max-len
            proxyProxy = dd.panelProxy.getProxy();
 
            // make sure proxy width is fluid in different width columns
            proxy.getProxy().setWidth('auto');
 
            if (colEl) {
                width = colWidth = colEl.getWidth();
                
                // A floating column was targeted
                if (over.beforeAfter) {
 
                    dd.panelProxy.moveProxy(colEl.dom, colEl.dom.firstChild);
 
                    width = colWidth / 2;
                    proxyProxy.setWidth(width);
                }
                else {
                    if (aboveItem) {
                        dd.panelProxy.moveProxy(aboveItem.el.dom.parentNode, aboveItem.el.dom);
                    }
                    else {
                        dd.panelProxy.moveProxy(colEl.dom, null);
                    }
                    
                    proxyProxy.setWidth('auto');
 
                }
                
                proxyProxy.setStyle({
                    'float': 'none',
                    'clear': 'none',
                    'margin-left': (over.beforeAfter > 0)
                        ? (colWidth - width - colEl.getPadding('lr')) + 'px'
                        : '',
                    'margin-top': '7px'
                });
            }
            else {
                padding = dashboard.body.getPadding('lr');
                
                proxyProxy.setStyle({
                    'float': 'left',
                    'clear': 'left',
                    'margin': '0 7px 0 7px'
                });
                
                proxyProxy.setWidth(dashboard.body.getWidth() - padding);
 
                // Target the innerCt for the move
                dd.panelProxy.moveProxy(dashboard.body.dom.firstChild.firstChild, null);
            }
 
            this.scrollPos = dashboard.body.getScroll();
 
            if (hasListeners.dragover) {
                dashboard.fireEvent('dragover', over);
            }
        }
 
        return over.status;
    },
 
    isRowExtensible: function(rowIndex) {
        var me = this,
            dashboard = me.dashboard,
            maxColumns = dashboard.getMaxColumns() || 1,
            items;
        
        items = dashboard.query('>dashboard-column[rowIndex=' + rowIndex + ']');
 
        return Ext.Array.from(items).length < maxColumns;
    },
 
    notifyDrop: function(dd, e, data) {
        this.callParent(arguments);
 
        // eslint-disable-next-line vars-on-top
        var dashboard = this.dashboard,
            over = data.lastOver,
            panel = over.panel,
            fromCt = panel.ownerCt,
            toCt = over.column,
            side = toCt ? over.beforeAfter : 1,
            currentIndex = fromCt.items.indexOf(panel),
            newIndex = toCt
                ? (over.above ? toCt.items.indexOf(over.above) : toCt.items.getCount())
                : 0,
            colIndex, newCol,
            hasListeners = dashboard.hasListeners;
 
        // Same column tests
        if (fromCt === toCt) {
            if (fromCt.items.getCount() === 1) {
                return;
            }
            
            if (!side) {
                if (currentIndex < newIndex) {
                    --newIndex;
                }
                
                if (currentIndex === newIndex) {
                    return;
                }
            }
        }
 
        if ((hasListeners.validatedrop && dashboard.fireEvent('validatedrop', over) === false) ||
            (hasListeners.beforedrop && dashboard.fireEvent('beforedrop', over) === false)) {
            return;
        }
 
        Ext.suspendLayouts();
 
        panel.isMoving = true;
        
        if (side) {
            colIndex = dashboard.items.indexOf(toCt);
 
            // inserting into new Row ?
            if (colIndex < 0) {
                colIndex = dashboard.items.getCount();
            }
            else if (side > 0) {
                ++colIndex;
            }
 
            newCol = dashboard.createColumn();
 
            if (toCt) {
                newCol.columnWidth = toCt.columnWidth = toCt.columnWidth / 2;
                delete toCt.width;
            }
            else {
                newCol.columnWidth = 1; // full row
            }
 
            toCt = dashboard.insert(colIndex, newCol);
            newIndex = 0;
        }
 
        // make sure panel is visible prior to inserting so the layout doesn't ignore it
        panel.el.dom.style.display = '';
 
        toCt.insert(newIndex, panel);
 
        panel.isMoving = false;
 
        toCt.updateLayout();
        Ext.resumeLayouts(true);
 
        if (hasListeners.drop) {
            dashboard.fireEvent('drop', over);
        }
    }
});