/**
 * Override to fix 'Permission denied' errors with FF and Firebug.
 * Fix by Condor http://www.extjs.com/forum/showthread.php?p=366510#post366510
 */
Ext.lib.Event.resolveTextNode = Ext.isGecko ? function(node){
	if(!node){
		return;
	}
	var s = HTMLElement.prototype.toString.call(node);
	if(s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]'){
		return;
	}
	return node.nodeType == 3 ? node.parentNode : node;
} : function(node){
	return node && node.nodeType == 3 ? node.parentNode : node;
};

/**
 * Clone Function, http://extjs.com/forum/showthread.php?t=26644
 * by Jozef Sakalos, aka Saki
 */
Ext.ux.clone = function(o) {
    if(!o || 'object' !== typeof o) {
        return o;
    }
    var c = 'function' === typeof o.pop ? [] : {};
    var p, v;
    for(p in o) {
        if(o.hasOwnProperty(p)) {
            v = o[p];
            if(v && 'object' === typeof v) {
                c[p] = Ext.ux.clone(v);
            }
            else {
                c[p] = v;
            }
        }
    }
    return c;
}; // eo function clone 



Ext.namespace('nl.jool.widget');
/**
 * Allows you to synchronise scrolling, row hover and row selection between grids 
 */
nl.jool.widget.GridSync = function (config) {
	this.grids = [];
	if(config) {
		Ext.apply(this, config);
		if(Ext.isArray(config.grid)) {
			Ext.each(config.grid, this.addGrid, this);
		}else if(config.grid)
			this.addGrid(config.grid);
	}
	nl.jool.widget.GridSync.superclass.constructor.call(this, config);
}
 
Ext.extend(nl.jool.widget.GridSync, Ext.util.Observable, {
	grids: [],
	
	addGrid: function (grid) {
		this.syncGrid(grid);
	},
	/** Used to check for recusion between synced grids */
	recursionCheck: false,
	
	syncGrid: function (grid) {
		this.grids.push(grid);
		var sm = grid.getSelectionModel(), cm = grid.getColumnModel();
		sm.on("rowselect", function (){this.execCallback(grid, this.onRowSelect, arguments);}, this);
		sm.on("rowdeselect", function (){this.execCallback(grid, this.onRowDeselect, arguments);}, this);
		grid.on("bodyscroll", function (){this.execCallback(grid, this.onBodyScroll, arguments);}, this);
		if(grid.trackMouseOver) {
			grid.on("mouseover", function (){this.execCallback(grid, this.onMouseOver, arguments);}, this);
			grid.on("mouseout", function (){this.execCallback(grid, this.onMouseOut, arguments);}, this);
		}
		if(grid.getView().constructor == Ext.grid.GroupingView) {
			//there is no event for group collapse/expand, so we create an interceptor
			grid.getView().toggleGroup = grid.getView().toggleGroup.createInterceptor(function(group, expanded) {
				this.execCallback(grid, this.onToggleGroup, arguments);
				return true;
			}, this);
		}

		//sync grid plugin events
		Ext.each(grid.initialConfig.plugins, function(plugin) {
			if(Ext.grid.RowExpander && plugin instanceof Ext.grid.RowExpander) {
				plugin.on('expand', function (){this.execCallback(grid, this.onRowExpand, arguments);
				    var rowIndex = arguments[3];
					//var grid = arguments[0];
					var row = grid.view.getRow(rowIndex);
					var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
					Ext.fly(body).parent().setHeight(Ext.fly(body).getHeight()+44);
				}, this);
				plugin.on('collapse', function (){this.execCallback(grid, this.onRowCollapse, arguments);}, this);
			}

		}, this);


	},
	
	unsyncGrid: function (grid) {
		this.grids.remove(grid);
		var sm = grid.getSelectionModel();
		sm.un("rowselect", function (){this.execCallback(grid, this.onRowSelect, arguments);}, this);
		sm.un("rowdeselect", function (){this.execCallback(grid, this.onRowDeselect, arguments);}, this);
		grid.un("bodyscroll", function (){this.execCallback(grid, this.onBodyScroll, arguments);}, this);
		grid.un("mouseover", function (){this.execCallback(grid, this.onMouseOver, arguments);}, this);
		grid.un("mouseout", function (){this.execCallback(grid, this.onMouseOut, arguments);}, this);
		cm.on("widthchange", function() {this.execCallback(grid, this.onWidthChange, arguments);}, this);
		//TODO: how do we remove the interceptor(s)?
	},

	/** Checks if the column is hidden in every connected grid */
	isHidden: function(i) {
		var isHidden = true;
		Ext.each(this.grids, function (grid) {
			if(!grid.colModel.isHidden(i))
				isHidden = false;
		});
		return isHidden;
	},
	
	/** execute the callback function for every grid other then the activeGrid */
	execCallback: function(activeGrid, cb, args) {
		if(!this.recursionCheck) {
			this.recursionCheck=true;
			Ext.each(this.grids, function (grid) {
				if(grid.id!=activeGrid.id) {
					cb.apply(grid, args);
				}
			});
			this.recursionCheck=false;
		}
	},
	 
	/* sync events */
	onWidthChange: function(cm, columnIndex, newWidth ) {
		//console.log('new width:', newWidth);
		this.getColumnModel().setColumnWidth(columnIndex, newWidth);
	},
	
	onToggleGroup: function (group, expanded) {
		var l = group.parentNode.childNodes;
		var index;
		for(var i=0; i< l.length; i++) {
			if(l.item(i).id==group.id) {
				index =i; 
				break;
			}
		}
		//.indexOf(group);
		group = this.getView().getGroups()[index];
		this.getView().toggleGroup(group, expanded);
	},

	onRowExpand: function (grid, record, body, rowIndex) {
		console.log(Ext.fly(body).getHeight());
		Ext.each(this.initialConfig.plugins, function(plugin) {
			if(plugin instanceof Ext.grid.RowExpander) {
				plugin.expandRow(rowIndex);
			}
		});
	},

	onRowCollapse: function (grid, record, body, rowIndex) {
		Ext.each(this.initialConfig.plugins, function(plugin) {
			if(plugin instanceof Ext.grid.RowExpander)
				plugin.collapseRow(rowIndex);
		});
	},

	onRowSelect: function(sm, index, record) {
		this.getSelectionModel().selectRow(index, true);
	},
	
	onRowDeselect: function(sm, index, record) {
		this.getSelectionModel().deselectRow(index);
	},
	
	onBodyScroll: function (scrollLeft, scrollTop) {
		this.getView().scroller.dom.scrollTop = scrollTop;
	},

	//scope: gridview
	onMouseOver: function (e, t) {
		var row;
		var view = this.getView();
		if((row = view.findRowIndex(t)) !== false){
			view.addRowClass(row, "x-grid3-row-over");
		}
	},
	
	//scope: gridview
	onMouseOut: function (e, t) {
		var row;
		var view = this.getView();
		if((row = view.findRowIndex(t)) !== false && row !== view.findRowIndex(e.getRelatedTarget())){
			view.removeRowClass(row, "x-grid3-row-over");
		}
	}
});


