/**
 * sorTable widget
 * adds headers to table which allow sorting
 * @version 0.2.2 2009-06-30
 * @author Gregor Kofler
 * 
 * @param {Object} table table or tbody (when several tbodies in one table) element
 * @param {Array} columnFormat optional array describing column format, core.js String.toDateTime()
 * 
 * served events: "beginSort", "finishSort"
 * 
 * @todo expose method to force sorting
 * @todo provide optional column to sort upon startup
 */

if(!this.vxJS) { throw Error("widget.sorTable: vxJS core missing."); }

vxJS.widget.sorTable = function(table, columnFormat, cb) {
	var	th, tb, rows = [],cols = [], h, w, activeColumn, that = {};

	var detectType = function(s) {
		if(!s) {
			return null;
		}
	};

	var analyzeColumns = function() {
		var r = tb.rows, i, j, t;

		for (i = 0; i < w; ++i) {
			if(columnFormat[i] === "no_sort") {
				cols.push({ ndx: i, elem: th.rows[0].cells[i], sortFunction: null });
				continue;
			}
			for (j = 0; j < h; ++j) {
				if ((t = vxJS.dom.concatText(r[j].cells[i]))) {
					break;
				}
			}
			cols.push({ ndx: i, asc: true, elem: th.rows[0].cells[i], sortFunction: detectType(t) });
		}
	};

	var findColumn = function(elem) {
		var i;

		for(i = w; i--;) {
			if(cols[i].elem === elem) {
				return cols[i];
			}
		}
	};

	var getColumnValues = function(ndx) {
		var i, f = columnFormat[ndx], cb;
		if(!f || !/^(?:date_(?:iso|us|de)|time_hms?|float(?:_comma)?)$/.test(f)) {
			for(i = h; i--;) {
				rows[i].sortValue = vxJS.dom.concatText(rows[i].elem.cells[ndx]).toLowerCase();
			}
			return;
		}
		else {
			if(f === "float") {
				cb = function(v) {
					return /^[+\-]?(?:\d{1,3}(?:[ ,\x27]\d{3})+|\d+)(?:\.\d+)?$/.test(v) ? v.replace(/[^0-9.\-]/g, "") : v;
				};
			}
			else if(f === "float_comma") {
				cb = function(v) {
					return /^[+\-]?(?:\d{1,3}(?:[ .\x27]\d{3})+|\d+)(?:,\d+)?$/.test(v) ? v.replace(/[^0-9,\-]/g, "").replace(",", ".") : v;
				};
			}
			else if(/^date_.+$/.test(f)) {
				cb =  function(v) {
					var d = v.toDateTime(f, true);
					return d ? d.format("%Y-%M-%D") : v;
				};
			}
			else {
				cb = function(v){
					return v.toDateTime(f);
				};
			}
		}
		for(i = h; i--;) {
			rows[i].sortValue = cb(vxJS.dom.concatText(rows[i].elem.cells[ndx]));
		}
	};

	var prepare = function() {
		var i;

		if (table.nodeName === "TBODY") {
			tb = table;
			table = tb.parentNode;
		}
		else if (table.nodeName === "TABLE") {
			tb = table.tBodies[0];
		}	
		else {
			throw Error("vxJS.widget.sorTable: No valid table or tbody element.");
		}

		if(!(th = table.getElementsByTagName("thead")[0])) {
			th = "thead".create();
			tb.parentNode.insertBefore(th, tb);
			th.appendChild(tb.rows[0]);
		}

		w = th.rows[0].cells.length;
		h = tb.rows.length;

		for (i = h; i--; ) {
			rows.push({
				elem: tb.rows[i],
				sortValue: null
			});
		}
	};

	var buildTable = function() {
		var t = document.createDocumentFragment(), i, l = rows.length;

		for(i = 0; i < l; i++) {
			t.appendChild(rows[i].elem);
		}
		tb.appendChild(t);
	};

	var listener = function(e) {
		var c, n = this;

		var cbSort = function(prop, asc, type){
			var s = asc ? 1 : -1;

			if(/^(?:float|float_comma)$/.test(type)) {
				return function(a, b) {
					a = +a[prop];
					if(isNaN(a)) { return 1; }
					b = +b[prop];
					if(isNaN(b)) { return -1; }
					if(a === b) { return 0; }
					return a < b ? -s : s;
				};
			}

			return function(a, b) {
				if(a[prop] === b[prop]) { return 0; }
				return a[prop] < b[prop] ? -s : s;
			};
		};

		while(!/th|td/i.test(n.nodeName) && n.parentNode) {
			n = n.parentNode;
		}

		if(!(c = findColumn(n)) || c.sortFunction === null) {	
			return;
		}
		if (activeColumn) {
			activeColumn.elem.className = activeColumn.elem.className.replace(/[ ]?vxJS_sorTable_header_(?:asc|desc)/, "");
		}
		if(c === activeColumn) {
			c.asc = !c.asc;
		}
		else {
			activeColumn = c;
		}

		c.elem.className += " vxJS_sorTable_header_" + (c.asc ? "asc" : "desc");

		vxJS.event.serve(that, "beginSort");

		getColumnValues(c.ndx);

		rows.sort(cbSort("sortValue", c.asc, columnFormat[c.ndx]));
		buildTable();

		vxJS.event.serve(that, "finishSort");
	};

	if(!columnFormat || !columnFormat.length) {
		columnFormat = [];
	}

	prepare();
	analyzeColumns();

	vxJS.event.addListener(th, "click", listener);
	
	that.element = tb;
	that.getActiveColumn = function() {
		return activeColumn;
	};

	return that;
};