/**
 * xhrTable
 * 
 * edit contents of table rows with AJAX support
 * @version 0.4.0 2009-03-12
 * @author Gregor Kofler
 * 
 * @param {Object} tableElem DOM table element
 * @param {Object} xhr request with optional xhr parameters (uri)
 * @param {Object} config additional parameters to configure widget
 *	requests:		{Object} request commands
 *	columnIndex:	{String}  buttons in first column if "first", otherwise last is chosen
 *	buttons:		{Object} optional button elements
 *	SingleEdit:		{Boolean} allow only single editable row
 */
/*global vxJS */

if(!vxJS || !vxJS.xhr) { throw new Error("widget.xhrTable: vxJS core or vxJS.xhr missing."); }

vxJS.widget.xhrTable = function(tableElem, xhrReq, config) {
	if(!tableElem || !tableElem.nodeName || tableElem.nodeName != "TABLE" && tableElem.nodeName != "TBODY") {
		throw new Error("widget.xhrTable: Missing table.");
	}

	var	table = {
			rows: [],
			cols: [],
			header: [],
			footer: [],
			width: null
		},
		tableHead,
		that = {}, xhr, listButtons = ["edit", "del"], editButtons = ["cancel", "submit"], allButtons, lastEdited;
	
	buttons = config.buttons || {};
	requests = config.requests || {
		edit: "xhrTable_edit",
		submit: "xhrTable_submit",
		cancel: "xhrTable_cancel",
		add: "xhrTable_add",
		del: "xhrTable_del"
	};

	xhrReq = xhrReq || {};
	xhrReq.echo = true;

	var showEditButtons = function(row) {
		var i, b = row.buttons;
		for(i = allButtons.length; i--;) {
			b[allButtons[i]].style.display = editButtons.inArray(allButtons[i]) ? "" : "none";
		}
		
		if(config.singleEdit) {
			if(lastEdited) {
				restoreRow(lastEdited);
				showListButtons(lastEdited);
			}
			lastEdited = row;
		}
	};

	var showListButtons = function(row) {
		var i, b = row.buttons;
		for(i = allButtons.length; i--;) {
			b[allButtons[i]].style.display = listButtons.inArray(allButtons[i]) ? "" : "none";
		}
		if(config.singleEdit) {
			lastEdited = null;
		}
	};

	var addButtons = function(row) {
		var i, j, r = row ? [row] : table.rows, c, l, b;
		
		for(i = editButtons.length; i--;) {
			if(!buttons[editButtons[i]]) {
				editButtons.splice(i, 1);
			}
		}
		for(i = listButtons.length; i--;) {
			if(!buttons[listButtons[i]]) {
				listButtons.splice(i, 1);
			}
		}
		allButtons = listButtons.concat(editButtons);

		for(i = allButtons.length; i--;) {
			buttons[allButtons[i]].className += (buttons[allButtons[i]].className ? " " : "") + "vxJS_xhrTable_"+allButtons[i]+"Element";
			buttons[allButtons[i]].style.display = listButtons.inArray(allButtons[i]) ? "" : "none";
		}

		for(i = r.length; i--;) {
			r[i].buttons = {};
			c = r[i].elem.childNodes[config.columnIndex === "first" ? 0 : r[i].elem.childNodes.length-1];
			vxJS.dom.deleteChildNodes(c);

			for(j = 0, l = allButtons.length; j < l; ++j) {
				b = buttons[allButtons[j]].cloneNode(true);
				r[i].buttons[allButtons[j]] = b;
				c.appendChild(b);
			}
		}
	};

	var backupRow = function(r) {
		var i, l;

		if (r.elem && r.elem.childNodes) {
			r.backupRow = [];
			for (i = 0, l = r.elem.childNodes.length; i < l; ++i) {
				if (!i && config.columnIndex === "first" || i === l - 1) { continue; }
				r.backupRow.push(r.elem.childNodes[i].cloneNode(true));
			}
		}
	};

	var restoreRow = function(r) {
		var i, l, c, e, rows;

		if(!r.backupRow) {
			e = r.elem;
			rows = table.rows;
			for (i = rows.length; i--;) {
				if (rows[i].elem === e) {
					rows.splice(i, 1);
					break;
				}
			}
			e.parentNode.removeChild(e);
			return;
		}

		c = r.elem.childNodes;
		for(i = 0, l = c.length; i < l; ++i ) {
				if (!i && config.columnIndex === "first" || i === l - 1) { continue; }
			c[i].parentNode.replaceChild(r.backupRow[i], c[i]); 
		}
		delete r.backupRow;
	};

	var findRow = function(key) {
		var i, ndx, r = table.rows;
		if(/(number|string)/.test(typeof key)) {
			ndx = "key";
		}
		else if(key.nodeType) {
			ndx = "elem";
		}
		else {
			return;
		}
		for(i = r.length; i--; ) {
			if(r[i][ndx] == key) {
				return r[i];
			}
		}
	};

	var analyzeTable = function(){
		var i, l, p = 0, cls, r;
		
		vxJS.dom.cleanDOM(tableElem);
		
		if (tableElem.nodeName !== "TBODY") {
			if (!tableElem.tBodies) {
				throw new Error("widget.xhrTable: no table body found.");
			}
			if(!tableElem.tBodies[0]) {
				tableElem.appendChild("tbody".create());
			}
			tableElem = tableElem.tBodies[0];
		}
		r = tableElem.rows;
		
		if (!r || !r.length || !r[0].cells) {
			tableHead = tableElem.parentNode.getElementsByTagName("thead")[0];
			if (!tableHead || !tableHead.rows || !tableHead.rows[0].cells) {
				throw new Error("widget.xhrTable: no table row found.");
			}
			table.width = tableHead.rows[0].cells.length;
			return;
		}

		table.width = r[0].cells.length;

		for(i = 0, l = r.length; i < l; i++) {
			if ((cls = r[i].className.match(/vxJS_xhrTable_key_([a-z0-9_\-]+)/i))) {
				table.rows.push({
					key: cls[1],
					elem: r[i]
				});
			}
			else {
				table.rows.push({
					key: p++,
					elem: r[i]
				});
			}
		}
	};

	var getValues = function(elem) {
		//todo optionsfelder, select-multiple
		
		var i, v = {}, fe;

		fe = elem.getElementsByTagName("input");
		
		if(fe && fe.length) {
			for(i = fe.length; i--;) {
				if(fe[i].disabled || fe[i].type === "checkbox" && !fe[i].checked || /^(?:submit|button|image)$/.test(fe[i].type)) {
						continue;
				}
				v[fe[i].name] = fe[i].value;
			}
		}

		fe = elem.getElementsByTagName("select");
		if(fe && fe.length) {
			for(i = fe.length; i--;) {
				if(fe[i].disabled) {
					continue;
				}
				if (fe[i].type === "select-one") {
					v[fe[i].name] = fe[i].value;
				}
				else {
					
				}
			}
		}
			
		fe = elem.getElementsByTagName("textarea");
		if(fe && fe.length) {
			for(i = fe.length; i--;) {
				if(fe[i].disabled) {
					continue;
				}
				v[fe[i].name] = vxJS.dom.concatText(fe[i]);
			}
		}

		return v;
	};

	var addRow = function(key) {
		var e = "tr".setProp("class", "vxJS_xhrTable_key_"+key).create([].fill("", table.width).domWrapWithTag("td")), r;

		table.rows.push( r = {
			key: key,
			elem: e
		} );
		tableElem.appendChild(e);
		return r;
	};
	
	var buildRow = function(elem, cells) {
		var e, i, j, c = elem.childNodes, cb, todo = [];

		for(i = 0; i < c.length; ++i) {
			if (!cells[i]) {
				continue;
			}

			if ((e = cells[i].elements)) {
				vxJS.dom.deleteChildNodes(c[i]);
				c[i].appendChild(vxJS.dom.parse(e));
			}

			if((cb = cells[i].callbacks)) {
				if(!cb.length ) {
					cb = [cb];
				}
				for (j = 0; j < cb.length; ++j) {
					if (cb[j].f && typeof that[cb[j].f] === "function") {
						todo.push({
							cell: c[i],
							f: cb[j].f,
							args: cb[j].args || []
						});
					}
				}
			}
		}

		for(i = 0; i < todo.length; ++i) {
			that[todo[i].f].apply(todo[i].cell, todo[i].args);
		}
	};

	var handleXhrTableEdit = function(resp){
		var k = resp.echo.key, r = resp.response, row;

		if(!k || !r || !r.cells || !r.cells.length) { return; }

		row = findRow(k);

		backupRow(row);
		buildRow(row.elem, r.cells);
		showEditButtons(row);
	};

	var handleXhrTableAdd = function(resp) {
		var r = resp.response, row;
		if(!r || !r.key || !r.cells || findRow(r.key)) { return; }

		row = addRow(r.key);
		buildRow(row.elem, r.cells);
		addButtons(row);
		showEditButtons(row);

		vxJS.event.addListener(row.elem, "click", handleClick);
	};

	var handleXhrTableSubmit = function(resp) {
		var k = resp.echo.key, r = resp.response, row, i, n, t;

		if(!k || !r || !r.status) { return; }

		row = findRow(k);

		if(row.prevErr) {
			for(i = row.prevErr.length; i--;){
				n = row.prevErr[i];
				n.className = n.className.replace(/(^| )error$/, "");
			}
		}

		if(r.status === "ok") {
			buildRow(row.elem, r.cells);

			showListButtons(row);
			
			if(r.newKey) {
				row.key = r.newKey;
				row.elem.className = "vxJS_xhrTable_key_"+r.newKey;
			}
			return;
		}
		
		if((r.status === "error") && r.elements) {
			row.prevErr = [];
			for(i = r.elements.length; i--;) {
				if((n = vxJS.dom.getFirstElementByName(row.elem, r.elements[i].name)) && (t = vxJS.dom.getParentElement(n, "td"))) {
					t.className += " error";
					row.prevErr.push(t);
				}
			}
		}
	};

	var handleXhrTableDel = function(resp) {
		var k = resp.echo.key, r = resp.response, i, n;

		if(!k || !r || !r.status || r.status !== "ok") { return; }

		n = table.rows;
		for(i = n.length; i--; ) {
			if(n[i].key == k) {
				n[i].elem.parentNode.removeChild(n[i].elem);
				table.rows.splice(i, 1);
				break;
			}
		}
	};

	var handleXhrResponse = function(r) {
		var cb, i;

		if(!r.echo || !r.echo.httpRequest) { return; }
		
		if(typeof that["handle_"+r.echo.httpRequest] === "function") {
			that["handle_"+r.echo.httpRequest](r);
			return;
		}
		
		switch (r.echo.httpRequest) {
			case requests.edit:
				handleXhrTableEdit(r);
				break;
			case requests.submit:
				handleXhrTableSubmit(r);
				break;
			case requests.del:
				handleXhrTableDel(r);
				break;
			case requests.add:
				handleXhrTableAdd(r);
				break;
		}
	
		if(r.response && (cb = r.response.callbacks)) {
			if(!cb.length) {
				cb = [cb];
			}
			for(i = 0; i < cb.length; ++i) {
				if(typeof that[cb[i].f] === "function") {
					that[cb[i].f].apply(that, cb[i].param || []);
				}
			}
		}
	};

	var handleClick = function(e) {
		var cls, elem, r;

		if(/disabled/.test(this.className) || this.disabled) { return; }

		if (!(cls = this.className.match(/vxJS_xhrTable_([a-z]+)Element/i))) { return; }

		if(typeof that["handle"+cls[1].toUcFirst()] === "function") {
			if(!that["handle"+cls[1].toUcFirst()](this)) {
				return;
			}
		}

		if (!requests[cls[1]]) {
			return;
		}

		if (!(elem = e.currentTarget)) {
			elem = vxJS.dom.getParentElement(this, "tr");
		}
		
		if(!(r = findRow(elem))) {
			return;
		}
		
		if(cls[1] === "cancel") {
			restoreRow(r);
			showListButtons(r);
			return;
		}
		
		xhrReq.command = requests[cls[1]];
		if(xhr) {
			xhr.use(null, null, { key: r.key, elements: cls[1] === "submit" ? getValues(r.elem) : null });
			return;
		}
		xhr = vxJS.xhr(
			xhrReq,
			{ completed: handleXhrResponse },
			{ key: r.key, elements: cls[1] === "submit" ? getValues(r.elem) : null }
		);
	};

	var addListeners = function() {
		var i;
		for(i = table.rows.length; i--;) {
			vxJS.event.addListener(table.rows[i].elem, "click", handleClick);
		}
	};
	
	analyzeTable();
	addButtons();
	addListeners();
	/**
	 * add row to table and request content from server
	 */
	that.addRow = function() {
		xhrReq.command = requests.add;
		if(xhr) {
			xhr.use();
			return;
		}
		xhr = vxJS.xhr(
			xhrReq,
			{ completed: handleXhrResponse }
		);
	};

	/**
	 * deletes row, when server confirms deletion
	 */
	that.delRow = function(key) {
		xhrReq.command = requests.add;
		if(xhr) {
			xhr.use(null, null, { key: key} );
			return;
		}
		xhr = vxJS.xhr(
			xhrReq,
			{ completed: handleXhrResponse },
			{ key: key } 
		);
	};

	return that;
};