/*********************
  Google Charts class
**********************/

function Chart(size, type, data, dataMax, title, labels, color)
{
	// arguments
	this.title = escape(title);
	this.size = size;
	this.type = type;
	this.data = data;
	if(data.length==0 || typeof dataMax == 'undefined')
		this.dataMax = 100;
	else
		this.dataMax = dataMax;
	this.labels = labels; // array
	this.color = color;
	
	// other members
	this.url = "http://chart.apis.google.com/chart?";
	this.gurl = "";
	
	// Adds a parameter to the end of current url string
	this._addParam = function(param) {
		if(this.gurl.length == 0)
		{
			this.gurl = this.url;
		}
		else
		{
			this.gurl = this.gurl + '&';
		}
		
		this.gurl += param;
	}
	
	// returns xy line chart dataset ("x1,x2|y1,y2")
	this._xyData = function()
	{
		// if there is no data, return default max value 100
		if(this.data.lenght==0)
			return array(100,100);
		// make sure we have an even number of data items (to make pairs)
		if(this.data.length%2 != 0)
			this.data.splice(this.data.length-1, 1);
		var xs = Array();
		var ys = Array();
		for(var i=0; i<this.data.length; i+=2)
		{
			xs.push(this.data[i]);
			ys.push(this.data[i+1]);
		}
		//return xs.join().toString() + '|' + ys.join().toString();
		return Array(xs, ys);
	}
	
	// internal update function
	this._update = function()
	{
		this.gurl = "";
		// title
		this._addParam('chtt='+this.title);			
		// size
		this._addParam('chs='+this.size); // default: 250x100
		// type
		this._addParam('cht=' + this.type);

		//data
		if(this.type == 'lxy')
		{
			// value pairs (xy) line chart needs special attention
			var xy = this._xyData();
			this._addParam('chd=t:' + xy[0].join().toString() + '|' + xy[1].join().toString());
			// data scaling
			this._addParam('chds=0,' + Array.max(xy[0]) + ',0,' + Array.max(xy[1]) );
		}
		else
		{
			this._addParam('chd=t:' + this.data.join());
			// Data scaling
			this._addParam('chds=0,' + this.dataMax);
		}
		
		
		// Labels
		// pie chart and lc x axix labels are custom, lxy chart labels are computed.
		// TODO custom x labels for (xy)line and pie graph
		switch(this.type)
		{
			// Line Chart, even x spacing
			case('lc'):
				// chxr=1,0,4&chxt=x,y&chxl=0:|Jan|July|Jan|July|Jan
				this._addParam('chxr=1,0,'+this.dataMax);
				this._addParam('chxt=x,y');
				this._addParam('chxl=0:|'+this.labels.join('|'));
			break;
			// Line Chart xy points
			case('lxy'):
				var xy = this._xyData()
				this._addParam('chxt=x,y');

				this._addParam('chxr=0,0,'+Array.max(xy[0])+'|1,0,'+Array.max(xy[1]));
				//this._addParam('chds='+Array.max(xy[0])+','+Array.max(xy[1]));
			break;
			// Pie chart
			case('p3'):
				this._addParam( 'chl='+this.labels.join('|') );
			break;
		}
		
		// Color
		this._addParam('chco='+this.color);

	}
	
	// Public Methods
	// sets the same param values as constructor
	this.updateParams = function(size, type, data, dataMax, title, labels, color)
	{
		this.size = size;
		this.type = type;
		this.data = data;
		if(typeof dataMax == 'undefined')
			this.dataMax = 100;
		else
			this.dataMax = dataMax;
		this.title = escape(title);
		this.labels = labels;
		this.color = color;
	}
	
	this.getUrl = function()
	{
		this._update();
		console.log(this.gurl);
		return this.gurl;
	}
}

