/**
 * This class is used for managing the configurator's panel drop zone.
 *
 * @private
 */
Ext.define('Ext.pivot.plugin.configurator.DropZone', {
    extend: 'Ext.dd.DropZone',
 
    proxyOffsets: [-4, -9],
    configPanelCls: Ext.baseCSSPrefix + 'pivot-grid-config-container-body',
    configColumnCls: Ext.baseCSSPrefix + 'pivot-grid-config-column',
 
    constructor: function(panel) {
        this.panel = panel;
        this.ddGroup = this.getDDGroup();
        this.callParent([panel.id]);
    },
 
    disable: function() {
        this.disabled = true;
    },
 
    enable: function() {
        this.disabled = false;
    },
 
    getDDGroup: function() {
        // return the column header dd group so we can allow column dropping
        // inside the grouping panel
        return 'configurator-' + this.panel.id;
    },
 
    getTargetFromEvent: function(e) {
        return e.getTarget('.' + this.configColumnCls) || e.getTarget('.' + this.configPanelCls);
    },
 
    getTopIndicator: function() {
        if (!this.topIndicator) {
            this.self.prototype.topIndicator = Ext.DomHelper.append(Ext.getBody(), {
                cls: 'col-move-top ' + Ext.baseCSSPrefix + 'col-move-top',
                html: " "
            }, true);
 
            this.self.prototype.indicatorXOffset =
                Math.floor((this.topIndicator.dom.offsetWidth + 1) / 2);
        }
 
        return this.topIndicator;
    },
 
    getBottomIndicator: function() {
        if (!this.bottomIndicator) {
            this.self.prototype.bottomIndicator = Ext.DomHelper.append(Ext.getBody(), {
                cls: 'col-move-bottom ' + Ext.baseCSSPrefix + 'col-move-bottom',
                html: " "
            }, true);
        }
 
        return this.bottomIndicator;
    },
 
    getLocation: function(data, t, e) {
        var x = e.getXY()[0],
            target = Ext.getCmp(t.id),
            region,
            pos;
 
        if (target instanceof Ext.pivot.plugin.configurator.Container) {
            // that means that the column is dragged above the grouping panel so find out
            // if there are any columns already
            if (target.items.getCount() > 0) {
                // now fetch the position of the last item
                region = Ext.fly(target.items.last().el).getRegion();
            }
            else {
                region = new Ext.util.Region(0, 1000000, 0, 0);
            }
        }
        else {
            region = Ext.fly(t).getRegion();
        }
 
        if ((region.right - x) <= (region.right - region.left) / 2) {
            pos = "after";
        }
        else {
            pos = "before";
        }
 
        return data.dropLocation = {
            pos: pos,
            header: Ext.getCmp(t.id),
            node: t
        };
    },
 
    positionIndicator: function(data, node, e) {
        var me = this,
            dragHeader = data.header,
            dropLocation = me.getLocation(data, node, e),
            targetHeader = dropLocation.header,
            pos = dropLocation.pos,
            nextHd,
            prevHd,
            topIndicator, bottomIndicator, topAnchor, bottomAnchor,
            topXY, bottomXY, headerCtEl, minX, maxX,
            allDropZones, ln, i, dropZone;
 
        // Avoid expensive CQ lookups and DOM calculations if dropPosition has not changed
        if (targetHeader === me.lastTargetHeader && pos === me.lastDropPos) {
            return;
        }
 
        nextHd = dragHeader.nextSibling('pivotconfigfield:not([hidden])');
        prevHd = dragHeader.previousSibling('pivotconfigfield:not([hidden])');
        me.lastTargetHeader = targetHeader;
        me.lastDropPos = pos;
 
        if ((dragHeader !== targetHeader) &&
            ((pos === "before" && nextHd !== targetHeader) ||
            (pos === "after" && prevHd !== targetHeader)) &&
            !targetHeader.isDescendantOf(dragHeader)) {
 
            // As we move in between different DropZones that are in the same
            // group (such as the case when in a locked grid), invalidateDrop
            // on the other dropZones.
            allDropZones = Ext.dd.DragDropManager.getRelated(me);
            ln = allDropZones.length;
            i = 0;
 
            for (; i < ln; i++) {
                dropZone = allDropZones[i];
 
                if (dropZone !== me && dropZone.invalidateDrop) {
                    dropZone.invalidateDrop();
                }
            }
 
            me.valid = true;
            topIndicator = me.getTopIndicator();
            bottomIndicator = me.getBottomIndicator();
 
            if (pos === 'before') {
                topAnchor = 'bc-tl';
                bottomAnchor = 'tc-bl';
            }
            else {
                topAnchor = 'bc-tr';
                bottomAnchor = 'tc-br';
            }
 
            // Calculate arrow positions. Offset them to align exactly with column border line
            if (targetHeader.isConfiguratorContainer && targetHeader.items.getCount() > 0) {
                // if dropping zone is the container then align the rows to the last column item
                topXY = topIndicator.getAlignToXY(targetHeader.items.last().el, topAnchor);
                bottomXY = bottomIndicator.getAlignToXY(targetHeader.items.last().el, bottomAnchor);
            }
            else {
                topXY = topIndicator.getAlignToXY(targetHeader.el, topAnchor);
                bottomXY = bottomIndicator.getAlignToXY(targetHeader.el, bottomAnchor);
            }
 
            // constrain the indicators to the viewable section
            headerCtEl = me.panel.el;
            minX = headerCtEl.getX() - me.indicatorXOffset;
            maxX = headerCtEl.getX() + headerCtEl.getWidth();
 
            topXY[0] = Ext.Number.constrain(topXY[0], minX, maxX);
            bottomXY[0] = Ext.Number.constrain(bottomXY[0], minX, maxX);
 
            // position and show indicators
            topIndicator.setXY(topXY);
            bottomIndicator.setXY(bottomXY);
            topIndicator.show();
            bottomIndicator.show();
 
        // invalidate drop operation and hide indicators
        }
        else {
            me.invalidateDrop();
        }
    },
 
    invalidateDrop: function() {
        this.valid = false;
        this.hideIndicators();
    },
 
    onNodeOver: function(node, dragZone, e, data) {
        var me = this,
            doPosition = true,
            dragColumn = data.header,
            target = me.getLocation(data, node, e);
 
        if (data.header.el.dom === node) {
            doPosition = false;
        }
 
        if (target && target.header && dragColumn) {
            doPosition = doPosition &&
                        dragColumn.getField().getSettings().isAllowed(target.header) &&
                        me.panel.isAllowed(target.header, dragColumn);
        }
 
        if (doPosition) {
            me.positionIndicator(data, node, e);
        }
        else {
            me.valid = false;
        }
 
        return me.valid ? me.dropAllowed : me.dropNotAllowed;
    },
 
    hideIndicators: function() {
        var me = this;
 
        me.getTopIndicator().hide();
        me.getBottomIndicator().hide();
        me.lastTargetHeader = me.lastDropPos = null;
 
    },
 
    onNodeOut: function() {
        this.hideIndicators();
    },
 
    onNodeDrop: function(node, dragZone, e, data) {
        var me = this,
            dragColumn = data.header,
            dropLocation = data.dropLocation;
 
        if (me.valid && dropLocation) {
            me.panel.dragDropField(dropLocation.header, dragColumn, dropLocation.pos);
        }
 
    }
});