/*
 Copyright (C) 2008 Nu Echo Inc.

 This file requires the Prototype.js library.

 Implementation of a generic table with sortable columns.
*/


var SortableColumn = Class.create({
   initialize: function(label, contentProvider, sorter, width) {
      this.label = label;
      this.contentProvider = contentProvider;
      this.sorter = sorter;
      this.width = width;
   },

   getLabel: function () {
      return this.label;
   },

   getContent: function(data) {
      return this.contentProvider(data);
   },

   getSorter: function() {
      return this.sorter;
   },
   
   getWidth: function() {
	   return this.width;
   }
});

var SortableTable = Class.create({
   initialize: function(dataProvider, columns, parentId) {
      this.dataProvider = dataProvider;
      this.columns = columns;
      this.data = [];
      this.parentId = parentId;
      this.sortedColumnIndex = 0;
      this.sortUp = false;
      this.page = 0;
      this.lastpage = 0;
      this.itemsPerPage = 10;
      this.failed = false;
      $(this.parentId)['$tableController'] = this; 
      this.updateData();
   },

   displayRefreshStatus: function(status)
   {
      var refreshStatus = document.getElementById('refresh_status');

	  if (refreshStatus != null) 
	  {
		  for (var child = refreshStatus.firstChild; child != null; child = child.nextSibling)
		  {
			  refreshStatus.removeChild(child);
		  }
		  
		  refreshStatus.appendChild(document.createTextNode(status));
	  }
   },
   
   refresh: function(newData) {	   
      this.displayRefreshStatus('...refreshing...');
		  
      var container = this.createContainer();      
      this.createTableHeaders(container);
      this.displayRows(container);
      this.displayPageControls(container);
      $(this.parentId).innerHTML = this.createInnerHTML(container);
      
      if (this.failed)
      {
    	  this.updateFailed();
      }
   },

   updateFailed: function() {
	   if (! this.failed)
	   {
		   this.failed = true;
	   }
	   
	   $('refresh_status').innerHTML = "<span class='error_status'>Table update failed</span>";
       $('error_diagnosis').innerHTML = "<p class='error_message'>Unable to connect to NuGram Server. Try refreshing the page using the browser. If the error persists, contact the <a href='/html_app/doc/support'>support team</a>.</p>.";
   },
   
   createInnerHTML: function(container) {
	   var table = "<table class='dynamic'>" + container.innerHTML + "</table>";
	   var control = this.createTableSizeControls();
	   return  control.innerHTML + table + "<div id='error_diagnosis'></div>";
   },
   
   
   createContainer: function() {
	   var container = document.createElement('container');
	   this.incorporateColumnsWidth(container);
	   
       return container;
   },

   incorporateColumnsWidth: function(container) {
	   for ( columnIndex = this.columns.length - this.tableNumberColumns(); 
	       	 columnIndex < this.columns.length; columnIndex++ )
	   {
		   var col = document.createElement("col");
		   col.setAttribute('width', this.columns[columnIndex].getWidth());
		   container.appendChild(col);
	   }	 		
   },
   
   tableNumberColumns: function() {
	   return this.columns.length;
   },
      
   createTableSizeControls: function() {
	   var parentId = this.parentId;

	   var controlsDiv = document.createElement('div');
	   var container = document.createElement('container');	   
	   var controlsDiv = document.createElement('div');
	   controlsDiv.setAttribute('class', 'table_size_control');
       	   
	   var statusDiv = document.createElement('div');
	   statusDiv.setAttribute('id', 'refresh_status');
	   
	   var test = 2;
	   
	   if (this.refreshTimestamp)
	   {
		   statusDiv.appendChild(document.createTextNode(this.refreshTimestamp.toDateString()	
				   + ', '+ this.refreshTimestamp.toLocaleTimeString()
		   ));
	   }
	   
	   var selectorDiv = document.createElement('div');
	   selectorDiv.setAttribute('id', 'nb_items_selector');

	   if ( ! this.failed )
	   {
	      var refreshButton = document.createElement('span');
	      refreshButton.setAttribute('id', 'refresh_button');
	      refreshButton.setAttribute('class', 'button');
	      refreshButton.setAttribute('onclick', '$("' + parentId + '").$tableController.updateData();');
	      refreshButton.appendChild(document.createTextNode('Refresh table'));
	   }


	   if (refreshButton)
	   {
		   controlsDiv.appendChild(refreshButton);
	   }

       controlsDiv.appendChild(statusDiv);
	   
	   controlsDiv.appendChild(selectorDiv);
	   container.appendChild(controlsDiv);
		   
	   var currentSize = this.itemsPerPage;
	   var createButton = function(size) {
		   var sizeLabel = size == Infinity ? "All" : size;
		   if (size == currentSize) {
			   return " <span class='disabled-button'>" + sizeLabel + "</span>";
		   }
		   else {
			   return " <span class='button' onclick='$(\"" + parentId + "\").$tableController.setItemsPerPage(" + size + ")'>" + sizeLabel + "</span>";
		   }
	   };
	   selectorDiv.innerHTML = "Items per page:" + createButton(10) + createButton(20) + createButton(50) + createButton(100) + createButton(Infinity);
	   
	   return container;
   },

   createColumnHeader: function(tr, columnIndex) {
	   var th = document.createElement('th');
       th.setAttribute('class', 'clickable');
       th.setAttribute('id', 'column-' + columnIndex);
       th.setAttribute('onclick', "$(\"" + this.parentId + "\").$tableController.sortColumn(" + columnIndex + ")"); 
       th.setAttribute('title', 'Click to sort');
       var sortDirection = "";
       if (columnIndex == this.sortedColumnIndex) {
      	 sortDirection = (this.sortUp ? " \u25BC" : " \u25B2");
       }
       th.appendChild(document.createTextNode(this.columns[columnIndex].getLabel()));
       th.appendChild(document.createTextNode(sortDirection));
       tr.appendChild(th);
       return th;
   },
   
   createTableHeaders: function(container)  {
      var tr = document.createElement('tr');
      for(columnIndex = 0; columnIndex < this.columns.length; columnIndex++) {
    	  this.createColumnHeader(tr, columnIndex)   
      }
      
      container.appendChild(tr);
   },
   
   updateData: function() {
	   this.displayRefreshStatus('...updating...');
	   this.dataProvider(true);
   },
	   
   dataUpdated: function() {	   
	   this.refreshTimestamp = new Date();

	   this.data = this.dataProvider(false).concat([]);	   
	   this.sortData();
	   	   
	   var nbElements = this.data.length;
	   this.lastpage = (Math.ceil( nbElements / this.itemsPerPage));
	   if (this.lastpage > 0) {
		   this.lastpage--;
	   }
	   if (this.page > this.lastpage) {
		   this.page = this.lastpage;
	   }

	   this.refresh(true);
   },

   displayRows: function(container) {
	   var data = this.data;
	   if (data.length == 0) {
		   // Warn that there is no data to be displayed
		   var tr = document.createElement('tr');
		   var td = document.createElement('td');
		   
		   td.setAttribute('colspan', this.tableNumberColumns());
		   td.appendChild(document.createTextNode("No data to display."));
		   
		   tr.appendChild(td);
		   container.appendChild(tr);
		   return;
	   }

	   var start = 0;
	   var end = data.length;
	   if (this.itemsPerPage != Infinity) {
		   start = this.page * this.itemsPerPage;
		   end = Math.min((this.page + 1) * this.itemsPerPage, data.length);
	   }
	   
	   for(rowIndex = start; rowIndex < end; rowIndex++) {
		   var entry = data[rowIndex];
		   this.displayRow(container, entry, rowIndex);
	   }
   },
   
   displayRow: function(container, entry, rowIndex) {

	   var tr = document.createElement('tr');
	   for(columnIndex = 0; columnIndex < this.columns.length; columnIndex++) {
		   this.displayRowItem(tr, entry, rowIndex, columnIndex);
	   }
	   container.appendChild(tr);
   },
   
   displayRowItem: function(container, entry, rowIndex, columnIndex) {
	   var column = this.columns[columnIndex];
	   var td = document.createElement('td');
	   td.setAttribute('class', (rowIndex % 2 == 0) ? 'odd' : 'even');
	   td.appendChild(column.contentProvider(entry));
	   container.appendChild(td);
	   return td;
   },

   displayPageControls: function(container) {
	   if (this.data.length <= 0)
		   return;

	   var id = this.parentId;
	   var createButton = function(label, tooltip, enabled, funName) {
		   var button = document.createElement('span');
		   button.setAttribute('class', enabled ? 'button' : 'disabled-button');
		   button.setAttribute('title', tooltip);
		   if (enabled) {
			   button.setAttribute('onclick', "$(\"" + id + "\").$tableController." + funName + "()");
		   }
		   button.appendChild(document.createTextNode(label));
		   return button;
	   };

	   var tr = document.createElement('tr');
	   var td = document.createElement('td');
	   var tdDiv = document.createElement('div');
	   td.appendChild(tdDiv);
	   td.setAttribute('align', 'center');
	   td.setAttribute('width', "100%");
	   td.setAttribute('colspan', this.tableNumberColumns());
	   tr.appendChild(td);
	   container.appendChild(tr);
	   var page = this.page;
	   var lastpage = this.lastpage;
	   var data = this.data;

	   var innerTable = document.createElement('table');
	   innerTable.setAttribute('width', '100%');
	   td.appendChild(innerTable);
	   var innerTr = document.createElement('tr');
	   innerTr.setAttribute('width', '100%');
	   innerTable.appendChild(innerTr);

	   var leftDiv = document.createElement('td');
	   leftDiv.setAttribute('align', 'left');
	   leftDiv.appendChild(createButton('<<', 'Go to first page', page > 0, 'firstPage'));
	   leftDiv.appendChild(document.createTextNode("  "));
	   leftDiv.appendChild(createButton('<', 'Go to previous page', page > 0, 'previousPage'));
	   innerTr.appendChild(leftDiv);

	   var pageIndicator = document.createElement('td');
	   pageIndicator.setAttribute('align', 'center');

	   var start = 1;
	   var end = data.length;
	   if (this.itemsPerPage != Infinity) {
		   start = (page * this.itemsPerPage) + 1;
		   end = Math.min((page + 1) * this.itemsPerPage, data.length);
	   }
	   var total = data.length;
	   pageIndicator.innerHTML = "<b> " + start + '-' + end + ' of ' + total + " </b>";
	   innerTr.appendChild(pageIndicator);

	   var rightDiv = document.createElement('td');
	   rightDiv.setAttribute('align', 'right');
	   rightDiv.appendChild(createButton('>', 'Go to next page', page < lastpage, 'nextPage'));
	   rightDiv.appendChild(document.createTextNode("  "));
	   rightDiv.appendChild(createButton('>>', 'Go to last page', page < lastpage, 'lastPage'));
	   innerTr.appendChild(rightDiv);
   },

   sortColumn: function(columnIndex) {
      if (this.sortedColumnIndex == columnIndex) 
      {
    	  this.selectSortedColumn(columnIndex, !(this.sortUp));
      }
      else {
    	 this.selectSortedColumn(columnIndex, false);
      }
   },

   selectSortedColumn: function(columnIndex, up) {
      this.sortedColumnIndex = columnIndex;
      this.sortUp = up;
      this.page = 0;
      this.sortData();
      this.refresh(false);	
   },
   
   sortData: function() {
	   this.data.sort(this.columns[this.sortedColumnIndex].getSorter()(this.sortUp));
	   //alert("sorted");
   },

   setItemsPerPage: function(itemsPerPage) {
      this.itemsPerPage = itemsPerPage;
      this.page = 0;
      this.refresh(false);
   },

   nextPage: function() {
      this.page++;
      this.refresh(false);
   },

   previousPage: function() {
      this.page--;
      this.refresh(false);
   },

   firstPage: function() {
      this.page = 0;
      this.refresh(false);
   },

   lastPage: function() {
      this.page = this.lastpage;
      this.refresh(false);
   }
});