/*********************
  UTILITY FUNCTIONS
**********************/
	
	// Fast Max function
	Array.max = function( array ){
	    return Math.max.apply( Math, array );
	};
	
	// Debug var dump function (PHP-like)
	function var_dump(o)
	{
		for(var i in o)
		{
			console.log(i + '|' + o[i]);
		}
	}
	
	// String trim function (delete surrounding whitespace)
	String.prototype.trim = function() { return this.replace(/^\s+|\s+$/, ''); };


	/*********************
	  CHART USER INTERFACE FUNCTIONS
	**********************/

	// Chart object (requires Chart.js)
	var chartObj = null;		
	var data = [];
	var labels = [];
	var oldType;
	var oldGurl;
	var NONNUMBER = -1;
	var VALUE_PAIRS = false;
	
	// Adds data from current values in form fields x and y
	// Supports only positive numbers.
	function addData()
	{
		
		var xEl = $('#g_data_x');
		var x = xEl.val();
		x = x.trim();
		
		if(	!VALUE_PAIRS && x.length==0 )
		{
			// no data entered.
			return;
		}
		
		var yEl = $('#g_data_y');
		if( !VALUE_PAIRS )
			var y = NONNUMBER.toString();
		else
			var y = yEl.val();
		
		y = y.trim();
		
		if(VALUE_PAIRS && (x.length==0 || y.length == 0) )
		{
			alert("Please enter both x and y data value.");
			return;
		}
		
		x = parseFloat(x);
		y = parseFloat(y);
		
		// if x<0, or y should be >=0 but isn't,
		if( x<0 || (VALUE_PAIRS && y<0) )
		{
			alert("Please enter only positive numbers.");
			return;
		}
		if( isNaN(x) ||isNaN(y) )
		{
			alert("Please enter data as numbers. E.g. 55 or 20.8");
			return;
		}
		
		data.push(x);
		if(y>=0)
			data.push(y);
			
		// reset form fields
		var x = $('#g_data_x').val('');
		var y = $('#g_data_y').val('');
		
		// Labels
		var oldLabels = labels.slice(0);
		labels = new Array(data.length);
		// init to empty string
		for(var i=0; i<labels.length; i++)
		{
			if(typeof oldLabels[i] != 'undefined')
				labels[i]=oldLabels[i];
			else
				labels[i]="";
		}
		
		return data.length;
	}
	
	function deleteData(dataIdx)
	{
		var deleteNum = 1;
		if(VALUE_PAIRS)
		{
			deleteNum = 2;
			// delete in front of, or behind this index?
			if(dataIdx %2 != 0)
				dataIdx -= 1;
		}
		data.splice(dataIdx, deleteNum);
		// delete label
		labels.splice(dataIdx, deleteNum);
		update();
		return data.length;
	}
	
	// puts a text input field on page so user can edit label
	function startEditLabel(){
		var el = $(this);
		//var parent = el.parent();
		var id = parseInt(el.attr('id').split('_')[1]);
		var parent = $('#row_'+id);
		var val = labels[id];
		el.remove();			
		parent.append('<th><input id="edit_label_'+id+'" type="text" value="'+val+'" /></th>');
		
		$('#edit_label_'+id).focus();
		$('#edit_label_'+id).select();
		$('#edit_label_'+id).bind('blur', editLabel);
		$('#edit_label_'+id).bind('keydown', editLabel);
	}
	
	// edits a label
	function editLabel(e)
	{	
		var isIe = window.event ? true : false;
		e = e || window.event;

		// did user press enter?
		if(e.type=='keydown'||e.type=='onkeydown')
		{
			var code = isIe ? e.keyCode : e.which;
		 	if ( code != 13 )
				return;
		}
		var target = e.target;//  || window.event.target;
		var id = target.id.split('_')[2];
		var el = $('#'+target.id);
		var value = el.val();
		//var parent = el.parent();
		var parent = $('#row_'+id);
		
		labels[id] = value;
		el.parent().remove();// INPUT element is wrapped in a TH
		parent.append( '<th title="Click to edit label" class="label" id="label_'+id+'">'+value+'</th>' );
		$('#g_data_table th.label').bind('click', startEditLabel);
		
		update();
	}
	
	// updates table with data+labels
	function updateDataTable()
	{
		// build table row html as strings
		var rows;
		/*var dataStr = "<tr>";
		var labelStr = "<tr class='labels'>";
		var editStr = "<tr class='edit_labels'>";*/
		//$('#g_data_table').html("");
		
		if( !VALUE_PAIRS )
		{
			rows = '<tr><th>Data</th><th>Label</th></tr>';
			for(var i=0; i<data.length; i++)
			{
				var d = data[i] == NONNUMBER ? "" : data[i].toString();
				var l = typeof labels[i]=='undefined' ? "" : labels[i];
				
				rows += '<tr id="row_'+i+'"><td onclick="deleteData('+i+')" title="Click to delete">'+d+'</td><th title="Click to edit label" class="label" id="labels_'+i+'">'+l+'</th></tr>';
				
				//editStr += "<th><input type='text' id='edit_label_"+i+"' />"+"</th>";*/
			}
		}
		else
		{
			rows = '<caption>Data Pairs</caption>';
			rows = '<tr><th>x</th>,<th>y</th></tr>';
			if( data.length%2 != 0 ) return; // make sure we have an even number of data elements
			for(var i=0; i<data.length; i+=2) // loop over data pairs
			{
				var dx = data[i] == NONNUMBER ? "" : data[i].toString();
				var dy = data[i] == NONNUMBER ? "" : data[i+1].toString();
			
				rows += '<tr class="pairs" id="row_'+i+'"><td onclick="deleteData('+i+')" title="Click to delete">'+dx+'</td>,<td onclick="deleteData('+i+')" title="Click to delete">'+dy+'</td></tr>';
			}
		}
		// update data table with html strings
		$('#g_data_table').html(rows);
		
		/* edit-labels event handler */
		$('#g_data_table th.label').bind('click', startEditLabel);
		
		// IE hover support
		$('#g_data_table th.label').bind('mouseover', function(){
			$(this).addClass('hover');
		});
		$('#g_data_table td').bind('mouseover', function(){
			$(this).addClass('hover');
		});
		$('#g_data_table th.label').bind('mouseout', function(){
			$(this).removeClass('hover');
		});
		$('#g_data_table td').bind('mouseout', function(){
			$(this).removeClass('hover');
		});
		
	}
	
	// Event handler for pressing the 'add data pair-button'
	function handleAddData(e){
		var isIe = window.event ? true : false;
		e = e || window.event;
		// did user press enter?
		if(e.type=='keydown'||e.type=='onkeydown')
		{
			var code = isIe ? e.keyCode : e.which;
		 	if ( code != 13 )
				return;
		}
		var oldLen = data.length;
		var newLen = addData();
		if (newLen == oldLen)
			return; // only update on actual change
		update();
	}
	
	// callback function for color picker
	function callback(color) {
		$('#g_color').attr('value', color.replace('#',''));
		update();
	}
	
	function updateDataInputFields(type)
	{
		// DATA Input Fields
		// Use one data input field for pie chart, two (x,y pair) for line chart.
		switch(type)
		{
			case 'lc':
				$('#g_data_y').addClass('inactive');
				$('label[@for*=g_data]').addClass('inactive');
				oldType = type;
				VALUE_PAIRS = false;
			break;			
			case 'lxy':
				// even number of data entries?
				if(data.length%2 != 0)
				{
					var ok = confirm("Even number of data values required. Delete last entered value?");
					if(ok)
					{
						// trim data array to even number of values (pairs only)
						deleteData(data.length-1);
					}
					else
					{
						// go back to previous state
						$('#'+oldType).attr('checked', 'checked');
						return;
					}
				}
				// update data input fields
				$('#g_data_y').removeClass('inactive');
				$('label[@for*=g_data]').removeClass('inactive');
				oldType = type;
				VALUE_PAIRS = true;
				
			break;
			case 'p3':
			default:
				$('#g_data_y').addClass('inactive');
				$('label[@for*=g_data]').addClass('inactive');
				oldType = type;
				VALUE_PAIRS = false;
			break;
		}
		
	}
	
	// updates the chart object, then the chart image, then the code area
	function update(e)
	{
		var type = $('#g_type input[@type=radio][@checked]').attr('id');
		
		// one field for lc and p3, two fields for lxy
		updateDataInputFields(type);
		
		// update data listing
		updateDataTable();
		
		// only continue update if user entered some data
		if (data.length == 0) return;
		
		//console.log('update brought to you by '+e);
		/* TODO
			- x value labels if no labels given
			- proper error handling on value get
			- multiple data sets
			- more types of charts
		*/
		// default max value=100
		var max = data.length>0 ? Array.max(data.join().split(',')) : 100;
		var size = $('#g_size_select option[@selected]').attr('value');
		var title = $('#g_title').attr('value') || "My Chart";
		var color = $('#g_color').val();
		//console.log(color.length); // todo default color if none chosen
		
		// Instantiate or Update?
		if(!chartObj)
		{
			chartObj = new Chart(size, type, data, max, title, labels, color);
		}
		else
		{
			chartObj.updateParams(size, type, data, max, title, labels, color);
		}

		// change image size attributes
		$('#graph_preview img').attr('width', size.split('x')[0]);
		$('#graph_preview img').attr('height', size.split('x')[1]);
				
		gurl = chartObj.getUrl();
		// Update chart image
		var holder = $('#dt_chart');
		
		console.log(oldGurl);
		console.log(gurl);
		if(oldGurl != gurl)
		{
			holder.attr('style', 'width:'+size.split('x')[0]+'px');
			holder.html('<img src="../images/1px_transparent.gif" alt="Google chart generated at Dollartimes.com" /><br /><a href="http://www.dollartimes.com/on-your-site/chart.htm" title="Google chart generator at Dollartimes.com" style="font-family:arial;font-size:10px;float: right; text-decoration: none; color: #bbb">Get your own chart at dollartimes.com</a>');
			
			$('#dt_chart').prepend('<span class="notification">Loading...</span>')

			// bind load event before update! IE fires load event immediately.
			$('#graph_preview img').load(function() {
			// ON IMAGE LOADED
				// remove notification
				$('#dt_chart .notification').remove();
			// Now change Image URL:
			}).attr('src', gurl);
			
			// Update user code
			//var html = holder.html();
			//var html = document.getElementById('dt_chart').innerHTML;
			var html = '<img alt="Google chart generated at Dollartimes.com" src="'+gurl+'" /><br /><a title="Google chart generator at Dollartimes.com" style="font-size: 10px; float: right; color: #bbb; font-family: arial; text-decoration: none" href="http://www.dollartimes.com/on-your-site/chart.htm">Get your own chart at dollartimes.com</a>'
			
			$('#g_code').attr('value', '<div id="#dt_chart" style="'+holder.attr('style')+'">'+html+'</div>');
		}
		oldGurl = gurl;
		
	}
	function _generateData()
	{
		var xMax = parseFloat($('#g_data_x').val().trim());
		var yMax = parseFloat($('#g_data_y').val().trim());
		var x = Array();
		var y = Array();
		for(var i=0; i<3; i++)
		{
			x.push(Math.round(Math.random() * xMax));
			y.push(Math.round(Math.random() * yMax));
		}
		x.sort();
		y.sort();
		for(var i=0; i<x.length; i++)
		{
			data.push(x[i]);
			data.push(y[i]);
		}
		
		// Labels
		var oldLabels = labels.slice(0);
		labels = new Array(data.length);
		// init to empty string
		for(var i=0; i<labels.length; i++)
		{
			labels[i]="";
			if(typeof oldLabels[i] != 'undefined')
				labels[i]=oldLabels[i];
		}

		update();
	}
	
	// Init
	$(function(){
		// store current graph type selected
		$('input#lc').attr('checked', true);
		oldType = $('#g_type input[@type=radio][@checked]').attr('id');
		
		// Update Event Handlers
		$('#g_form input[type=text]').bind('blur', update);
		$('#g_form input[type=radio]').bind('change', update);
		$('#g_form select').bind('change', update);
		$('#g_form input.noupdate').unbind('blur', update);
		
		// Add Data Button Event Handler
		$('#g_add_data').bind('click', handleAddData);
		$('#g_data_x').bind('keydown', handleAddData)
		$('#g_data_y').bind('keydown', handleAddData)
		
		// select code on focus
		$('#g_code').bind('focus', function(){ $(this).select(); });
		
		// Color Picker
		if($('#colorpicker') && $('#pb_color'))
		{
			$('#colorpicker').farbtastic(callback);
		}
				
	});

