function RowDef(values,formatfunc,rowheader) {
	this.values = values;
	this.rowheader = rowheader;
	this.rowheaderspan = 1;
	
	this.formatedvalues = Array(values.length);
	for(i = 0; i < values.length;i++) {
		this.formatedvalues[i] = formatfunc(values[i]);
	}
}

function ColDef(values,formatfunc) {
	this.values = values;
	this.colspan = 1;
	
	this.getValue = function(index) {
		return this.values[Math.floor(index/this.colspan) % this.values.length];
	}
	
	this.getFormatedValue = function(index) {
		return formatfunc(this.getValue(index*this.colspan));
	}		
}

function TableDef(rowdef,coldefs,cellfunc,caption) { 
	this.cellfunc = cellfunc;
	this.caption = caption;
	
	this.rowdef = rowdef;	
	rowdef.rowheaderspan = coldefs.length;
	
	this.coldefs = coldefs;
	colspan = 1;
	for(i = this.coldefs.length -1;i >= 0;i--) {
		this.coldefs[i].colspan = colspan;
		colspan = colspan*this.coldefs[i].values.length;
	}	
	
	// Total number of columns is the number of first level columns times
	// their colspan, plus 1 for the row labels.
	this.totalcols =  this.coldefs[0].colspan*this.coldefs[0].values.length + 1;
	
	this.getCellNode = function(rowindex,colindex) {	
		// Build an array to hold all the args.       
	  var newargs = Array(this.coldefs.length+1);
	  
	  // Add the row value for the args
    newargs[0] = rowdef.values[rowindex];	
    
    for(var i = 0; i < this.coldefs.length;i++) {
    	newargs[i+1] = this.coldefs[i].getValue(colindex);
    }
    return this.cellfunc.apply(this.cellfunc,newargs); 
  }
}

function SmartTable(tabledef) {
	this.tabledef = tabledef;

	this.getNode = function() {
		// Build div
  	var div = document.createElement("div");
  	var attr = document.createAttribute("class");
  	attr.value = "smartTable";
  	div.setAttributeNode(attr);

		// Initiallize the table
  	var table = document.createElement("table");
  	var attr = document.createAttribute("cellspacing");
  	attr.value = "0";
  	table.setAttributeNode(attr);
  	div.appendChild(table);
  
  	// Add the caption
  	var caption = document.createElement("caption");
  	table.appendChild(caption);
  
  	// Add text to caption
  	var text = document.createTextNode(tabledef.caption);
  	caption.appendChild(text);
  	
  	// Add the table header
  	table.appendChild(this.buildHeader());
  	
  	// Add table body to the table.
	  var tbody = document.createElement("tbody");
  	table.appendChild(tbody);
  	
  	// Add the data body to the table.
  	for(var i=0; i < tabledef.rowdef.values.length; i++){
  		tbody.appendChild(this.buildDataRow(i));
  	}
  	
  	// Build details node
	  var detailsNode = document.createElement("div");
 	 	attr = document.createAttribute("class");
 	 	attr.value = "details";
 	 	detailsNode.setAttributeNode(attr);
 	 	div.appendChild(detailsNode);
  
  	return div;	 
  }

	this.buildHeader = function() {  
  	// Build the table header
  	var thead = document.createElement("thead");

	
		for(var i=0;i < tabledef.coldefs.length;i++) {
			thead.appendChild(this.buildHeaderRow(i));
	  }
	  return thead;
	}
	
	this.buildHeaderRow = function (index) {

		// Add a row to the table header
		var row = document.createElement("tr");		  
		
		if(index == 0) {

			// Add a cell to the row
 			var cell = document.createElement("th");
 			row.appendChild(cell);

	 		// Add rowspan attribute to the cell
 			var attr = document.createAttribute("rowspan");
			attr.value = tabledef.rowdef.rowheaderspan.toString();
		  cell.setAttributeNode(attr);

  		// Add text to the cell
  		text = document.createTextNode(tabledef.rowdef.rowheader);
  		cell.appendChild(text);
		}
				
		for(var i=0;i < (tabledef.totalcols -1)/tabledef.coldefs[index].colspan;i++) {
			// Add another cell to the row
  		cell = document.createElement("th");
  		row.appendChild(cell);

  		// Add a colspan attribute to the cell
  		attr = document.createAttribute("colspan");
  		attr.value = tabledef.coldefs[index].colspan;
  		cell.setAttributeNode(attr);
  
  		// Add text to the cell
  		text = document.createTextNode(tabledef.coldefs[index].getFormatedValue(i));
  		cell.appendChild(text);
    }
    return row;
  }
  
  this.buildDataRow = function(index) {
  	// Add a row to the table body 
    var row = document.createElement("tr");

    // Add a row header to the row
    var cell = document.createElement("th");
    row.appendChild(cell);

    // Add text to the header cell
    text = document.createTextNode(tabledef.rowdef.formatedvalues[index]);
    cell.appendChild(text);
    
    for(var i =0;i < tabledef.totalcols -1;i++) {
    	// Add a cell to row
    	cell = tabledef.getCellNode(index,i);
    	row.appendChild(cell);
    }
    return row;
	}
}

