/******************************************
* 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,
            currencySymbols: ['$', '\u20ac']
        };
        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){
                switch(element._sortType){
                    case '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;
                        }
                        break;
                    case 'currency':
                        var curr = cells[this.sortIndex].collectTextNodes();
                        this.options.currencySymbols.each(function(c){
                            if (curr.startsWith(c))
                                curr = curr.substr(1, curr.length - 1).strip();
                        })
                        if(!isNaN(curr))
                            curr = parseFloat(curr).toPaddedString(20);

                        sortVal = curr + sortVal;
                        break;
                    default:
                        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();
        this.resetSort();
    },
    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...')));
    }
});
