/* 
 * TREEVIEW ASSET MANAGER
 */
hXUL.AssetManager.treeViews = [];

hXUL.AssetManager.createTreeView = function(id, columnTitles, data, placeholder) {		
	var r = this.createTreeViewRenderer();
	var t = new hXUL.TreeView(id, columnTitles, this.dataTransformer(data), placeholder, r);			
	hXUL.AssetManager.treeViews.push(t);
	t.assetIndex = hXUL.AssetManager.treeViews.length - 1;
	r.assetIndex = hXUL.AssetManager.treeViews.length - 1;
	return t;	
}

hXUL.AssetManager.dataTransformer = function(data) {
	// You may override this function with a custom data transformation routine
	return data;
}

hXUL.AssetManager.createTreeViewRenderer = function() {
	if (hXUL.hasSupportForXUL()) {
		return new hXUL.TreeView.Renderer.XULRenderer();
	} else {
		return new hXUL.TreeView.Renderer.XHTMLRenderer();
	}
}

/* 
 * TREEVIEW CLASS
 */

hXUL.TreeView = function(id, columnTitles, data, placeholder, renderer) {	
	this.id        = id;
	this.element   = null;
	this.data	   = data;	
	this.columnTitles = columnTitles; 
	this._renderer = renderer;			
	this.setPlaceholder(placeholder);	
	this.onselect   = null;   // tree item selection event handler
	this.ondeselect = null;   // tree item de-selection event handler 
	this.onreorder  = null;   // post drag&drop event handler
}

hXUL.TreeView.prototype.setPlaceholder = function(placeholder) {
	if(typeof placeholder == "string") 
		this.placeholder  = document.getElementById(placeholder); 
	else 
		this.placeholder  = placeholder;	
}

hXUL.TreeView.prototype.render = function(placeholder) {	
	
	// Placeholder can be set here, or whith the hXUL.AssetManager.createTreeView method
	if(placeholder)
		this.setPlaceholder(placeholder);
					
	// Render treeview
	this._renderer.render(this);
}

/* 
 * TREEVIEW RENDERER 
 */

hXUL.TreeView.Renderer = function() {}

/* 
 * XUL TREEVIEW RENDERER
 */
  
hXUL.TreeView.Renderer.XULRenderer = function() {
	this.tree = null;	
	this.primaryColumnIndex = 0; /* Column with hierarchical data */	
}

hXUL.Class.extend(hXUL.TreeView.Renderer.XULRenderer, hXUL.TreeView.Renderer);

hXUL.TreeView.Renderer.XULRenderer.prototype.render = function(treeView) {
	if(!treeView.data || !treeView.data[0])
		throw new Error("Tree data not provided");

	var tree = this.createTreeElement(treeView.id);
	var treeCols = tree.appendChild(this.createTreeColsElement(treeView.id));
	for(var i=0;i < treeView.data[0].content.length; i++) {
		if(i>0)
			treeCols.appendChild(this.createTreeSplitterElement());
		treeCols.appendChild(this.createTreeColElement(treeView.id,i,treeView.columnTitles[i]));
	}
	var treeChildren = tree.appendChild(this.createTreeChildrenElement(treeView.id));	
	
	tree.setAttribute("rows",this.renderDataNodes(treeView.id, treeView.data, treeChildren));	
	
	treeView.element = treeView.placeholder.appendChild(tree); 
	this.tree = treeView.element;
	
}

hXUL.TreeView.Renderer.XULRenderer.prototype.renderDataNodes = function(idPrefix, data, treeChildren, rowCount) {

	if(!rowCount) rowCount = 0;

	for(var i=0; i<data.length; i++) {
		var treeItem = treeChildren.appendChild(this.createTreeItemElement());
		var treeRow  = treeItem.appendChild(this.createTreeRowElement(rowCount));
		
		rowCount++;
		
		for(var j=0;j<data[i].content.length;j++) {
			treeRow.appendChild(this.createTreeCellElement(data[i].content[j],j));			
		}
		if(data[i].children) {
			treeItem.setAttribute("container","true");
			treeItem.setAttribute("open","true");
			var subTreeChildren = treeItem.appendChild(this.createTreeChildrenElement());
			rowCount = this.renderDataNodes(idPrefix, data[i].children, subTreeChildren, rowCount);
		} 
	}
	return rowCount;
}

hXUL.TreeView.Renderer.XULRenderer.prototype.createTreeElement = function(idPrefix) {	
	var tree = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","tree");	
	tree.id = idPrefix + "-tree";	
	tree.setAttribute("flex","1");	
	tree.setAttribute("hidecolumnpicker","true");	
	tree.setAttribute("seltype","single");		
	tree.className = "tree";
	return tree;
}

