/***
 * Excerpted from "Domain-Driven Design Using Naked Objects",
 * published by The Pragmatic Bookshelf.
 * Copyrights apply to this code. It may not be used to create training material, 
 * courses, books, articles, and the like. Contact us if you are in doubt.
 * We make no guarantees that this code is fit for any purpose. 
 * Visit http://www.pragmaticprogrammer.com/titles/dhnako for more book information.
***/
TableSorter = function(id, dateParser)
{
  this.id = id;
  this.table = document.getElementById(id);
  this.activeSort = null;
  this.dateParser = dateParser;

  this.getSortDirection = function(sortCol)
  {
    if (this.activeSort == null || this.activeSort.column != sortCol)
      this.activeSort = new SortReference(sortCol, "asc");
    else
    {
      if (this.activeSort.direction == "asc")
        this.activeSort.direction = "dsc";
      else
        this.activeSort.direction = "asc";
    }
    return this.activeSort.direction;
  }

  this.sort = function(sortCol, datatype)
  {
    var rowDataArray = new TableRowDataArray(this.id);
    rowDataArray.setSortCol(sortCol);
    var direction = this.getSortDirection(sortCol);
    if (datatype == 'number')
      rowDataArray.sort(new NumericComparator());
    else if (datatype == 'date')
      rowDataArray.sort(new DateComparator(this.dateParser));
    else
      rowDataArray.sort();
    if (direction == "dsc")
      rowDataArray.reverse();
    rowDataArray.rebuildTable();
  }
  return this;
}

TableRowData = function(cols)
{
  this.cols = cols;
  this.sortCol = 0;
  this.setSortCol = function(col)
  {
    this.sortCol = col;
  }
  this.toString = function()
  {
    return this.cols[this.sortCol];
  }
}

TableRowDataArray = function(id)
{
  this.arr = new Array();
  this.arr.table = document.getElementById(id);
  this.arr.cssClasses = new Array();

  var tbody = this.arr.table.tBodies[0];
  var rows = tbody.rows;
  for (var row = 0; row < rows.length; row++)
  {
    this.arr.cssClasses.push(rows[row].className);
    var cells = rows[row].cells;
    var cellTextArray = new Array();
    for (var j = 0; j < cells.length; j++)
      cellTextArray.push(cells[j].innerHTML);
    this.arr.push(new TableRowData(cellTextArray));
  }

  this.arr.rebuildTable = function()
  {
    var tbody = this.table.tBodies[0];
    var mybody = tbody.cloneNode(false);
    for (var row = 0; row < this.length; row++)
    {
      var tr = document.createElement("tr");
      tr.className = this.cssClasses[row];
      for (var i = 0; i < this[row].cols.length; i++)
      {
        var td = document.createElement("td");
        td.innerHTML = this[row].cols[i];
        tr.appendChild(td);
      }
      mybody.appendChild(tr);
    }
    this.table.replaceChild(mybody, tbody);
  }

  this.arr.setSortCol = function(sortCol)
  {
    for (var i = 0; i < this.length; i++)
    {
      this[i].setSortCol(sortCol);
    }
  }
  return this.arr;
}

SortReference = function (column, direction)
{
  this.column = column;
  this.direction = direction;
}


NumericComparator = function()
{
  function compare(a, b)
  {
    a = parseInt(a);
    b = parseInt(b);
    if (a > b)
      return 1;
    if (a < b)
      return -1;
    return 0;
  }
  return compare;
}

DateComparator = function(dateParser)
{
  this.compare = function(x, y)
  {
    x = dateParser.parse(x).getTime();
    y = dateParser.parse(y).getTime();
    if (x > y)
      return 1;
    if (x < y)
      return -1;
    return 0;
  }
  return this.compare;
}

DateParser = function(re, a, b, c, d, e, f)
{
  this.re = re;
  this.a = a;
  this.b = b;
  this.c = c;
  this.d = d || -1;
  this.e = e || -1;
  this.f = f || -1;
  this.parseMonth = function(month)
  {
    month = month.toLowerCase();
    months = "jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec".split(',');
    for (var i = 0; i < months.length; i++)
    {
      if (month == months[i])
      {
        return i;
      }
    }
  }

  this.parse = function(dateStr)
  {
    value = dateStr.toString().toLowerCase();
    if (this.re.test(value))
    {
      matches = this.re.exec(value);
      year = parseInt(matches[a]);
      if (parseInt(matches[b]))
        month = parseInt(matches[b] - 1);
      else
        month = this.parseMonth(matches[b]);
      day = parseInt(matches[c]);
      hour = d > 0 ? parseInt(matches[d]) : 0;
      minute = e > 0 ? parseInt(matches[e]) : 0;
      second = f > 0 ? parseInt(matches[f]) : 0;
      date = new Date(year, month, day, hour, minute, second);
      return date;
    }
  }

  return this;
}
