//
// new SortingTable( 'my_table', {
//   zebra: true,     // Stripe the table, also on initialize
//   details: false   // Has details every other row
// });
//
// The above were the defaults.  The regexes in load_conversions test a cell
// begin sorted for a match, then use that conversion for all elements on that
// column.
//
// Requires mootools Class, Array, Function, Element, Element.Selectors,
// Element.Event, and you should probably get Window.DomReady if you're smart.
//

var SortingTable = new Class({

  initialize: function( table, options ) {
    this.options = $merge({
      zebra: true,
      details: false,
      paginator: false
    }, options);
    
    this.table = $(table);
    this.tbody = $(this.table.getElementsByTagName('tbody')[0]);
    if (this.options.zebra) {
      SortingTable.stripe_table( this.tbody.getElementsByTagName( 'tr' ) );
    }

    this.headers = new Hash;
    var thead = $(this.table.getElementsByTagName('thead')[0]);
    $each(thead.getElementsByTagName('tr')[0].getElementsByTagName('th'), function( header, index ) {
      var header = $(header);
      this.headers.set( header.getText(), { column: index, element: header } );
      header.addEvent( 'mousedown', function(evt){
        var evt = new Event(evt);
        this.sort_by_header( evt.target.getText() );
        if ( this.options.paginator) this.options.paginator.to_page( 1 );
      }.bind( this ) );
    }.bind( this ) );

    this.load_conversions();
  },

  sort_by_header: function( header_text ){
    this.rows = new Array;
    var trs = this.tbody.getElementsBySelector( 'tr, span' );
    while ( row = trs.shift() ) {
      row = { row: row.remove() };
      if ( this.options.details ) {
        row.detail = trs.shift().remove();
      }
      this.rows.unshift( row );
    }
    
    var header = this.headers.get( header_text );
    if ( this.sort_column >= 0 && this.sort_column == header.column ) {
      // They were pulled off in reverse
      if ( header.element.hasClass( 'reverseSort' ) ) {
        header.element.removeClass( 'reverseSort' );
        header.element.addClass( 'forwardSort' );
      } else {
        header.element.removeClass( 'forwardSort' );
        header.element.addClass( 'reverseSort' );
      }
    } else {
      this.headers.each(function(h){
        h.element.removeClass( 'forwardSort' );
        h.element.removeClass( 'reverseSort' );
      });
      this.sort_column = header.column;
      if (header.conversion_function) {
        this.conversion_matcher = header.conversion_matcher;
        this.conversion_function = header.conversion_function;
      } else {
        this.conversion_function = false;
        this.rows.some(function(row){
          var to_match = $(row.row.getElementsByTagName('td')[this.sort_column]).getText();
          if (to_match == ''){ return false }
          this.conversions.some(function(conversion){
            if (conversion.matcher.test( to_match )){
              this.conversion_matcher = conversion.matcher;
              this.conversion_function = conversion.conversion_function;
              return true;
            }
            return false;
          }.bind( this ));
          if (this.conversion_function){ return true; }
          return false;
        }.bind( this ));
        header.conversion_function = this.conversion_function.bind( this );
        header.conversion_matcher = this.conversion_matcher;
        this.headers.set( header_text, header );
      }
      header.element.addClass( 'forwardSort' );
      this.rows.each(function(row){
        row.compare_value = this.conversion_function( row );
        row.toString = function(){ return this.compare_value }
      }.bind( this ));
      this.rows.sort();
    }

    var index = 0;
    while ( row = this.rows.shift() ) {
      row.row.injectInside( this.tbody );
      if (row.detail){ row.detail.injectInside( this.tbody ) };
      if ( row.row.getTag() == 'tr' ) {
      if ( this.options.zebra ) {
        row.row.className = row.row.className.replace( this.removeAltClassRe, '$1').clean();
        if (row.detail){
          row.detail.className = row.detail.className.replace( this.removeAltClassRe, '$1').clean();
        }
        if ( ( index % 2 ) == 0 ) {
          row.row.addClass( 'alt' );
          if (row.detail){ row.detail.addClass( 'alt' ); }
        }
      }
      index++;
      }
    }
    this.rows = false;
  },

  load_conversions: function() {
    this.conversions = $A([
      // 1.75 MB, 301 GB, 34 KB, 8 TB
      { matcher: /([0-9.]{1,8}).*([KMGT]{1})B/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).getText();
          cell = this.conversion_matcher.exec( cell );
          if (!cell) { return '0' }
          if (cell[2] == 'M') {
            sort_val = '1';
          } else if (cell[2] == 'G') {
            sort_val = '2';
          } else if (cell[2] == 'T') {
            sort_val = '3';
          } else {
            sort_val = '0';
          }
          var i = cell[1].indexOf('.')
          if (i == -1) {
            post = '00'
          } else { 
            var dec = cell[1].split('.');
            cell[1] = dec[0];
            post = dec[1].concat('00'.substr(0,2-dec[1].length));
          }
          return sort_val.concat('00000000'.substr(0,2-cell[1].length).concat(cell[1])).concat(post);
        }
      },
      // 1 day ago, 4 days ago, 38 years ago, 1 month ago
      { matcher: /(\d{1,2}) (.{3,6}) ago/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).getText();
          cell = this.conversion_matcher.exec( cell );
          if (!cell) { return '0' }
          var sort_val;
          if (cell[2].indexOf('month') != -1) {
            sort_val = '1';
          } else if (cell[2].indexOf('year') != -1) {
            sort_val = '2';
          } else {
            sort_val = '0';
          }
          return sort_val.concat('00'.substr(0,2-cell[1].length).concat(cell[1]));
        }
      },
      // Numbers
      { matcher: /^\d+$/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).getText();
          return '00000000000000000000000000000000'.substr(0,32-cell.length).concat(cell);
        }
      },
      // Fallback 
      { matcher: /.*/,
        conversion_function: function( row ) {
          return $(row.row.getElementsByTagName('td')[this.sort_column]).getText();
        }
      }
    ]);
  }

});