hXUL.TreeView.Renderer.XULRenderer.prototype.createTreeColsElement = function(idPrefix) {	
	var treeCols = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","treecols");	
	treeCols.id = idPrefix + "-treeCols";		
	return treeCols;
}

hXUL.TreeView.Renderer.XULRenderer.prototype.createTreeColElement = function(idPrefix, index, title) {	
	var treeCol = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","treecol");	
	treeCol.id = idPrefix + "-treeCol"+index;
	if(index==this.primaryColumnIndex) {
		treeCol.setAttribute("primary","true");	
		treeCol.setAttribute("flex","2");	
	} else
		treeCol.setAttribute("flex","1");
	if(title)
		treeCol.setAttribute("label",title);	
	treeCol.setAttribute("persist","width");	
	
	return treeCol;
}

hXUL.TreeView.Renderer.XULRenderer.prototype.createTreeSplitterElement = function() {	
	var splitter = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","splitter");	
	splitter.className = "tree-splitter";
	return splitter;
}	

hXUL.TreeView.Renderer.XULRenderer.prototype.createTreeChildrenElement = function(idPrefix) {	
	var treeChildren = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","treechildren");	
	if(idPrefix)
		treeChildren.id = idPrefix + "-treeChildren";		
	return treeChildren;
}

hXUL.TreeView.Renderer.XULRenderer.prototype.createTreeItemElement = function() {	
	var treeItem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","treeitem");	
	return treeItem;
}

hXUL.TreeView.Renderer.XULRenderer.prototype.createTreeRowElement = function() {	
	var treeRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","treerow");	
	return treeRow;
}

hXUL.TreeView.Renderer.XULRenderer.prototype.createTreeCellElement = function(text) {	
	var treeCell = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","treecell");	
	treeCell.setAttribute("label",text);
	return treeCell;
}


hXUL.TreeView.Renderer.XULRenderer.prototype.getTreeItemElement = function(index) {
	
	var items = this.tree.getElementsByTagName('treeitem');
	 for (var i=0; i<items.length; i++) {
        if (this.tree.contentView.getIndexOfItem(items[i]) == index) {
            return items[i];
        }
    }
	return null;
	
	// return treeView.element.view.getItemAtIndex(index); // would not work because of collapsed branches
}

/* 
 * XHTML RENDERER
 */

hXUL.TreeView.Renderer.XHTMLRenderer = function() {
	this.selectedRow        = null;
	this.selectedRowIndex   = -1;
	this.rowYCoordinates    = [];	
	this.dragGestureTimeout = 400;  /* milliseconds before the drag&drop is activate on a mouse down */
	this.primaryColumnIndex = 0;    /* Column with hierarchical data */	
}

