/**
 * Provides functionality for sorting, paging and filtering a given XHTML-Table
 *
 * @access public
 * @version 0.1
 * @author Dominik Fettel, <fettel@navigate.de>
 * @param string
 * @return void
 * @requires Prototype 1.5.1
 * @requires Dataset 0.1
 * @requires Pager 0.1
 */
function JSGridView (rootDivId, rowSampleCount) {
	/**
	 * The DIV-Element, which contains a XHTML-Table
	 *
	 * @access public
	 * @var string
	 */
	this.rootDivId = rootDivId;

	/**
	 * The DOMNode-Reference of rootDiv
	 *
	 * @access public
	 * @var DOMNode
	 */
	this.rootDiv = null;

	/**
	 * True, if the TABLE-has a <thead>-Element
	 *
	 * @access public
	 * @var boolean
	 */
	this.hasHeaders = false;

	/**
	 * DOMNodeCollection of the <th>-Elements
	 *
	 * @access public
	 * @var DOMNodeCollection
	 */
	this.headers = null;

	/**
	 * DOMNodeCollection of <tr> in <tbody>
	 *
	 * @access public
	 * @var DOMNodeCollection
	 */
	this.rows = null;

	/**
	 * Short description of attribute thead
	 *
	 * @access public
	 * @var DOMNode
	 */
	this.thead = null;

	/**
	 * Short description of attribute tbody
	 *
	 * @access public
	 * @var DOMNode
	 */
	this.tbody = null;

	/**
	 * Short description of attribute tableData
	 *
	 * @access public
	 * @var Array
	 */
	this.tableData = new Array();

	/**
	 * The number of different rows types which should be sampled by rewriting
	 * table rows, i.e. 2 for alternative Rows (Black/White for instance).
	 *
	 * @access public
	 * @var int
	 */
	 if(rowSampleCount == null) this.rowSampleCount = 1;
	 else this.rowSampleCount = rowSampleCount;

	/**
	 * Copy of rowSampleCount-times-rows for rewriting the table
	 *
	 * @access public
	 * @var Array
	 */
	this.rowSamples = new Array();

	/**
	 * Holds pager-information. To change paging-object, reset this, because it
	 * public
	 *
	 * @access public
	 * @var Pager
	 */
	this.pager = null;

	/**
	 * Dataset-Instance for this.
	 *
	 * @access public
	 * @var Dataset
	 */
	this.dataset = null;
	/**
	 * Name of paging-navi-div. Defined by user or default.
	 *
	 * @access public
	 * @var string
	 */

	this.pagingNaviDivId = "";

	/**
	 * Callback-Function for click on Paging-Navi
	 *
	 * @access public
	 * @var JSFunction
	 */
	this.pagingNaviEvent = null;

	/**
	 * Initialising the Object (to call after the page has been loaded).
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @return void
	 */
	this.init = function () {
		this.rootDiv = $(this.rootDivId);
		var result = this.rootDiv.getElementsByTagName("THEAD");
		if(result.length == 1) {
			this.thead = result[0];
			var result2 = this.thead.getElementsByTagName("TH");
			if(result2.length > 0) {
				this.hasHeaders = true;
				this.headers = result2;
			}
		}
		var result = this.rootDiv.getElementsByTagName("TBODY");
		if(result.length == 1) {
			this.tbody = result[0];
			this.rows = this.tbody.getElementsByTagName("TR");
		}
		this.tableData = this.getTableData();
		for(var i=0;i<this.rowSampleCount;i++) {
			this.rowSamples.push(this.rows[i]);
		}
		this.pager = new Pager(this.rows.length);
		this.initDataSet();
	}
	/**
	 * Reads table-Data
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @return Array
	 */
	this.getTableData = function () {
		var returnValue = new Array();
		for(var i=0;i<this.rows.length;i++) {
			returnValue.push(this.getDataFromRow(this.rows[i]));
		}
		return returnValue;
	}

	/**
	 * Reading all <TD>-Contents from a given <TR>-Element
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param DOMNode
	 * @return Array
	 */
	this.getDataFromRow = function (inputRow) {
		var returnValue = new Array();
		var tds = inputRow.getElementsByTagName("TD");
		for(var i=0;i<tds.length;i++) {
			returnValue.push(this.getDataFromCell(tds[i]));
		}
		return returnValue;
	}

	/**
	 * Reading innerHTML from a given DOMNode, that means the Content.
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param DOMNode
	 * @return mixed
	 */
	this.getDataFromCell = function (inputCell){
		if(Prototype.Browser.IE) return inputCell.innerText;
		else return inputCell.textContent;
	}
	/**
	 * Clears the tbody.
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @return void
	 */
	this.clearTbody = function () {
		var len = this.rows.length;
		for(var i=0;i<len;i++) {
			this.tbody.removeChild(this.rows[0]);
		}
	}
	/**
	 * Initialize dataset by table-settings
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @return void
	 */
	this.initDataSet = function () {
		var columnTitles = new Array();
		if(this.hasHeaders) {
			for(var i=0;i<this.headers.length;i++) {
				columnTitles.push(this.getDataFromCell(this.headers[i]));
			}
		}
		this.dataset = new Dataset(this.tableData, columnTitles);
	}
	/**
	 * Appends the given row to tbody
	 *
	 * @access private
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param DOMNode
	 * @return void
	 */
	this.appendTbodyRow = function (row) {
		this.tbody.appendChild(row);
	}
	/**
	 * Samples a given row with the given data in it.
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param DOMNode
	 * @param Array
	 * @return DOMNode
	 */
	this.sampleRow = function ( sample,  data) {
		var returnValue = sample.cloneNode(true);
		var cells = returnValue.getElementsByTagName("TD");
		for(var i=0;i<cells.length;i++) {
			if(cells[i].lastChild) {
				if(cells[i].lastChild.innerHTML) cells[i].lastChild.innerHTML = data[i];
				else cells[i].lastChild.data = data[i];
			} else {
				cells[i].innerHTML =  data[i];
			}
		}
		 return returnValue;
	}
	/**
	 * Creates the Table using the samples from data of Dataset and using the
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @return void
	 */
	this.showTable = function (){
		this.clearTbody();
		var batch = this.dataset.resultData;
		this.pager.items = batch.length;
		this.pager.calc();
		if(this.pagingNaviDivId && this.pagingNaviEvent) {
			this.createPagingNavi(this.pagingNaviDivId, this.pagingNaviEvent);
		}
		for(var i=0;i<batch.length;i++) {
			if(this.pager.currentFromIndex <= i && i <= this.pager.currentToIndex) {
				var sampleNo = i % this.rowSampleCount;
				this.appendTbodyRow(this.sampleRow(this.rowSamples[sampleNo], batch[i]));
			}
		}
	}
	/**
	 * Creates paging-navi
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param string
	 * @return void
	 */
	this.createPagingNavi = function (divId, clickEvent) {
		if(divId == null) divId = "pager";
		/**
		 * @todo documentate and in definition
		 */
		this.pagingNaviDivId = divId;
		this.pagingNaviEvent = clickEvent;
		if($(divId)) {
			var pagingNavi = $(divId);
			var count = pagingNavi.childNodes.length;
			for(var i=0;i<count;i++) {
				pagingNavi.removeChild(pagingNavi.childNodes[0]);
			}
		} else {
			var pagingNavi = document.createElement("DIV");
		}
		pagingNavi.setAttribute("id", divId);
		for(var i=0;i<this.pager.pages;i++) {
			var pagingNaviPoint = document.createElement("A");
			pagingNaviPoint.appendChild(document.createTextNode(i+1));
			if((i+1)==this.pager.currentPage) pagingNaviPoint.className = "active";
			Event.observe(pagingNaviPoint, 'click', clickEvent);
			pagingNavi.appendChild(pagingNaviPoint);
		}
		this.rootDiv.appendChild(pagingNavi);
	}
	/**
	 * Creates sorting-functionality
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param string
	 * @return void
	 */
	this.addSorting = function (clickEvent)  {
		for(var i=0;i<this.headers.length;i++) {
			Event.observe(this.headers[i], 'click', clickEvent);
		}
	}
	/**
	 * Sets the Page
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param int
	 * @return void
	 */
	this.setCurrentPage = function(no) {
		this.pager.currentPage = no;
		this.pager.calc();
		this.showTable();
	}
	/**
	 * Toogles order direction by a header-node
	 * @param DOMNode
	 * @return void
	 */
	this.toogleOrderDirection = function(th) {
		for(var i=0;i<this.headers.length;i++) {
			if(this.headers[i]==th) {
				if(this.headers[i].hasClassName("asc")) {
					this.headers[i].removeClassName ("asc");
					this.headers[i].addClassName("desc");
				} else {
					this.headers[i].addClassName("asc");
					this.headers[i].removeClassName ("desc");
				}
				var orderConfig = new DataSetOrderConfiguration(i,this.headers[i].hasClassName("asc"), "");
				this.dataset.orderBy(orderConfig);
			} else {
				this.headers[i].removeClassName("asc");
				this.headers[i].removeClassName("desc");
			}
		}
		this.showTable();
	}
	/**
	 * CreatesSelection-Box like EXCEL-Auto-Filter
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param int
	 * @param JSFunction
	 * @param string
	 * @param string
	 * @return DOMNode
	 */
	this.createAutoFilter = function (col, callBack, id, defaultText) {
		if(id == null) id = "autoFilterCol"+col;
		if(defaultText == null) defaultText = "Bitte auswählen";
		if($(id)) var node = $(id);
		else var node = document.createElement("DIV");
		node.setAttribute("id", id);
		var select = document.createElement("SELECT");
		Event.observe(select,"change", callBack);
		var option = document.createElement("OPTION");
		option.setAttribute("value", "");
		option.appendChild(document.createTextNode(defaultText));
		select.appendChild(option);
		for(var i=0;i<this.dataset.cols[col].uniqueValues.length;i++) {
			var option = document.createElement("OPTION");
			option.setAttribute("value", this.dataset.cols[col].uniqueValues[i]);
			option.appendChild(document.createTextNode(this.dataset.cols[col].uniqueValues[i]));
			select.appendChild(option);
		}
		node.appendChild(select);
		if(!node.parentNode) this.rootDiv.appendChild(node);
		return node;
	}
	/**
	 * Short description of method createSearchField
	 *
	 * @access public
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param int
	 * @param JSFunction
	 * @param string
	 * @param string
	 * @return void
	 */
	this.createSearchField = function (col, callBack, id, defaultText) {
		if($(id)) {
			var node = $(id);
		} else {
			var node = document.createElement("INPUT");
			if(id == null) id = "autoFilterCol"+col;
			node.setAttribute("id", id);
			this.rootDiv.appendChild(node);
		}
		if(typeof(defaultText) == "undefined") defaultText = "Suche nach "+this.dataset.cols[col].title;
		node.setAttribute("value", defaultText);
		Event.observe(node, "keyup", callBack);
	}
	/**
	 * Creates a dropdown for count of the items per page
	 *
	 * @param JSFunction
	 * @param string
	 * @param Array
	 * @param string
	 */
	this.createNumPerPageDropDown = function (callBack, id, entries, text) {
		if($(id)) {
			var node = $(id);
		} else {
			var node = document.createElement("DIV");
			node.setAttribute("id", "numPerPage");
			this.rootDiv.appendChild(node);
		}
		if(entries == null) entries = new Array("10", "20", "50");
		if(text == null) text = " pro Seite";
		var select = document.createElement("SELECT");
		var option = document.createElement("OPTION");
		option.setAttribute("value", this.pager.numPerPage);
		option.innerHTML = this.pager.numPerPage+text;
		select.appendChild(option);
		for(var i=0;i<entries.length;i++) {
			var option = document.createElement("OPTION");
			option.setAttribute("value", entries[i]);
			option.innerHTML = entries[i]+text;
			select.appendChild(option);
		}
		node.appendChild(select);
		Event.observe(select, "change", callBack);
	}
	/**
	 * Clears a given INPUT-Field (useful for search-field)
	 *
	 * @access private
	 * @author Dominik Fettel, <fettel@navigate.de>
	 * @param Event
	 * @return void
	 */
	this.clearInputField = function (event) {
		Event.element(event).value = "";
	}
}
