define([
	'./Grid',
	'dojo/_base/declare',
	'put-selector/put'
], function (Grid, declare, put) {
	// summary:
	//		This module supports parsing grid structure information from an HTML table.
	//		This module does NOT support ColumnSets; see GridWithColumnSetsFromHtml

	// name of data attribute to check for column properties
	var bagName = 'data-dgrid-column';

	function getSubRowsFromDom(domNode) {
		// summary:
		//		generate columns from DOM. Should this be in here, or a separate module?
		var columns = [], // to be pushed upon / returned
			trs = domNode.getElementsByTagName('tr'),
			trslen = trs.length,
			getCol = GridFromHtml.utils.getColumnFromCell,
			rowColumns, tr, ths, thslen;

		for (var i = 0; i < trslen; i++) {
			rowColumns = [];
			columns.push(rowColumns);
			tr = trs[i];
			ths = tr.getElementsByTagName('th'), thslen = ths.length;
			for (var j = 0; j < thslen; j++) {
				rowColumns.push(getCol(ths[j]));
			}
		}
		if (tr) {
			// NOTE: this assumes that applicable TRs were ONLY found under one
			// grouping element (e.g. thead)
			domNode.removeChild(tr.parentNode);
		}

		return columns;
	}

	var GridFromHtml = declare(Grid, {
		configStructure: function () {
			// summary:
			//		Configure subRows based on HTML originally in srcNodeRef
			if (!this._checkedTrs) {
				this._checkedTrs = true;
				this.subRows = getSubRowsFromDom(this.srcNodeRef, this.subRows);
			}
			return this.inherited(arguments);
		},

		create: function (params, srcNodeRef) {
			// We need to replace srcNodeRef, presumably a table, with a div.
			// (Otherwise we'll generate highly invalid markup, which IE doesn't like)
			var div = document.createElement('div'),
				id = srcNodeRef.id,
				style = srcNodeRef.getAttribute('style');

			// Copy some commonly-used attributes...
			if (id) {
				this.id = id; // Will be propagated in List's create
			}
			div.className = srcNodeRef.className;
			style && div.setAttribute('style', style);

			// replace srcNodeRef in DOM with the div
			srcNodeRef.parentNode.replaceChild(div, srcNodeRef);

			(params = params || {}).srcNodeRef = srcNodeRef;
			// call inherited with the new node
			// (but configStructure will look at srcNodeRef)
			this.inherited(arguments, [params, div]);

			// destroy srcNodeRef for good now that we're done with it
			put(srcNodeRef, '!');
		}
	});

	// hang some utility functions, potentially useful for extensions
	GridFromHtml.utils = {
		// Functions for getting various types of values from HTML attributes
		getBoolFromAttr: function (node, attr) {
			// used for e.g. sortable
			var val = node.getAttribute(attr);
			return val && val !== 'false';
		},
		getNumFromAttr: function (node, attr) {
			// used for e.g. rowSpan, colSpan
			var val = node.getAttribute(attr);
			val = val && Number(val);
			return isNaN(val) ? undefined : val;
		},
		getPropsFromNode: function (node) {
			// used to pull properties out of bag e.g. "data-dgrid-column".
			var obj,
				str = node.getAttribute(bagName);
			if (!str) {
				return {};
			}

			try {
				/* jshint evil: true */
				// Yes, eval is evil, but this is ultimately the same thing that
				// dojo/parser does for objects.
				obj = eval('(' + str + ')');
			} catch (error) {
				throw new Error('Error in ' + bagName + ' {' + str + '}: ' + error.toString());
			}
			return obj;
		},

		// Function for aggregating th attributes into column properties
		getColumnFromCell: function (th) {
			var getNum = GridFromHtml.utils.getNumFromAttr,
				obj,
				tmp;

			// Look for properties in data attribute.
			// It's imperative that we hold on to this object as returned, as the
			// object may be augmented further by other sources,
			// e.g. Grid adding the grid property to reference the instance.
			obj = GridFromHtml.utils.getPropsFromNode(th);

			// inspect standard attributes, but data attribute takes precedence
			obj.label = 'label' in obj ? obj.label : th.innerHTML;
			obj.field = obj.field || th.className || th.innerHTML;
			if (!obj.className && th.className) {
				obj.className = th.className;
			}
			if (!obj.rowSpan && (tmp = getNum(th, 'rowspan'))) {
				obj.rowSpan = tmp;
			}
			if (!obj.colSpan && (tmp = getNum(th, 'colspan'))) {
				obj.colSpan = tmp;
			}

			return obj;
		}
	};
	return GridFromHtml;
});