/** lockingrid plugin */
nl.jool.widget.LockingGrid = function(config){
	nl.jool.widget.LockingGrid.superclass.constructor.call(this, config);
};

Ext.extend(nl.jool.widget.LockingGrid, nl.jool.widget.GridSync, {
	
	init: function(grid) {
		this.cloneGrid(grid);
	},
	
	/** Return the grid with the locked columns */
	getLockedGrid: function () {
		return this.grids[1];
	},
	
	/** Return the original grid  */
	getOriginalGrid: function () {
		return this.grids[0];
	},

	cloneGrid: function(originalGrid) {
		var originalConfig = originalGrid.initialConfig;
		var cloneConfig = { //we use the same store
            store: originalGrid.store
		};
		if(originalGrid.initialConfig.viewConfig) { //the view configuration
			cloneConfig.viewConfig = Ext.ux.clone(originalConfig.viewConfig);
        
		} else if(originalGrid.initialConfig.view) { //the view
        	cloneConfig.view = new originalGrid.initialConfig.view.constructor(Ext.ux.clone(originalGrid.view.initialConfig));
		}
        	
        if(originalGrid.initialConfig.columns) {// columns
        	cloneConfig.columns = Ext.ux.clone(originalGrid.initialConfig.columns);
        }
		if (originalGrid.initialConfig.sm) {//selection model
			cloneConfig.sm = new originalGrid.initialConfig.sm.constructor (Ext.ux.clone(originalGrid.initialConfig.sm.initialConfig));
		}

        if(originalGrid.initialConfig.cm) { //column model
        	var cmConfig = [];
        	for(var i=0; i< originalGrid.initialConfig.cm.config.length; i++) {
        		var col = originalGrid.initialConfig.cm.config[i];
        		if((originalGrid.initialConfig.cm.config[i].constructor == Ext.grid.RowExpander) || (originalGrid.initialConfig.cm.config[i].constructor == Ext.grid.CheckboxSelectionModel)) {
        			originalGrid.initialConfig.cm.config.remove(col);
        			col.locked=true;
        			cmConfig.push(col);
        		}else	
        			cmConfig.push(Ext.ux.clone(col));
        	}
        	cloneConfig.cm = new originalGrid.initialConfig.cm.constructor(cmConfig);
        }
		
		//plugins
		cloneConfig.plugins = [];
		Ext.each(originalGrid.initialConfig.plugins, function(plugin) {
			if(!(plugin instanceof nl.jool.widget.LockingGrid)){
				var ccfg = Ext.ux.clone(plugin.initialConfig || {});
				if(plugin instanceof Ext.grid.RowExpander) //RowExpander doesn't have an initialconfig property
					ccfg.tpl = plugin.tpl;
				cloneConfig.plugins[cloneConfig.plugins.length] = new plugin.constructor(ccfg);
			}

		});

		//instantiate
		var clone = new originalGrid.constructor(cloneConfig);
		
		//Use split dnd proxy from the original grid
		clone.getView().initElements = clone.getView().initElements.createSequence(function () {
			//console.log('initElements sequence');
	        this.resizeMarker = originalGrid.getView().resizeMarker;
	        this.resizeProxy = originalGrid.getView().resizeProxy;
		}, clone.getView());
		
		//hide/unhide the correct 'locked' columns
		var cm = clone.getColumnModel();
		var cm2 = originalGrid.getColumnModel();
        for(var i = 0; i < cm.config.length; i++){
        	if(cm.config[i].locked) {
        		cm.setHidden(i, false);
        		cm2.setHidden(i, true)
        	} else {	
        		cm.setHidden(i, true);
        		cm2.setHidden(i, false)
        	}
        	
        }
        clone.setWidth(0);
		originalGrid.on("render", this.onRender, this);
		
		// patch the columsn grid header menu
		var lc = this;		
		var beforeColMenuShow = function(){
	        var cm = this.cm,  colCount = cm.getColumnCount();
	        this.colMenu.removeAll();
	        for(var i = 0; i < colCount; i++){
	            if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
	                this.colMenu.add(new Ext.menu.CheckItem({
	                    id: "col-"+cm.getColumnId(i),
	                    text: cm.getColumnHeader(i),
	                    checked: !lc.isHidden(i),
	                    hideOnClick:false,
	                    disabled: cm.config[i].hideable === false
	                }));
	            }
	        }
	    };
		originalGrid.getView().beforeColMenuShow = beforeColMenuShow;
		clone.getView().beforeColMenuShow = beforeColMenuShow;

		//Add the grids to the gridsync
		this.addGrid(originalGrid);
		this.addGrid(clone);
		return clone;
	},
	

	
	onRender: function(){
		var grid = this.grids[0];
		var clone = this.grids[1];
        var el = grid.getGridEl();
		el = el.dom.firstChild;
		var allWrap = Ext.get(el);
		//inject the lockWrap div and render the clone to that element
		var lockWrap = allWrap.insertFirst({cls:'lockWrap'});
		clone.render(lockWrap);

		//add  lock/unlock menu items
		grid.getView().hmenu.add('-', {id:"lock", text: "Lock", cls: "xg-hmenu-lock"});
		clone.getView().hmenu.add('-', {id:"unlock", text: "Unlock", cls: "xg-hmenu-unlock"});

		clone.on("columnresize", this.handleResize, this);
		grid.on("columnresize", this.handleResize, this);

		grid.getGridEl().addClass('lockingColumnContainer');
		
		grid.getView().hmenu.on("itemclick", function (item) {
			var grid = this.getOriginalGrid();
			var clone = this.getLockedGrid();
			var view = grid.getView();
	        var index = view.hdCtxIndex;
			var cm = view.cm, ds = view.ds;
			var cm2 = clone.getView().cm;
	        switch(item.id){
	            case "lock":
	                cm.setHidden(index, true);
	                cm2.setHidden(index, false);
					this.handleResize();
					break;
			}
		}, this);
	    
	    clone.getView().hmenu.on("itemclick", function (item) {
			var grid = this.getOriginalGrid();
			var clone = this.getLockedGrid();
			var view = clone.getView();
	        var index = view.hdCtxIndex;
			var cm2 = view.cm;
			var cm = grid.getView().cm;
	        switch(item.id){
				case "unlock":
	                cm.setHidden(index, false);
	                cm2.setHidden(index, true);
					this.handleResize();
					break;
			}
		}, this);

	    grid.getView().onLayout = grid.getView().onLayout.createInterceptor(this.handleResize, this);
		clone.getView().onLayout = clone.getView().onLayout.createInterceptor(this.handleResize, this);
	},

	//sync locked grid size with content grid size
	handleResize: function (vw, vh) {
		var clone = this.getLockedGrid();
		var grid = this.getOriginalGrid();
		var c = grid.getGridEl();
		var csize = c.getSize(true);
		var vw = csize.width;
		var hdHeight = grid.getView().mainHd.getHeight();
		var vh = csize.height - (hdHeight);
		var lw = parseInt(clone.view.getTotalWidth());
		var scrollbarHeight = 0;
		
		//resize content grid to view width - lock width
		grid.getView().scroller.setSize(vw-lw, vh);
		grid.getView().renderBody();
		
		if(!lw) { //we don't have any locked columns, hide the clone
			grid.getGridEl().addClass('noLocking');
			grid.getGridEl().removeClass('withLocking');
		} else {
			grid.getGridEl().removeClass('noLocking');
			grid.getGridEl().addClass('withLocking');
		}
		
		//move content grid to the right
		grid.getView().scroller.dom.parentNode.style.paddingLeft=lw+'px';
		if(!grid.getView().forceFit) {
			//see if we got a scrollbar
			//var diff = clone.getView().scroller.dom.clientHeight - grid.getView().scroller.dom.clientHeight;
			//console.log(diff);
			scrollbarHeight = (grid.getView().scroller.dom.scrollWidth > grid.getView().scroller.dom.clientWidth)?16:0;
		}
		//clone.setSize(lw, csize.height - scrollbarHeight);
		clone.setSize(vw, csize.height - scrollbarHeight);
		clone.getGridEl().setWidth(lw);
		clone.getGridEl().first().setWidth(lw);
	}

		
});