/*
 CONTENT PROVIDERS
*/

function stringContent(x) {
   return document.createTextNode(x == null ? "" : x.toString());
}

function propertyContent(propertyName) {
   return function(x) {
      return stringContent(x[propertyName]);
   };
}


/* 
 SORTERS
*/

function propertySorter(property) {
   return function(a, b) {
      var a_prop = a[property];
      var b_prop = b[property];
      if (a_prop < b_prop)
	 return -1;
      if (a_prop > b_prop)
	 return 1;
      return 0;
   }
}

function propertySorterGenerator(property) {
	return function(sortUp) {
		return function(a,b){			
			var result = (propertySorter(property))(a, b);
			return sortUp ? -1 * result :  result;
		}
	}
}

function propertySorterGenerator(first_property, second_property) {
	return function(sortUp) {
		return function(a,b) {			
			var result = propertySorter(first_property)(a, b);
			
			if (result != 0) {
				return sortUp ? -1 * result :  result;
			}
			else {
				return propertySorter(second_property)(a, b)
			}
		}
	}
}

/*
 * SPECIALIZED TABLES
 */

/*
 * Table displaying cells over two rows 
 * (the first cell is displayed on its own row)
 */
var TwoRowsSortableTable = Class.create();

Object.extend(TwoRowsSortableTable.prototype, SortableTable.prototype); 

