pExt.ux.Portal = pExt.extend(pExt.Panel, {
    layout: 'table',
    autoScroll:true,
	header:false,
	border:false,
	frame:false,
    cls:'x-portal',
    defaultType: 'portalcolumn',
    layoutConfig: {columns: 1},

	lockedPanelBorderClass: 'x-locked-panel-border',
	unlockedPanelBorderClass: 'x-unlocked-panel-border',
	transparentPanelBorderClass: 'x-transparent-panel-border',

    defaults: {
        // applied to each contained panel
        bodyStyle:'padding:2px'
    },
    initComponent : function(){
        pExt.ux.Portal.superclass.initComponent.call(this);
        this.addEvents({
            validatedrop:true,
            beforedragover:true,
            dragover:true,
            beforedrop:true,
            drop:true
        });
    },

    initEvents : function(){
        pExt.ux.Portal.superclass.initEvents.call(this);
        this.dd = new pExt.ux.Portal.DropZone(this, this.dropConfig);
    },
    
    beforeDestroy: function() {
        if(this.dd){
            this.dd.unreg();
        }
        pExt.ux.Portal.superclass.beforeDestroy.call(this);
    },

	setDragDropCallBack: function(f){
		this.dragDropCallBack = f;
	}
});
pExt.reg('portal', pExt.ux.Portal);


pExt.ux.Portal.DropZone = function(portal, cfg){
    this.portal = portal;
    pExt.dd.ScrollManager.register(portal.body);
    pExt.ux.Portal.DropZone.superclass.constructor.call(this, portal.bwrap.dom, cfg);
    portal.body.ddScrollConfig = this.ddScrollConfig;
};

