/**
* Heihō: simple spreadsheet viewer
* @link https://github.com/kktsvetkov/heiho
*/

;(function(root, factory)
{
	if (typeof exports === 'object')
	{
		module.exports = factory(window, document)
	} else
	{
		root.Heiho = factory(window, document)
  	}
})(this, function(w, d)
{
	/**
	* Count number of columns in array or object
	*
	* @param {Array|Object} data
	* @return {Integer}
	*/
	function cols(data)
	{
		var cols = 0;
		for (var i in data)
		{
			var l = 0;
			if ('object' == typeof data[i])
			{
				if (Array.isArray(data[i]))
				{
					l = data[i].length;
				} else
				{
					l = Object.keys( data[i] ).length;
				}
			}

			if (l > cols)
			{
				cols = l;
			}
		}

		return cols;
	}

	/**
	* Creates the DOM elements of the preview
	*
	* @param {String} id common prefix for ids of DOM elements
	* @return {Object} collection of elements
	*/
	function load(id)
	{
		var el = {};

		/* outter preview shell */
		el.shell = document.createElement('div');
		el.shell.setAttribute('id', id);
		el.shell.style.display = 'none';

		/* preview header */
		el.header = document.createElement('div');
		el.header.setAttribute('id', id + '-header');
		el.shell.appendChild(el.header);

		/* preview header close button */
		el.close = document.createElement('div');
		el.close.setAttribute('id', id + '-close');
		el.header.appendChild(el.close);

		/* preview header title caption */
		el.title = document.createElement('div');
		el.title.setAttribute('id', id + '-title');
		el.header.appendChild(el.title);

		/* scrollable wrap of the preview grid  */
		el.scroll = document.createElement('div');
		el.scroll.setAttribute('id', id + '-scroll');
		el.shell.appendChild(el.scroll);

		/* preview table grid */
		el.table = document.createElement('table');
		el.table.setAttribute('id', id + '-table');
		el.scroll.appendChild(el.table);

		/* preview grid thead */
		el.thead = document.createElement('tr');
		el.thead.setAttribute('id', id + '-thead');
		el.table.appendChild(el.thead);

		/* preview grid tbody */
		el.tbody = document.createElement('tbody');
		el.tbody.setAttribute('id', id + '-tbody');
		el.table.appendChild(el.tbody);

		/* preview truncate warning */
		el.truncate = document.createElement('div');
		el.truncate.setAttribute('id', id + '-truncated');
		el.truncate.style.display = 'none';
		el.shell.appendChild(el.truncate);

		/*
		* shell
		*  |
		*  + header
		*  |  |
		*  |  + close
		*  |  |
		*  |  + title
		*  |
		*  + scroll
		*  |  |
		*  |  + table
		*  |     |
		*  |     + thead
		*  |     |
		*  |     + tbody
		*  |
		*  + truncate
		*/

		document.body.appendChild(el.shell);
		return el;
	}

	/**
	* Return TH column title based on integer index
	*
	* Column titles are only alphabet letters, using the 26 ascii
	* uppercase chars, e.g. "B" for 2, "AA" for 27
	*
	* @param {Integer} i
	* @retun {String}
	*/
	function label(i)
	{
		i = parseInt(i);
		if (!i)
		{
			return '';
		}

		var h = '', j = i, k = 0;
		while (j > 26)
		{
			k = j % 26;
			j = Math.floor(j / 26);
			h = String.fromCharCode(64 + k) + h;
		}

		h = String.fromCharCode(64 + j) + h;
		return h;
	}

	/**
	* Resise the preview
	*
	* @param {Array} el collection of preview elements
	*/
	function resize(el)
	{
		/* span the grid to full page width */
		if (el.shell.offsetWidth > el.table.offsetWidth)
		{
			el.table.className += ' width100';
		}

		/* adjust height */
		el.scroll.style.height = '';
		var height = el.shell.offsetHeight
			- 2 /* bottom offset */
			- el.header.offsetHeight
			- el.truncate.offsetHeight;
		if (height < el.table.offsetHeight)
		{
			el.scroll.style.height = height + 'px';
		}
	}

	/**
	* @var {Object} default options
	*/
	var options = {

		/**
		* @var {String} prefix for all HTML ids used in the preview
		*/
		id: 'heiho-view',

		/**
		* @var {Integer} max number of rows to preview
		*/
		max: 100,

		/**
		* @var {Bool} whether to use first row as header or not
		*/
		header: null,

		/**
		* @var {Function} renders the preview title contents
		* @param {DomElement} el the preview element
		* @param {Object} o extra options
		*/
		title: function(el, o)
		{
			var title = 'Preview';
			if ('title' in o)
			{
				title = o.title;
			}

			el.innerHTML = title;
		},

		/**
		* @var {Function} renders the truncate warning contents
		* @param {DomElement} el the truncate element
		* @param {Integer} max
		* @param {Array|Object} data
		* @param {Object} o extra options
		*/
		truncate: function(el, max, data, o)
		{
			el.innerHTML = 'Showing only first ' + max + ' rows, ' + cols(data) + ' in total';
			el.style.display = '';
		}
	}

	/**
	* Constructor
	*
	* @param {Array|Object} data
	* @param {Object} o extra options
	*/
	function hh(data, o)
	{
		/* read options */
		o = o || {}
		for (var i in options)
		{
			if (i in o)
			{
				continue;
			}

			o[i] = options[i];
		}

		/* get the preview elements */
		var el = load(o.id);

		/* header title */
		var t = o; delete t.title;
		(typeof o.title === 'function')
			? o.title(el.title, t)
			: options.title(el.title, t);

		/* resize preview */
		var windowResize = function(event)
		{
			resize(el);
		}
		window.addEventListener('resize', windowResize);

		/* close button */
		const scrollReset = {top: 0, left: 0, behavior: 'auto'};
		var scrollTo = scrollReset;
		el.close.addEventListener('click', function (event)
		{
			document.body.classList.remove('heiho-body');
			document.body.removeChild(el.shell);
			window.removeEventListener('resize', windowResize);

			document.body.scrollTo( scrollTo );
			scrollTo = scrollReset;
		});

		var columns = cols(data);

		/* preview thead */
		el.thead.innerHTML = '';
		for (var i = 0; i <= columns; i++)
		{
			var th = document.createElement('th');
			th.innerHTML = label(i);
			el.thead.appendChild(th);
		}

		/* preview grid rows */
		el.tbody.innerHTML = '';
		el.truncate.innerHTML = '';

		var rows = 0;
		var header = [];
		for (var i in data)
		{
			if (o.max > 0 && ++rows > o.max)
			{
				(typeof o.truncate === 'function')
					? o.truncate(el.truncate, o.max, data, o)
					: options.truncate(el.truncate, o.max, data, o);
				break;
			}

			if (1 === rows)
			{
				header = data[i];
			}

			var tr = document.createElement('tr');

			var td = document.createElement('td');
			td.innerHTML = rows;
			tr.appendChild(td);

			for (var j in data[i])
			{
				td = document.createElement('td');
				td.innerHTML = data[i][j];
				tr.appendChild(td);
			}

			/* pad missing columns */
			if (columns > tr.childNodes.length)
			{
				while (tr.childNodes.length <= columns)
				{
					td = document.createElement('td');
					tr.appendChild(td);
				}
			}

			el.tbody.appendChild(tr);
		}

		/* first row is a header or not */
		if (null === o.header)
		{
			o.header = true;

			var j = 0;
			for (var i in header)
			{
				j++;

				if (!header[i])
				{
					o.header = false; /* empty header column */
					break;
				}

				if (!isNaN( parseFloat(header[i]) ))
				{
					o.header = false; /* number in header */
					break;
				}
			}

			if (false !== o.header)
			{
				if (j < columns)
				{
					o.header = false; /* too short  header row */
				}
			}
		}
		if (o.header)
		{
			el.tbody.firstChild.classList.add('heiho-header');
		}

		/* finally show the preview and hive everything else in the body */
		scrollTo.top = document.body.scrollTop;
		scrollTo.left = document.body.scrollLeft;
		document.body.classList.add('heiho-body');
		el.shell.style.display = '';
		resize(el);
	}

	var Heiho = hh;
	return Heiho;
});