hXUL.Class.extend(hXUL.TreeView.Renderer.XHTMLRenderer, hXUL.TreeView.Renderer);

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.render = function(treeView) {
				
	var tree  = this.createTreeElement(treeView.id);
	var tbody = tree.firstChild; // switch to tbody
	var treeCols = tbody.appendChild(this.createTreeColsElement(treeView.id));
	for(var i=0;i < treeView.data[0].content.length; i++) {
		treeCols.appendChild(this.createTreeColElement(treeView.id,i,treeView.columnTitles[i]));
	}		
	this.renderTreeElements(treeView.id, treeView.data, tbody);
	tbody.appendChild(this.createTreeFooterElement(treeView));
	treeView.element = treeView.placeholder.appendChild(tree); 	
	this.tree = treeView.element;
	this.resetRowYCoordinates();
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.renderTreeElements = function(idPrefix, data, container, rowCount, level) {

	if(!rowCount) rowCount = 0;
	if(!level) level = 0;
	
	for(var i=0; i<data.length; i++) {
		
		var isContainer = data[i].children?true:false;
		var rowClass = (typeof data[i].rowClass != "undefined")?data[i].rowClass:null;
		var treeRow = container.appendChild(this.createTreeRowElement(isContainer, rowClass));
		treeRow.id = data[i].id;	
		rowCount++;
		
		for(var j=0;j<data[i].content.length;j++) {
			treeRow.appendChild(this.createTreeCellElement(data[i].content[j], j, isContainer));					
		} 

		this.setLevel(treeRow,level);

		if(isContainer) {						
			rowCount = this.renderTreeElements(idPrefix, data[i].children, container, rowCount, level+1);
		} 
	}
	return rowCount;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.createTreeElement = function(idPrefix) {	
	var tree = document.createElement("table");	
	tree.id = idPrefix + "-tree";	
	tree.className = "tree";	
	var tbody = document.createElement("tbody");
	tree.appendChild(tbody);
	return tree;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.createTreeColsElement = function(idPrefix) {	
	var treeCols = document.createElement("tr");	
	treeCols.id = idPrefix + "-treeCols";		
	treeCols.className = "treecols";
	return treeCols;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.createTreeColElement = function(idPrefix, index, title) {	
	var treeCol = document.createElement("th");	
	treeCol.id = idPrefix + "-treeCol"+index;
	treeCol.className = "treecol";
	if(title)
		treeCol.appendChild(document.createTextNode(title));	
	return treeCol;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.createTreeRowElement = function(isContainer, rowClass, idPrefix, index) {	
	var treeRow = document.createElement("tr");	
	if(idPrefix)
		treeRow.id = idPrefix + "-treeRow"+index;	
	treeRow.className = "treerow";	
	if(isContainer)
		treeRow.className += " container open";	
	if(index % 2 == 1)
		treeRow.className += " odd";	
	if(rowClass) 
		treeRow.className += " " + rowClass;	
	return treeRow;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.createTreeCellElement = function(text, colIndex, isContainer, idPrefix, rowIndex) {	
	var treeCell = document.createElement("td");	
	if(idPrefix && rowIndex && colIndex)
		treeCell.id = idPrefix + "-treeCell"+rowIndex+"-"+colIndex;	
	treeCell.className = "treecell";	
	
	treeCell.innerHTML = text;
	
	if(colIndex == this.primaryColumnIndex) {
		treeCell.className += " primary";	
		
		var icon = document.createElement("div");
		icon.className = "icon";		
		treeCell.insertBefore(icon,treeCell.firstChild);
		
		if(isContainer) {			
			treeCell.insertBefore(this.createTwistieElement(),treeCell.firstChild);
		}		
		treeCell.insertBefore(this.createSpacersElement(),treeCell.firstChild);
	}
	return treeCell;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.createTwistieElement = function() {
	var twistie = document.createElement("div");
	twistie.className = "twistie";
	twistie.onclick = hXUL._aux.bind(this.toggleOpenState, this);
	twistie.style.cursor = "pointer";
	return twistie;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.createTreeFooterElement = function(treeView) {
	var footer = document.createElement("tr");
	footer.className = "treefooter";
	for(var i=0;i < treeView.data[0].content.length; i++) {
		footer.appendChild(document.createElement("td"));
	}
	return footer;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.createSpacersElement = function() {
	var spacers = document.createElement("div");
	spacers.className = "spacers";
	return spacers;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.createSpacerElement = function(type) {
	var spacer = document.createElement("div");
	spacer.className = "spacer "+type;
	return spacer;
}

/*
 * XHTML OPEN/CLOSE TREE 
 */
 
hXUL.TreeView.Renderer.XHTMLRenderer.prototype.toggleOpenState = function(e) {
	var twistie = hXUL._aux.getEventSource(e);	
	var rowElement = twistie.parentNode.parentNode;
	if(this.isContainerOpen(rowElement)) {
		this.closeContainer(rowElement);
	} else {
		this.openContainer(rowElement);
	}	
	this.preventSelectEvent   = true;
	this.preventDeselectEvent = true;
	
	this.resetRowYCoordinates();
	hXUL._aux.stopPropagation(e); 
	return hXUL._aux.preventEvent(e);	
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.openContainer = function(container) {	
	var rows = this.getChildrenRows(container); // rows is inversed
	for(var i=rows.length-1;i>=0;i--) {		
		this.showRow(rows[i]);
		if(this.isContainerClosed(rows[i])) {
			i-=this.getChildrenRows(rows[i]).length;
		}
	}
	container.className = container.className.replace(" closed", " open");	
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.closeContainer = function(container) {	
	var rows = this.getChildrenRows(container);
	for(var i=0;i<rows.length;i++)
		this.hideRow(rows[i]);
		
	container.className = container.className.replace(" open", " closed");
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.isContainerOpen = function(container) {
	return hXUL._aux.hasClass(container,"open") && this.isContainer(container);
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.isContainerClosed = function(container) {
	return hXUL._aux.hasClass(container,"close") && this.isContainer(container);
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.isContainer = function(row) {
	return hXUL._aux.hasClass(row,"container");
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.showRow = function(row) {
	try {
		row.style.display = "table-row";
	} catch(x) {
		row.style.display = "block";
	}
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.hideRow = function(row) {
	row.style.display = "none";
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.getLevel = function(row) {
	if(!row || !row._level)
		return 0;
	return row._level;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.setLevel = function(row, level) {
	if(!row)
		return;
	var pattern = /level\d+/;
	var matches = row.className.match(pattern);
	if(matches && matches[0]) {
		row.className = row.className.replace(matches[0],"level"+level);
	} else {
		row.className += " level"+level;
	}	
	row._level = level;
	
	var primaryCell = row.getElementsByTagName("td")[this.primaryColumnIndex];

	var spacers = primaryCell.firstChild;	
	for(var i=0;i<level;i++) {
		
		if(i==level-1)
			var type = "branch";
		else 
			var type = "line";
			
		if(spacers.childNodes[i]) {
			// make sure the spacer is of the right type.
			spacers.childNodes[i].className = "spacer "+type;
		} else {
			// create spacer
			insertPoint = spacers.appendChild(this.createSpacerElement(type));
		}
	}
	while(spacers.childNodes[i]) {
		// delete extra spacers
		spacers.removeChild(spacers.childNodes[i]);
	}
}




hXUL.TreeView.Renderer.XHTMLRenderer.prototype.toggleRowSelection = function(e) {
	var mouse = hXUL._aux.getMouseCoordinates(e);
	var r = this.getRowAt(this.tree, mouse.x, mouse.y);
	if(r.index == this.selectedRowIndex) {
		setTimeout(hXUL._aux.bind(
						function() { 
							if(!this.dragStarted) 
								this.deselectRow();
							},
						this),
					500);
		return false;
	} else {		
		this.selectRow(r.row,r.index);
		return true;
	}
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.selectRow = function(row,index) {
	this.deselectRow();
	this.selectedRow = row;
	this.selectedRowIndex = index;
	if(this.selectedRow) 
		this.selectedRow.className += " selected";
	
	// run onselect event handler
	var treeview = hXUL.AssetManager.treeViews[this.assetIndex];
	if(treeview.onselect) {
		setTimeout(hXUL._aux.bind(
						function() {
							if(!this._renderer.dragStarted && !this._renderer.preventSelectEvent)
								this.onselect(this._renderer.selectedRow);
							this._renderer.preventSelectEvent = false;
						}, treeview),
				   this.dragGestureTimeout+100);
	}
	this.preventDeselectEvent = false;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.deselectRow = function() {	
	if(this.selectedRow) {
		this.selectedRow.className = this.selectedRow.className.replace(" selected","");
		// run ondeselect event handler		
		var treeview = hXUL.AssetManager.treeViews[this.assetIndex];
		if(treeview.ondeselect) {
			setTimeout(hXUL._aux.bind(
						function() {
							if(!this._renderer.dragStarted && !this._renderer.preventDeselectEvent)
								this.ondeselect(this._renderer.selectedRow);
							this._renderer.preventDeselectEvent = false;
						}, treeview),
				   this.dragGestureTimeout+100);
		}
		
	}
	this.preventSelectEvent = false;
	this.selectedRow = null;
	this.selectedRowIndex = -1;		
}
	
hXUL.TreeView.Renderer.XHTMLRenderer.prototype.getRowAt = function(tree,x,y) {
	var tbody = tree.firstChild;
	var l = tbody.childNodes.length;		
	var i = ((y-this.rowYCoordinates[0]) / this.averageRowHeight) | 0;
	var direction = 1;
	if (i==l) i=l-1;
	while(i>=0 && i<l) {
		var row = tbody.childNodes[i];
		if(row.style.display!="none") {			
			var t = this.rowYCoordinates[i]; //hXUL._aux.getScreenCoordinates(row).y
			if(t>y)
				direction = -1;
			else {
				var b = t + row.offsetHeight;
				if(t <= y && b >= y)
					return {row: row, index: i};				
			}				
		}
		i += direction;
	}
	return {row:null, index:-1};;	
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.resetRowYCoordinates = function() {
	var tbody = this.tree.firstChild;
	this.rowYCoordinates = [];
	var h = 0;
	var prevy = 0;
	var j = 0;
	for(var i=0;i<tbody.childNodes.length;i++) {
		var row = tbody.childNodes[i];
		if(row.style.display!="none") {
			var y = hXUL._aux.getScreenCoordinates(row).y;
			this.rowYCoordinates.push(y);
			if(i>0) 
				h += y - prevy;	
			prevy = y;			
			j++;
		} else {			
			this.rowYCoordinates.push(-1);
		}
	}
	this.averageRowHeight = h/j;
}

hXUL.TreeView.Renderer.XHTMLRenderer.prototype.getChildrenRows = function(container) {
	var parentLevel = this.getLevel(container);
	var rows = [];
	var row = container.nextSibling;
	while(row) {
		var level = this.getLevel(row);
		if(level > parentLevel) {
			rows.unshift(row);
			row = row.nextSibling;
		} else 
			row = null;
	}
	return rows;
}
