// Functions for interacting with Tables
// =====================================
var Table = (function(){

	// Create the table object which will hold the functions	
	var table = {};

	// Get a parent TABLE object reference from any element within it
	// --------------------------------------------------------------
	table.getTable = function(o) {
		if (o==null) { return null; }
		return DOM.getParentByTagName(o,"TABLE");
	}

	// Expand all hidden TBODY tags	
	// ----------------------------
	table.expandBodies = function(t) {
		var bodies = table.getBodies(t);
		if (bodies==null) { return null; }
		for (var i=0; i<bodies.length; i++) {
			if (CSS.getStyle(bodies[i],"display")=="none") {
				CSS.setStyle(bodies[i],"display","block");
			}
		}
	}

	// Get all the tbody elements in a table
	// -------------------------------------
	table.getBodies = function(t) {
		if (t==null) { return null; }
		if (t.tBodies && t.tBodies.length>0) { return t.tBodies; }
		if (t.getElementsByTagName) {
			return t.getElementsByTagName("TBODY");
		}
		return null;
	}

	// Get all the thead elements in a table
	// -------------------------------------
	table.getHeads = function(t) {
		if (t==null) { return null; }
		if (t.getElementsByTagName) {
			return t.getElementsByTagName("THEAD");
		}
		return null;
	}

	// Expand all table rows and hide the row containing the onclick action
	// --------------------------------------------------------------------
	table.expandRowClicked = function(o) {
		var t, tr;
		if ((tr = DOM.getParentByTagName(o,"TR"))==null) { return; }
		if ((t = Table.getTable(tr))==null) { return; }
		Table.expandBodies(t);
		CSS.setStyle(tr,"display","none");
	}
	
	// Get the text value of a cell
	// ----------------------------
	table.getCellValue = function(td) {
		if (td==null) { return null; }
//		if (defined(td.innerText)) { 
//			return td.innerText;
//		}
		if (!td.childNodes) { return ""; }
		var childNodes = td.childNodes;
		var childNodesLength = childNodes.length;
		var node = null;
		var ret = "";
		for (var i=0; i<childNodesLength; i++) {
			node = childNodes[i];
			if (node.nodeType && node.nodeType==1) {
				if (node.nodeName=="INPUT" && defined(node.value)) {
					ret += node.value;
				}
				else {
					ret += table.getCellValue(node);
				}
			}
			else if (node.nodeType && node.nodeType==3 && defined(node.nodeValue)) {
				ret += node.nodeValue;
			}
		}
		return ret;
	}
	
	// Sort all rows in each TBODY (tbodies are sorted independent of each other)
	// --------------------------------------------------------------------------
	table.lastSortedColumn = new Object();
	table.SortedAscendingClassName = "TableSortedAscending";
	table.SortedDescendingClassName = "TableSortedDescending";
	table.SortableClassName = "sortable";
	
	table.sort = function(t,args) {
		var colIndex, sortType, descending, rowShade;
		if (!defined(args)) { args = new Object(); }
		
		// Resolve colIndex
		if (!defined(args['colIndex'])) { 
			colIndex = (defined(t) && defined(t.cellIndex))?t.cellIndex:0;
		}
		else {
			colIndex = args['colIndex'];
		}
		
		// Resolve sortType
		if (!defined(args['sortType']) || typeof(args['sortType'])!="function") { sortType = Sort.Default; }
		else { sortType = args['sortType']; }
		
		// Resolve the table
		if (t==null) { return; }
		if (t.nodeName && t.nodeName!="TABLE") {
			t = table.getTable(t);
		}
		
		// If the table doesn't have an ID, give it one
		CSS.createId(t);
		
		// Loop through all THEADs and remove sorted class names

		var theads = table.getHeads(t);
		for (var i=0; i<theads.length; i++) {
			var th = theads[i];
			if (defined(th.rows) && th.rows.length>0) { 
				for (var j=0; j<th.rows.length; j++) { 
					var row = th.rows[j];
					if (defined(row.cells) && row.cells.length>0) { 
						var cells = row.cells;
						for (var k=0; k<cells.length; k++) {
							CSS.removeClass(cells[k],table.SortedAscendingClassName);
							CSS.removeClass(cells[k],table.SortedDescendingClassName);
						}
					}
				}
			}
		}

		// Resolve descending
		if (defined(table.lastSortedColumn[t.id]) && table.lastSortedColumn[t.id]['index']==colIndex) {
			descending = !(table.lastSortedColumn[t.id]['descending']);
		}
		else if (defined(args['descending']) && typeof(args['descending'])=="boolean") { 
			descending = args['descending'];
		}
		else {
			descending = false;
		}
		var sortedClassName = descending?table.SortedDescendingClassName:table.SortedAscendingClassName;
		
		table.lastSortedColumn[t.id] = {'index':colIndex, 'descending':descending};
		var bodies = table.getBodies(t);
		if (bodies==null || bodies.length==0) { return; }
		for (var i=0; i<bodies.length; i++) {
			var tb = bodies[i];
			var rows = new Array();
			for (var j=0; j<tb.rows.length; j++) {
				rows[j] = tb.rows[j];
			}
			sortrows(rows,colIndex,sortType,descending);
			for (var j=0; j<rows.length; j++) {
				tb.appendChild(rows[j]);
			}
		}
		
		// Loop through all THEADs and add sorted class names
		var theads = table.getHeads(t);
		for (var i=0; i<theads.length; i++) {
			var th = theads[i];
			if (defined(th.rows) && th.rows.length>0) { 
				for (var j=0; j<th.rows.length; j++) { 
					var row = th.rows[j];
					if (defined(row.cells) && row.cells.length>0 && colIndex<row.cells.length) { 
						if (CSS.hasClass(row.cells[colIndex],table.SortableClassName)) {
							CSS.addClass(row.cells[colIndex],sortedClassName);
						}
					}
				}
			}
		}

		// Shade rows is a class name was supplied
		if (defined(args['rowShade'])) {
			table.shadeOddRows(t,args['rowShade']);
		}

		// inner function to sort rows
		function sortrows(rowarray,colIndex,sortfunc,descending) {
			var newSortFunc = function(a,b) {
				var aa = (a.cells&&colIndex<a.cells.length)?table.getCellValue(a.cells[colIndex]):null;
				var bb = (b.cells&&colIndex<b.cells.length)?table.getCellValue(b.cells[colIndex]):null;
				return (descending)?sortfunc(bb,aa):sortfunc(aa,bb);
			}
			rowarray.sort(newSortFunc);
		}
	}

	// Sort all rows in each TBODY (tbodies are sorted independent of each other)
	// --------------------------------------------------------------------------
	// TODO: Finish this functionality!
	// TODO: Keep track of filters already in place so re-filtering works!
	
	table.FilteredClassName = "TableFiltered";
	table.FilterableClassName = "filterable";
	
	table.filter = function(t,filter,args) {
		var colIndex, rowShade;
		if (!defined(args)) { args = new Object(); }
		
		// Resolve colIndex
		if (!defined(args['colIndex'])) { 
			colIndex = (defined(t) && defined(t.cellIndex))?t.cellIndex:0;
		}
		else {
			colIndex = args['colIndex'];
		}
		
		// Resolve the table
		if (t==null) { return; }
		if (t.nodeName && t.nodeName!="TABLE") {
			t = table.getTable(t);
		}
		
		// If the table doesn't have an ID, give it one
		CSS.createId(t);
		
		var bodies = table.getBodies(t);
		if (bodies==null || bodies.length==0) { return; }
		for (var i=0; i<bodies.length; i++) {
			var tb = bodies[i];
			var rows = new Array();
			for (var j=0; j<tb.rows.length; j++) {
				var row = tb.rows[j];
				if ("none"!=CSS.getStyle(row,"display")) {
					if (row.cells && colIndex < row.cells.length) {
						var val = table.getCellValue(row.cells[colIndex]);
						if (val!=filter) {
							row.style.display="none";
							continue;
						}
					}
				}
			}
		}
		
		// Loop through all THEADs and add filtered class names
		// TODO: Remove filter if it is set to null!
		var theads = table.getHeads(t);
		for (var i=0; i<theads.length; i++) {
			var th = theads[i];
			if (defined(th.rows) && th.rows.length>0) { 
				for (var j=0; j<th.rows.length; j++) { 
					var row = th.rows[j];
					if (defined(row.cells) && row.cells.length>0 && colIndex<row.cells.length) { 
						if (CSS.hasClass(row.cells[colIndex],table.FilterableClassName)) {
							CSS.addClass(row.cells[colIndex],table.FilteredClassName);
						}
					}
				}
			}
		}
		
		// Shade rows is a class name was supplied
		if (defined(args['rowShade'])) {
			table.shadeOddRows(t,args['rowShade']);
		}
	}
	
	
	// Shade alternate rows
	// --------------------
	table.shadeOddRows = function(t,className) { 
		if (t==null) { return; }
		if (t.nodeName && t.nodeName!="TABLE") {
			t = table.getTable(t);
		}
		var bodies = table.getBodies(t);
		if (bodies==null || bodies.length==0) { return; }
		for (var i=0; i<bodies.length; i++) {
			var tb = bodies[i];
			var rowNum=0;
			for (var j=0; j<tb.rows.length; j++) {
				if ("none"!=CSS.getStyle(tb.rows[j],"display")) {
					if (rowNum++%2==0) { 
						CSS.removeClass(tb.rows[j],className);
					}
					else {
						CSS.addClass(tb.rows[j],className);
					}
				}
			}
		}
		
	}
	
	return table;
})();
