
var Widget = require ('./widget');

module.exports = new Class
({
	Extends: Widget
	,Tag: 'htk-grid'
	,Child: 'model'
	,Properties:
	{
		/**
		 * The source data model.
		 **/
		model:
		{
			type: Db.Model
			,set: function (x)
			{
				this.link ({_model: x},
				{
					 'status-changed': this.onModelChange
					,'row-deleted': this.onRowDelete
					,'row-updated': this.onRowUpdate
					,'row-inserted': this.onRowInsert
					,'updatable-changed': this.onUpdatableChange
				});

				var set = new Db.SimpleIterator ({model: x});
				this.link ({_set: set});
				
				this.onUpdatableChange ();
				this.onModelChange ();
			}
			,get: function ()
			{
				return this._model;
			}
		},
		/**
		 * Message that should be displayed when source model is not ready.
		 **/
		emptyMessage:
		{
			type: String
			,value: null
		},
		/**
		 * Wether to display the header with column titles.
		 **/
		showHeader:
		{
			type: Boolean
			,set: function (x)
			{
				this._showHeader = x;
				this.thead.style.display = x ? 'table-header-group' : 'none';
			}
			,get: function ()
			{
				return this._showHeader;
			}
		}
	}

	,_model: null
	,_set: null
	,columns: new Array ()
	,internalColumn: null
	,internalColumns: 0
	,sortColumn: -1
	,sortWay: null
	,_showHeader: true
	
	,render: function ()
	{
		var table = this.createRoot ('table');
		table.className = 'htk-grid';

		var thead = this.createElement ('thead');
		table.appendChild (thead);
		
		this.thead = this.createElement ('tr')
		thead.appendChild (this.thead);
		
		this.tbody = this.createElement ('tbody');
		table.appendChild (this.tbody);
	}
	
	,appendChild: function (child)
	{
		this.appendColumn (child);
	}

	,removeClicked: function (column, value, row)
	{
		if (confirm (_('ReallyDelete')))
			this._model.deleteRow (row);
	}

	,onRowDelete: function (model, row)
	{
		var tableRows = this.tbody.childNodes;
		this.tbody.removeChild (tableRows[row]);
		
		for (var i = row; i < tableRows.length; i++)
			tableRows[i].className = (i % 2) ? 'pair-row' : '';
	
		this.showNoRecordsFound ();
	}

	,onRowInsert: function (model, row)
	{
		this.buildRow (1);
	}

	,renderCell: function (row, column, tr)
	{
		if (column.columnIndex != -1)
			column.value = this._model.data[row][column.columnIndex];

		if (column.renderer)
		{
			this._set.row = row;
			column.renderer (column, this._set);
		}

		return column.render (tr);
	}
	
	,refreshRow: function (row, columns)
	{
		var x = this.columns;
		var tr = this.tbody.childNodes[row];
		
		for (var i = 0; i < x.length; i++)
		if (x[i].renderer || columns.indexOf (x[i].columnIndex) != -1)
		{
			var cell = this.renderCell (row, x[i], tr);
			tr.replaceChild (cell, tr.childNodes[i]);
		}
	}

	,onRowUpdate: function (model, row, columns)
	{
		this.refreshRow (row, columns);
	}

	,buildRow: function (count)
	{
		for (var i = 0; i < count; i++)
		{
			var tr = this.createElement ('tr');
		
			if (i % 2)
				tr.className = 'pair-row';

			for (var j = 0; j < this.columns.length; j++)
			{
				var cell = this.renderCell (i, this.columns[j], tr);
				tr.appendChild (cell);
			}

			this.tbody.appendChild (tr);
		}
	}

	,onUpdatableChange: function ()
	{
		if (this._model && this._model.updatable)
		{
			if (!this.internalColumn)
			{
				this.internalColumn = new Htk.ColumnButton
				({
					 icon: 'delete'
					,tip: _('Remove')
				});
				this.internalColumn.on ('clicked', this.removeClicked, this);
				this.insertInternalColumn (0, this.internalColumn);
			}
		}
		else if (this.internalColumn)
		{
			this.internalColumn = null;
			this.removeInternalColumn (0);
		}
	}

	,onModelChange: function ()
	{
		var emptyMessage = this.emptyMessage ?
			this.emptyMessage : _('NoData');
	
		if (!this._model)
		{
			this.showMessage (emptyMessage, 'refresh.svg');
			return;
		}

		this._node.removeChild (this.tbody);
		this.tbody = this.createElement ('tbody');

		switch (this._model.status)
		{
			case Db.Model.Status.READY:
			{
				for (var i = 0; i < this.columns.length; i++)
					this.columns[i].updateColumnIndex (this._model);
			
				this.buildRow (this._model.numRows);
				this.showNoRecordsFound ();
				break;
			}
			case Db.Model.Status.LOADING:
				this.showMessage (_('Loading'), null);
				break;
			case Db.Model.Status.CLEAN:
				this.showMessage (emptyMessage, 'refresh');
				break;
			case Db.Model.Status.ERROR:
				this.showMessage (_('ErrorLoadingData'), 'error');
				break;
		}

		this._node.appendChild (this.tbody);
	}

	,showNoRecordsFound: function (count)
	{
		if (this._model.numRows == 0)
			this.showMessage (_('EmptyList'), 'clean');
	}

	,showMessage: function (message, src)
	{
		if (this.columns.length == 0)
			return;
	
		var tr =  this.createElement ('tr');
		this.tbody.appendChild (tr);

		var td = this.createElement ('td');
		td.className = 'message';
		td.colSpan = this.columns.length;
		tr.appendChild (td);
		
		if (src)
		{
			var img = this.createElement ('img');
			img.alt = '';
			img.src = 'image/icon/light/'+ src +'.svg';
			td.appendChild (img);
		}
		else
		{
			var spinner = new Htk.Spinner ();
			spinner.start ();
			td.appendChild (spinner.node);
		}
		
		var message = this.createTextNode (message);
		td.appendChild (message);
	}
	
	,scrollToRow: function (row)
	{
		if (row >= 0)
		{
			var height = parseInt (this.tr.style.height);
			this.node.scrollTop = (row - 2) * height;
		
			if (this.selectedRow)
				this.selectedRow.style.backgroundColor = null;
		
			this.selectedRow = this.tbody.childNodes[row]
			this.selectedRow.style.backgroundColor = '#AAD';
		}
	}

	,sortModel: function (column)
	{
		var columnIndex = column.columnIndex;

		if (this._model && columnIndex != -1)
		{
			if (this.sortColumn === columnIndex
			&& this.sortWay === Db.Model.SortWay.ASC)
				this.sortWay = Db.Model.SortWay.DESC;
			else
				this.sortWay = Db.Model.SortWay.ASC;

			this.sortColumn = columnIndex;
		
			this._model.sort (columnIndex, this.sortWay);
		}
	}
	
	,columnChanged: function (column, row, newValue)
	{
		var columnIndex = column.columnIndex;

		if (columnIndex != -1)
			this._model.setByIndex (row, columnIndex, newValue);
	}
	
	,addColumn: function (pos, column)
	{
		var header = column.renderHeader ();

		if (pos == -1 || pos >= this.columns.length)
		{
			pos = this.columns.push (column) - 1;
			this.thead.appendChild (header);
		}
		else
		{
			this.columns.splice (pos, 0, column);
			this.thead.insertBefore (header, this.thead.childNodes[pos]);
		}

		header.addEventListener ('click', 
			this.sortModel.bind (this, column));
		header.title = _('Sort');

		column.on ('changed', this.columnChanged, this);
		
		var rows = this.tbody.childNodes;
		
		if (this._model && this._model.numRows > 0)
		for (var i = 0; i < rows.length; i++)
		{
			var cell = this.renderCell (i, column, rows[i]);
			rows[i].insertBefore (cell, rows[i].childNodes[pos+1]);
		}

		return pos;
	}

	,insertInternalColumn: function (pos, column)
	{
		if (pos < 0 || pos > this.internalColumns)
			pos = this.internalColumns;

		this.internalColumns++;
		return this.addColumn (pos, column);
	}

	,insertColumn: function (pos, column)
	{
		if (pos > 0)
			pos += this.internalColumns;

		return this.addColumn (pos, column) - this.internalColumns;
	}

	,appendColumn: function (column)
	{
		return this.insertColumn (-1, column);
	}

	,deleteColumn: function (pos)
	{
		if (pos < 0 || pos >= this.columns.length)
			return;

		this.columns.splice (pos, 1);
		this.thead.removeChild (this.thead.childNodes[pos]);
		
		var rows = this.tbody.childNodes;
		
		if (this._model && this._model.numRows > 0)
		for (var i = 0; i < rows.length; i++)
			rows[i].removeChild (rows[i].childNodes[pos]);
	}

	,removeInternalColumn: function (pos)
	{
		if (this.internalColumns == 0)
			return;
	
		if (pos < 0 || pos > this.internalColumns)
			pos = this.internalColumns;
	
		this.deleteColumn (pos);
		this.internalColumns--;
	}

	,removeColumn: function (pos)
	{
		if (pos > 0)
			pos += this.internalColumns;
		
		this.deleteColumn (pos);
	}

	,reloadModel: function ()
	{
		if (this._model != null)
			this.onModelChange ();
	}
});

