// TableImport created and licensed by fry @ friedcellcollective
// http://friedcellcollective.net/js/
// This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License (http://creativecommons.org/licenses/by-sa/2.5/)
// Relies on http://www.json.org/json.js
// Version: 0.6

// CHANGELOG
// 0.5 - first version
// 0.6 - supports split by chunk of whitespace

// Namespace
if (typeof(FCC) === 'undefined') {
	var FCC = {};
}
FCC._ = function (elem, obj) {
	var prop = '';
	if (!elem) {
		elem = {};
	}
	for (prop in obj) {
		if (obj.hasOwnProperty(prop)) {
			elem[prop] = obj[prop];
		}
	}
	return elem;
};
FCC._(FCC, {
	doc: document,
	win: window
});
// DOM helpers
if (!FCC.DOM) {
	FCC.DOM = {};
}
FCC._(FCC.DOM, {
	ce: function (tag, text, obj) {
		var e = FCC.doc.createElement(tag);
		if (text) {
			e.appendChild(FCC.doc.createTextNode(text));
		}
		if (obj) {
			FCC._(e, obj);
		}
		return e;
	}
});
// TableImport
FCC.TableImport = (function () {
	var prvt = {
		types: {
			'null': {l: 0, s: 'null'},
			'int': {l: 1, s: 'int'},
			'float': {l: 2, s: 'float'},
			'boolean': {l: 3, s: 'boolean'},
			'string': {l: 4, s: 'string'}
		},
		splitRows: function (text) {
			return text.split(this.findNewline(text));
		},
		findNewline: function (text) {
			var nli = text.indexOf("\n");
			if (nli < 0) {
				return "\r";
			} else {
				if (text.charAt(nli - 1) === "\r") {
					return "\r\n";
				} else if (text.charAt(nli + 1) === "\r") {
					return "\n\r";
				}
			}
			return "\n";
		},
		splitCells: function (text, divider) {
			return text.split(divider || /\s{2,}/);
		},
		hasHeader: function (firstrow, types) {
			var difs = 0, i = 0, j = firstrow.length, currType = null;
			for (; i < j; i += 1) {
				currType = this.cellType(firstrow[i]);
				if (currType.l > types[i].l) {
					difs += 1;
				}
			}
			return difs >= types.length / 2;
		},
		fillRow: function (row, cols, header) {
			var i = 0;
			for (; i < cols; i += 1) {
				row[i] = row[i] || header && 'Column' + (i + 1) || null;
			}
			return row;
		},
		colType: function (column) {
			var type = this.types['null'],
				i = 0, j = column.length,
				currType = null;
			for (; i < j; i += 1) {
				currType = this.cellType(column[i]);
				if (currType.l > type.l) {
					type = currType;
				}
			}
			return type;
		},
		cellType: function (value) {
			// value is always string as it comes from a text field!
			return (value === '' || value === 'null' || value === 'undefined') && this.types['null'] ||
				parseInt(value, 10) + '' === value && this.types['int'] ||
				parseFloat(value) + '' === value && this.types['float'] || 
				parseFloat(value) + '' === value.substr(0, value.indexOf('.')) && parseInt(value.substr(value.indexOf('.') + 1), 10) === 0 && this.types['float'] || // nums that are .0*
				(value.toLowerCase() === 'true' || value.toLowerCase() === 'false') && this.types['boolean'] ||
				this.types['string'];
		},
		convertRow: function (row, types) {
			var i = 0, j = row.length,
				type = null;
			for (; i < j; i += 1) {
				type = types[i];
				if (type.s === 'null') {
					row[i] = null;
				} else if (type.s === 'int') {
					row[i] = parseInt(row[i], 10);
				} else if (type.s === 'float') {
					row[i] = parseFloat(row[i]);
				} else if (type.s === 'boolean') {
					row[i] = !(row[i].toLowerCase() === 'false' || !row[i]);
				}
			}
			return row;
		}
	}, pblc = {
		convert: function (text, divider, header, types) {
			// init
			var i = 0, j = 0, k = 0, l = 0, t = 0,
				r = {data: []},
				rows = prvt.splitRows(text), cols = [],
				hr = false, currType = null, temp = null, tempo = {};
			// parse into rows and cols (without first line)
			for (i = 0, j = rows.length; i < j; i += 1) {
				rows[i] = prvt.splitCells(rows[i], divider);
				for (k = 0, l = rows[i].length; k < l; k += 1) {
					if (!cols[k]) {
						cols[k] = [];
					}
					if (i > 0) {
						cols[k].push(rows[i][k]);
					}
				}
			}
			// determine body types
			if (types) {
				for (i = 0, j = types.length; i < j; i += 1) {
					t = prvt.types[types[i]];
					if (t === null) {
						t = {l: 100, s: types[i]};
					}
					types[i] = t;
				}
			} else {
				types = [];
				for (i = 0, j = cols.length; i < j; i += 1) {
					types[i] = prvt.colType(cols[i]);
				}
			}
			// determine header. if no header we need to reconsider the types
			hr = header || prvt.hasHeader(rows[0], types);
			if (hr) {
				r.header = prvt.fillRow(rows.shift(), cols.length, true);
			} else {
				for (i = 0, j = rows[0].length; i < j; i += 1) {
					currType = prvt.cellType(rows[0][i]);
					if (currType.l > types[i].l) {
						types[i] = currType;
					}
				}
			}
			r.types = types;
			// parse data to JS
			for (i = 0, j = rows.length; i < j; i += 1) {
				// last line: if empty break
				if (i === j - 1 && rows[i].length <= 1 && rows[i][0] === '') {
					break;
				}
				// fill and convert
				temp = prvt.convertRow(prvt.fillRow(rows[i], cols.length), types);
				// header means object, not array
				if (hr) {
					tempo = {};
					for (k = 0, l = temp.length; k < l; k += 1) {
						tempo[r.header[k]] = temp[k];
					}
					r.data.push(tempo);
				} else {
					r.data.push(temp);
				}
			}
			return r;
		},
		convert2JSON: function () {
			return this.convert.apply(this, arguments).toJSONString();
		},
		convert2Table: function () {
			var i = 0, j = 0, k = 0, l = 0,
				obj = this.convert.apply(this, arguments),
				table = FCC.DOM.ce('table'), thead = null, tbody = FCC.DOM.ce('tbody'), tr = null,
				val = '';
			if (obj.header) {
				thead = FCC.DOM.ce('thead');
				tr = FCC.DOM.ce('tr');
				for (i = 0, j = obj.header.length; i < j; i += 1) {
					tr.appendChild(FCC.DOM.ce('th', obj.header[i]));
				}
				table.appendChild(thead).appendChild(tr);
			}
			for (i = 0, j = obj.data.length; i < j; i += 1) {
				tr = FCC.DOM.ce('tr');
				l = obj.header && obj.header.length || obj.data[i].length;
				for (k = 0; k < l; k += 1) {
					val = obj.header && obj.data[i][obj.header[k]] || obj.data[i][k];
					tr.appendChild(FCC.DOM.ce('td', '' + val));
				}
				tbody.appendChild(tr);
			}
			table.appendChild(tbody);
			return table;
		},
		convert2Tabdelimited: function () {
			var i = 0, j = 0, k = 0, l = 0,
				obj = this.convert.apply(this, arguments), val = null, div = '',
				t = '';
			if (obj.header) {
				for (i = 0, j = obj.header.length; i < j; i += 1) {
					t += div + obj.header[i];
					div = '\t';
				}
				t += '\n';
			}
			for (i = 0, j = obj.data.length; i < j; i += 1) {
				l = obj.header && obj.header.length || obj.data[i].length;
				div = '';
				for (k = 0; k < l; k += 1) {
					val = obj.header && obj.data[i][obj.header[k]] || obj.data[i][k];
					t += div + val;
					div = '\t';
				}
				t += '\n';
			}
			return t;
		},
		convert2Input: function () {
			var i = 0, j = 0, k = 0, l = 0,
				obj = this.convert.apply(this, arguments), val = null,
				fieldset = FCC.DOM.ce('fieldset');
			if (obj.header) {
				for (i = 0, j = obj.header.length; i < j; i += 1) {
					fieldset.appendChild(FCC.DOM.ce('input', null, {value: obj.header[i], name: 'header_' + i, type: 'hidden'}));
				}
			}
			for (i = 0, j = obj.data.length; i < j; i += 1) {
				l = obj.header && obj.header.length || obj.data[i].length;
				for (k = 0; k < l; k += 1) {
					val = obj.header && obj.data[i][obj.header[k]] || obj.data[i][k];
					fieldset.appendChild(FCC.DOM.ce('input', null, {value: val, name: 'cell_' + i + '_' + k, type: 'hidden'}));
				}
			}
			if (obj.types) {
				for (i = 0, j = obj.types.length; i < j; i += 1) {
					fieldset.appendChild(FCC.DOM.ce('input', null, {value: obj.types[i].s, name: 'type_' + i, type: 'hidden'}));
				}
			}
			return fieldset;
		}
	};
	return pblc;
}());