Object.extend(TwoRowsSortableTable.prototype, {

	createTableHeaders : function(container)  {		
	var tr = document.createElement('tr');
	
	topHeader = this.createColumnHeader(tr, 0);
	topHeader.setAttribute('colspan', this.tableNumberColumns());
	container.appendChild(tr);

	tr = document.createElement('tr');
	tr.setAttribute('class', 'smaller');
	
	for(columnIndex = 1; columnIndex < this.columns.length; columnIndex++) {		   
		var cell = this.createColumnHeader(tr, columnIndex);

		if (columnIndex == 1)
		{
			cell.setAttribute('class', cell.getAttribute('class') + " indented" );
		}
	}

	container.appendChild(tr);
},

displayRow: function(container, entry, rowIndex) {

	var tr = document.createElement('tr');
	var separator = document.createElement('td');	
	separator.setAttribute('colspan', this.tableNumberColumns() );
	separator.setAttribute('height', '2px');
		
	tr.appendChild(separator);
	container.appendChild(tr);
		
	tr = document.createElement('tr');
	var topCell = this.displayRowItem(tr, entry, rowIndex, 0);
	topCell.setAttribute('colspan', this.tableNumberColumns() );
	container.appendChild(tr);
	
	tr = document.createElement('tr');
	tr.setAttribute('class', 'smaller');
		
	for(columnIndex = 1; columnIndex < this.columns.length; columnIndex++) {
		var cell = this.displayRowItem(tr, entry, rowIndex, columnIndex);

		if (columnIndex == 1)
		{
			cell.setAttribute('class', cell.getAttribute('class') + " indented" );
		}
	}

	container.appendChild(tr);
},

tableNumberColumns: function() {
	   return this.columns.length - 1;
}

});