SortingTable.removeAltClassRe = new RegExp('(^|\\s)alt(?:\\s|$)');
SortingTable.implement({ removeAltClassRe: SortingTable.removeAltClassRe });

SortingTable.stripe_table = function ( tr_elements  ) {
  var counter = 0;
  $$( tr_elements ).each( function( tr ) {
    //if ( tr.style.display != 'none' && !tr.hasClass('collapsed') ) {
      counter++;
    //}
    tr.className = tr.className.replace( this.removeAltClassRe, '$1').clean();
    if ( !(( counter % 2 ) == 0) ) {
      tr.addClass( 'alt' );   
    }
  }.bind( this ));
}

var PaginatingTable = new Class({

  initialize: function( tbl, ids, options ) {
    this.options = $merge({
      per_page: 10,
      current_page: 1,
      offset_el: false,
      cutoff_el: false
    }, options);
    
    this.table = $(tbl);
    this.tbody = this.table.getElementsByTagName( 'tbody' )[0];
    
    if (this.options.offset_el)
      this.options.offset_el = $(this.options.offset_el);
    if (this.options.cutoff_el)
      this.options.cutoff_el = $(this.options.cutoff_el);

    this.paginators = $A();
    if ($type(ids) == 'array'){
      $each(ids,function(id){
        this.paginators.push( $(id) );
      }.bind( this ));
    } else {
      this.paginators.push( $(ids) );
    }

    this.update_pages();
  },

  update_pages: function(){
    this.pages = Math.ceil( this.tbody.getElementsByTagName('tr').length / this.options.per_page );
    this.create_pagination();
    this.to_page( 1 );
  },

  to_page: function( page_num ) {
    page_num = parseInt( page_num );
    if (page_num > this.pages || page_num < 1) return;
    this.current_page = page_num;
    this.low_limit  = this.options.per_page * ( this.current_page - 1 );
    this.high_limit = this.options.per_page * this.current_page;
    var trs = this.tbody.getElementsByTagName('tr')
    if (trs.length < this.high_limit) this.high_limit = trs.length;
    for (var index=0; index < trs.length; index++) {
      if ( this.low_limit  <= index &&
           this.high_limit > index    ) {
        trs[index].style.display = '';
      } else {
        trs[index].style.display = 'none';
      }
    }
    this.paginators.each(function(paginator){
      var as = paginator.getElementsByTagName( 'a' );
      for (var index=0;index < as.length;index++){
        if (index == this.current_page) {
          as[index].addClass('currentPage');
        } else {
          as[index].removeClass('currentPage');
        }
      }
    }.bind( this ));
    if (this.options.offset_el)
      this.options.offset_el.setText(this.low_limit+1);
    if (this.options.cutoff_el)
      this.options.cutoff_el.setText(this.high_limit);
  },

  to_next_page: function() {
    this.to_page( this.current_page + 1 );
  },

  to_prev_page: function() {
    this.to_page( this.current_page - 1 );
  },

  create_pagination: function() {
    this.paginators.each(function(paginator){
      paginator.empty();
      this.create_pagination_node( '&#171;', function(evt){
        var evt = new Event( evt );
        this.to_prev_page();
        evt.stop();
        return false;
      }).injectInside( paginator );
      for (var page=1; page <= this.pages; page++){
        this.create_pagination_node( page, function(evt){
          var evt = new Event( evt );
          this.to_page( evt.target.getText() );
          evt.stop();
          return false;
        }).injectInside( paginator );  
      }
      this.create_pagination_node( '&#187;', function(evt){
        var evt = new Event( evt );
        this.to_next_page();
        evt.stop();
        return false;
      }).injectInside( paginator );
    }.bind( this ));
  },

  create_pagination_node: function( text, evt ) {
    var span = new Element( 'span' ).setHTML( text );
    if (text == '&#171;'){
      var a = new Element( 'a', { 'href': '#', 'class': 'previous-page' }).addEvent( 'click', evt.bind( this ) );
    } else if (text == '&#187;'){
      var a = new Element( 'a', { 'href': '#', 'class': 'next-page' }).addEvent( 'click', evt.bind( this ) );
    } else {
      var a = new Element( 'a', { 'href': '#'}).addEvent( 'click', evt.bind( this ) );
    }
    var li   = new Element( 'li' );
    span.injectInside( a.injectInside( li ) );
    return li;
  }

});