pExt.extend(pExt.ux.Portal.DropZone, pExt.dd.DropTarget, {
    ddScrollConfig : {
        vthresh: 50,
        hthresh: -1,
        animate: true,
        increment: 200
    },

    createEvent : function(dd, e, data, col, c, pos){
        return {
            portal: this.portal,
            panel: data.panel,
            columnIndex: col,
            column: c,
            position: pos,
            data: data,
            source: dd,
            rawEvent: e,
            status: this.dropAllowed
        };
    },

    notifyOver : function(dd, e, data){
	    this.showCellBorders(true);
        var xy = e.getXY(), portal = this.portal, px = dd.proxy;

        // case column widths
        this.grid = this.getGrid();

        // handle case scroll where scrollbars appear during drag
        var cw = portal.body.dom.clientWidth;
        if(!this.lastCW){
            this.lastCW = cw;
        }else if(this.lastCW != cw){
            this.lastCW = cw;
            portal.doLayout();
            this.grid = this.getGrid();
        }

        // determine column (cell)
        var col = 0, xs = this.grid.columnX, cmatch = false;
        for(var len = xs.length; col < len; col++){
            if(xy[0] > xs[col].x && xy[0] < (xs[col].x + xs[col].w)
            && xy[1] > xs[col].y && xy[1] < (xs[col].y + xs[col].h)){
                cmatch = true;
                break;
            }
        }

        // no match, fix last index
        if(!cmatch){
            col--;
        }

	    // prevent dragging into locked panel
	    if(portal.items.get(col).locked){
		    return;
	    }

        // find insert position (pos)
        var p, match = false, pos = 0,
            c = portal.items.itemAt(col),
            items = c.items.items;

        for(var len = items.length; pos < len; pos++){
            p = items[pos];
            var h = p.el.getHeight();
            if(h !== 0 && (p.el.getY()+(h/2)) > xy[1]){
                match = true;
                break;
            }
        }

	    // check pos against headOffset & tailOffset
	    if (pos < c.headOffset || pos > items.length - c.tailOffset) {
		    return;
	    }

        var overEvent = this.createEvent(dd, e, data, col, c,
                match && p ? pos : c.items.getCount());

        if(portal.fireEvent('validatedrop', overEvent) !== false &&
           portal.fireEvent('beforedragover', overEvent) !== false){

            // make sure proxy width is fluid
            px.getProxy().setWidth('auto');

            if(p){
                px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null);
            }else{
                px.moveProxy(c.el.dom, null);
            }

            this.lastPos = {c: c, col: col, p: match && p ? pos : false};
            this.scrollPos = portal.body.getScroll();

            portal.fireEvent('dragover', overEvent);

            return overEvent.status;
        }else{
            return overEvent.status;
        }

    },

    notifyOut : function(){
	    this.showCellBorders(false);
        delete this.grid;
    },

    notifyDrop : function(dd, e, data){
	    this.showCellBorders(false);
        delete this.grid;
        if(!this.lastPos){
            return;
        }
        var c = this.lastPos.c, col = this.lastPos.col, pos = this.lastPos.p;

        var dropEvent = this.createEvent(dd, e, data, col, c,
                pos !== false ? pos : c.items.getCount());

	    var p1=dd.panel.ownerCt.cellId, p2=c.cellId, p1v="", p2v="";
	    for (var i=dd.panel.ownerCt.headOffset; i<dd.panel.ownerCt.items.length-dd.panel.ownerCt.tailOffset; i++) {
		    if (dd.panel.ownerCt.items.get(i).moduleId != dd.panel.moduleId) {
		        p1v += dd.panel.ownerCt.items.get(i).moduleId;
		    }
	    }

        if(this.portal.fireEvent('validatedrop', dropEvent) !== false &&
           this.portal.fireEvent('beforedrop', dropEvent) !== false){

            dd.proxy.getProxy().remove();
            dd.panel.el.dom.parentNode.removeChild(dd.panel.el.dom);
            if(pos !== false){
                c.insert(pos, dd.panel);
            }else{
                c.add(dd.panel);
            }
            
            c.doLayout();

            this.portal.fireEvent('drop', dropEvent);

            // scroll position is lost on drop, fix it
            var st = this.scrollPos.top;
            if(st){
                var d = this.portal.body.dom;
                setTimeout(function(){
                    d.scrollTop = st;
                }, 10);
            }

        }
        delete this.lastPos;

	    // Post drop
	    this.portal.doLayout();
	    this.portal.show();

	    for (var i=c.headOffset; i<c.items.length-c.tailOffset; i++) {
		    p2v += c.items.get(i).moduleId;
	    }

	    if(this.portal.dragDropCallBack){
		    this.portal.dragDropCallBack(p1, p1v, p2, p2v);
	    }

	    // renumber portlets in p1 and p2
	    for (var i=0; i<this.portal.items.length; i++) {
		    var panel = this.portal.items.get(i);
		    if (panel.cellId == p1 || panel.cellId == p2) {
		        panel.renumberItemModuleIds();
		    }
	    }
    },

    // internal cache of body and column coords
    getGrid : function(){
        var box = this.portal.bwrap.getBox();
        box.columnX = [];
        this.portal.items.each(function(c){
	         var cell = pExt.get(c.cellId);
             box.columnX.push({x: cell.getX(), w: cell.getWidth(), y: cell.getY(), h: cell.getHeight()});
        });
        return box;
    },

    // unregister the dropzone from ScrollManager
    unreg: function() {
        //pExt.dd.ScrollManager.unregister(this.portal.body);
        pExt.ux.Portal.DropZone.superclass.unreg.call(this);
    },

	// show/unshow cell borders guidelines
	showCellBorders: function(show) {
		for(var i=0; i<this.portal.items.length; i++) {
			var cell = pExt.get(this.portal.items.get(i).cellId);
			if (show) {
				if (this.portal.items.get(i).locked) {
					cell.removeClass(this.portal.unlockedPanelBorderClass);
					cell.removeClass(this.portal.transparentPanelBorderClass);
					cell.addClass(this.portal.lockedPanelBorderClass);
				} else {
					cell.removeClass(this.portal.lockedPanelBorderClass);
					cell.removeClass(this.portal.transparentPanelBorderClass);
					cell.addClass(this.portal.unlockedPanelBorderClass);
				}
			} else {
				cell.removeClass(this.portal.lockedPanelBorderClass);
				cell.removeClass(this.portal.unlockedPanelBorderClass);
				cell.addClass(this.portal.transparentPanelBorderClass);
			}
		}
	}
});

