/******************************************
* Sortable table
******************************************/
var SortableTable = Class.create({
	constants: {
      	sortMonthNames: ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'],
      	sortAsc: 'asc',
      	sortDesc: 'desc',
      	classSortSuffix: 'Sort',
      	classHighlightTR: 'highlight',
      	classGridTBody: 'grid',
      	classZebraTR: 'zebra',
      	classLoading: 'loading'
    },
	initialize: function(el, options) {
		this.options = {
            columnSortTypes: [],
            defaultSortType: 'normal',
            sortPrimaryColumn: 0,
            initialSort: false,
            initialSortColumn: 0,
            initialSortOrder: 'asc',
            zebra: false,
            highlight: false,
            grid: false
        };
        Object.extend(this.options, options || { });
        this.element = $(el);
		this.tbody = this.element.down('tbody');
		this.thead = this.element.down('thead');
		this.thead.headers = this.thead.down('tr').childElements();
		this.initTableSort();
		this.initDisplayOptions();
	},
	initTableSort: function(){
		if(this.options.initialSort){
            this.sortIndex = this.options.initialSortColumn;
            this.sortOrder = this.options.initialSortOrder;
            this.thead.headers[this.options.initialSortColumn].addClassName(this.options.initialSortOrder + this.constants.classSortSuffix);
        }else{
            this.sortIndex = -1;
            this.sortOrder = this.constants.sortAsc;
        }
		this.thead.headers.each(function(el, i){
			el._colIndex = i;
			if(this.options.columnSortTypes[i] == null)
				el._sortType = this.options.defaultSortType;
			else
				el._sortType = this.options.columnSortTypes[i];
		}.bind(this));
		this.element.observe('click', this.handleHeaderClick.bindAsEventListener(this));
   	},
   	initDisplayOptions: function(){
   		this.toggleZebra(this.options.zebra);
		this.toggleGrid(this.options.grid);
		this.toggleHighlight(this.options.highlight);
		this.enableRowWatch();
   	},
   	resetSort: function(){
   		this.thead.headers.invoke('removeClassName',this.sortOrder + this.constants.classSortSuffix);
      	if(this.options.initialSort){
            this.sortIndex = this.options.initialSortColumn;
            this.sortOrder = this.options.initialSortOrder;
            this.thead.headers[this.options.initialSortColumn].addClassName(this.options.initialSortOrder + this.constants.classSortSuffix);
        }else{
            this.sortIndex = -1;
            this.sortOrder = this.constants.sortAsc;
        }
   	},
   	handleHeaderClick: function(event){
      	var element = event.element();
      	if(!('_colIndex' in element)){
      		element = element.ancestors().find(function(el)	{
      			return '_colIndex' in el;
      		});
      		if(!((element) && '_colIndex' in element))
      			return;
      	}
      	if (element._sortType == null || element._sortType == 'none')
      		return;
      	this.bookKeeping(element);
    },
	bookKeeping: function(element){
		if(this.sortIndex != -1)
			this.thead.headers[this.sortIndex].removeClassName(this.sortOrder + this.constants.classSortSuffix);

		if(this.sortIndex != element._colIndex){
			this.sortOrder = this.constants.sortAsc;
			this.sortIndex = element._colIndex;
		}else
			this.sortOrder = (this.constants.sortAsc == this.sortOrder ? this.constants.sortDesc : this.constants.sortAsc);
		this.thead.headers[this.sortIndex].addClassName(this.sortOrder + this.constants.classSortSuffix);
		this.sort(element);
	},
	sort: function(element){
		var rows = this.tbody.childElements();
		rows = rows.sortBy(function(row) {
			var cells = row.childElements();
			var sortVal;
			if(!(this.options.sortPrimaryColumn == null))
				sortVal = cells[this.options.sortPrimaryColumn].collectTextNodes();
			if (this.sortIndex != this.options.sortPrimaryColumn){
				if(element._sortType == 'pradate'){
					var praDate = cells[this.sortIndex].innerHTML;
					if (praDate.strip() == 'n/a')
						sortVal = '3001010100:00' + sortVal;
					else{
						praDate = praDate.split('-');
						var day = praDate[0];
						var month = praDate[1];
						var year = praDate[2].substr(0,4);
						var time = praDate[2].substr(5,praDate[2].length-5);
						month = this.constants.sortMonthNames.indexOf(month.toUpperCase())+1;
						if (month < 10)
							month = '0' + month;
						sortVal = year+month+day+time+ sortVal;
					}
				}else
					sortVal = cells[this.sortIndex].collectTextNodes() + sortVal;
			}
			return sortVal.toUpperCase();
		}.bind(this));
		if(this.constants.sortAsc == this.sortOrder)
			rows.reverse();
		rows.each(function(row, i){
			if(i > 0)
				this.tbody.insertBefore(row, rows[i-1]);
		}.bind(this));
		this.toggleZebra(this.options.zebra);
	},
    toggleHighlight: function(force){
    	if(force == null)
    		this.options.highlight = (this.options.highlight ? false : true);
    	else
    		this.options.highlight = force;
    },
    toggleZebra: function(force){
    	this.tbody.select('tr.'+this.constants.classZebraTR).invoke('removeClassName', this.constants.classZebraTR);

    	if(force == null)
    		this.tbody.select('tr.'+this.constants.classZebraTR).invoke('toggleClassName', this.constants.classZebraTR);
    	else if(force)
    		this.tbody.select('tr:nth-child(even)').invoke('addClassName', this.constants.classZebraTR);

    	if(force == null)
    		this.options.zebra = (this.options.zebra ? false : true);
    	else
    		this.options.zebra = force;
    },
    toggleGrid: function(force){
    	if(force == null)
    		this.tbody.toggleClassName(this.constants.classGridTBody);
    	else if(force)
    		this.tbody.addClassName(this.constants.classGridTBody);
    	else
    		this.tbody.removeClassName(this.constants.classGridTBody);

    	//work around for Firefox bug
      	this.tbody.childElements().each(function(r){
      		this.tbody.insert(r);
      	}.bind(this));
      	//end workaround

      	if(force == null)
    		this.options.grid = (this.options.grid ? false : true);
    	else
    		this.options.grid = force;
    },
    enableRowWatch: function(){
    	this.tbody.childElements().each(function(r){
    		r.observe('mouseover',this.handleRowOver.bind(this, r));
			r.observe('mouseout',this.handleRowOut.bind(this, r));
    	}.bind(this));
    },
	handleRowOver: function(row){
		if(this.options.highlight)
			row.addClassName(this.constants.classHighlightTR);
	},
	handleRowOut: function(row){
		if(this.options.highlight)
			row.removeClassName(this.constants.classHighlightTR)
	},
	updateTBody: function(newTBody){
		this.tbody.replace(newTBody);
		this.tbody = this.element.down('tbody');
		this.initDisplayOptions();
	},
	showLoading: function(classLoading){
		if(classLoading == null)
			classLoading = this.constants.classLoading;
		this.tbody.update(Builder.node('tr', Builder.node('td', {className: classLoading, colSpan: this.thead.headers.length}, 'Loading...')));
	}
});