/*----------------------------------------------------------------------------\
|                                  Chart 1.0                                  |
|-----------------------------------------------------------------------------|
|                          Created by Emil A Eklund                           |
|                        (http://eae.net/contact/emil)                        |
|-----------------------------------------------------------------------------|
| Client side chart painter, supports line, area and bar charts and stacking, |
| uses Canvas (mozilla,  safari,  opera) or SVG (mozilla, opera) for drawing. |
| Can be used with IECanvas to allow the canvas painter to be used in IE.     |
|-----------------------------------------------------------------------------|
|                      Copyright (c) 2006 Emil A Eklund                       |
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
| This program is  free software;  you can redistribute  it and/or  modify it |
| under the terms of the MIT License.                                         |
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
| Permission  is hereby granted,  free of charge, to  any person  obtaining a |
| copy of this software and associated documentation files (the "Software"),  |
| to deal in the  Software without restriction,  including without limitation |
| the  rights to use, copy, modify,  merge, publish, distribute,  sublicense, |
| and/or  sell copies  of the  Software, and to  permit persons to  whom  the |
| Software is  furnished  to do  so, subject  to  the  following  conditions: |
| The above copyright notice and this  permission notice shall be included in |
| all copies or substantial portions of the Software.                         |
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
| THE SOFTWARE IS PROVIDED "AS IS",  WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED,  INCLUDING BUT NOT LIMITED TO  THE WARRANTIES  OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| AUTHORS OR  COPYRIGHT  HOLDERS BE  LIABLE FOR  ANY CLAIM,  DAMAGES OR OTHER |
| LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT, TORT OR  OTHERWISE,  ARISING |
| FROM,  OUT OF OR  IN  CONNECTION  WITH  THE  SOFTWARE OR THE  USE OR  OTHER |
| DEALINGS IN THE SOFTWARE.                                                   |
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
|                         http://eae.net/license/mit                          |
|-----------------------------------------------------------------------------|
| Dependencies: canvaschartpainter.js  - Canvas chart painter implementation. |
|               canvaschart.css        - Canvas chart painter styles.         |
|           or: svgchartpainter.js     - SVG chart painter implementation.    |
|-----------------------------------------------------------------------------|
| 2006-01-03 | Work started.                                                  |
| 2006-01-05 | Added legend and axis labels. Changed the painter api slightly |
|            | to allow two-stage initialization (required for ie/canvas) and |
|            | added legend/axis related methods. Also updated bar chart type |
|            | and added a few options, mostly related to bar charts.         |
| 2006-01-07 | Updated chart size calculations to take legend and axis labels |
|            | into consideration.  Split painter implementations to separate |
|            | files.                                                         |
| 2006-01-10 | Fixed bug in automatic range calculation.  Also added explicit |
|            | cast to float for stacked series.                              |
| 2006-04-16 | Updated constructor to set painter factory  based on available |
|            | and supported implementations.                                 |
|-----------------------------------------------------------------------------|
| Created 2006-01-03 | All changes are in the log above. | Updated 2006-04-16 |
\----------------------------------------------------------------------------*/

var CHART_LINE    =  1;
var CHART_AREA    =  2;
var CHART_BAR     =  3;
var CHART_STACKED =  4;

/*----------------------------------------------------------------------------\
|                                    Chart                                    |
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
| Chart class, control class that's used to represent a chart. Uses a painter |
| class for the actual drawing.  This is the only  class that should be  used |
| directly, the other ones are internal.                                      |
\----------------------------------------------------------------------------*/

function Chart(el) {
	this._cont             = el;
	this._yMin             = null;
	this._yMax             = null;
	this._xGridDensity     = 0;
	this._yGridDensity     = 1;
	this._flags            = 0;
	this._series           = new Array();
	this._labelPrecision   = 0;
	this._horizontalLabels = new Array();
	this._barWidth         = 10;
	this._barDistance      = 2;
	this._bars             = 0;
	this._showLegend       = true;
	
	/*
	 * Determine painter implementation to use based on what's available and
	 * supported. CanvasChartPainter is the prefered one, JsGraphicsChartPainter
	 * the fallback one as it works in pretty much any browser. The
	 * SVGChartPainter implementation one will only be used if set explicitly as
	 * it's not up to pair with the other ones.
	 */
	if ((typeof CanvasChartPainterFactory != 'undefined') && (window.CanvasRenderingContext2D)) {
		this._painterFactory = CanvasChartPainterFactory;
		//alert("CanvasChartPainterFactory");
	}
	else if (typeof JsGraphicsChartPainterFactory != 'undefined') {
		this._painterFactory = JsGraphicsChartPainterFactory;
		//alert("JsGraphicsChartPainterFactory");
	}
	else { this._painterFactory = null; 
	//alert("no factory painter.");
	}
}


Chart.prototype.setPainterFactory = function(f) {
	this._painterFactory = f;
};


Chart.prototype.setVerticalRange = function(min, max) {
	this._yMin = min;
	this._yMax = max;
};


Chart.prototype.setLabelPrecision = function(precision) {
	this._labelPrecision = precision;
};


Chart.prototype.setShowLegend = function(b) {
	this._showLegend = b;
};


Chart.prototype.setGridDensity = function(horizontal, vertical) {
	this._xGridDensity = horizontal;
	this._yGridDensity = vertical;
};


Chart.prototype.setHorizontalLabels = function(labels) {
	this._horizontalLabels = labels;
};


Chart.prototype.setDefaultType = function(flags) {
	this._flags = flags;
};


Chart.prototype.setBarWidth = function(width) {
	this._barWidth = width;
};


Chart.prototype.setBarDistance = function(distance) {
	this._barDistance = distance;
};


Chart.prototype.add = function(label, color, values, flags) {
	var o, offset;

	if (!flags) { flags = this._flags; }
	if ((flags & CHART_BAR) == CHART_BAR) { offset = this._barDistance + this._bars * (this._barWidth + this._barDistance); this._bars++; }
	else { offset = 0; }
	o = new ChartSeries(label, color, values, flags, offset);

	this._series.push(o);
};


Chart.prototype.draw = function() {
	var painter, i, o, o2, len, xlen, ymin, ymax, series, type, self, bLabels;
	
	if (!this._painterFactory) { return; }

	/* Initialize */
	series = new Array();
	stackedSeries = new Array();
	xlen = 0;
	ymin = this._yMin;
	ymax = this._yMax;

	/* Separate stacked series (as they need processing). */
	for (i = 0; i < this._series.length; i++) {
		o = this._series[i]
		if ((o.flags & CHART_STACKED) == CHART_STACKED) { series.push(o); }
	}

	/* Calculate values for stacked series */
	for (i = series.length - 2; i >= 0; i--) {
		o  = series[i].values;
		o2 = series[i+1].values;
		len = (o2.length > o.length)?o2.length:o.length;
		for (j = 0; j < len; j++) {
			if ((o[j]) && (!o2[j])) { continue; }
			if ((!o[j]) && (o2[j])) { o[j] = o2[j]; }
			else { o[j] = parseInt(o[j]) + parseFloat(o2[j]); }
	}	}

	/* Append non-stacked series to list */
	for (i = 0; i < this._series.length; i++) {
		o = this._series[i]
		if ((o.flags & CHART_STACKED) != CHART_STACKED) { series.push(o); }
	}

	/* Determine maximum number of values, ymin and ymax */
	for (i = 0; i < series.length; i++) {
		o = series[i]
		if (o.values.length > xlen) { xlen = o.values.length; }
		for (j = 0; j < o.values.length; j++) {
			if ((o.values[j] < ymin) || (ymin == null))  { ymin = o.values[j]; }
			if (o.values[j] > ymax) { ymax = o.values[j]; }
	}	}


	//Begin MO
	MaxVal = 0; //Universal variable to hold Y-axis max value
	
	fnFindMax(ymax);
	ymax = MaxVal;
	//fnFindMax(1.12*(ymax-ymin)); //Adjust the max value according to scale
	/*
	var beforeYmin = ymin;
	
	var tStep = MaxVal/8;
	if (ymax <= 0) {
		var tMin = -MaxVal;	
		for (i = 0; i < 8; i++) {
				tMin+=tStep;
				if (tMin>ymin) {
						tMin -= tStep;
						break;
				}		
		}
		ymin = tMin;
		ymax = tMin+MaxVal;		
	} else {
		var tMax = MaxVal;	
		for (i = 0; i < 8; i++) {
				tMax-=tStep;
				if (tMax<ymax) {
						tMax += tStep;
						break;
				}		
		}
		ymax = tMax;
		ymin = tMax-MaxVal;
		//ymin = (tMax-MaxVal) + tStep; 
	}
	*/
	//End MO

	/*
	 * For bar only charts the number of charts is the same as the length of the
	 * longest series, for others combinations it's one less. Compensate for that
	 * for bar only charts.
	 */
	if (this._series.length == this._bars) {
		xlen++;
		this._xGridDensity++;
	}

	/*
	 * Determine whatever or not to show the legend and axis labels
	 * Requires density and labels to be set.
	 */
	bLabels = ((this._xGridDensity) && (this._yGridDensity) && (this._horizontalLabels.length >= this._xGridDensity));

	/* Create painter object */
	painter = this._painterFactory();
	painter.create(this._cont);

	/* Initialize painter object */
	painter.init(xlen, ymin, ymax, this._xGridDensity, this._yGridDensity, bLabels);

	/* Draw chart */
	painter.drawBackground();

	/*
	 * If labels and grid density where specified, draw legend and labels.
	 * It's drawn prior to the chart as the size of the legend and labels
	 * affects the size of the chart area.
	 */
	if (this._showLegend) { painter.drawLegend(series); }
	if (bLabels) {
		painter.drawVerticalAxis(this._yGridDensity, this._labelPrecision);
		painter.drawHorizontalAxis(xlen, this._horizontalLabels, this._xGridDensity, this._labelPrecision);
	}

	/* Draw chart */
	painter.drawChart();

	/* Draw series */
	for (i = 0; i < series.length; i++) {
		type = series[i].flags & ~CHART_STACKED;
		switch (type) {
			case CHART_LINE: painter.drawLine(series[i].color, series[i].values); break;
			case CHART_AREA: painter.drawArea(series[i].color, series[i].values); break;
			case CHART_BAR:  painter.drawBars(series[i].color, series[i].values, xlen-1, series[i].offset, this._barWidth); break;
			default: ;
		};
	}

	/*
	 * Draw axis (after the series since the anti aliasing of the lines may
	 * otherwise be drawn on top of the axis)
	 */
	painter.drawAxis();

};


/*----------------------------------------------------------------------------\
|                                 ChartSeries                                 |
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
| Internal class for representing a series.                                   |
\----------------------------------------------------------------------------*/

function ChartSeries(label, color, values, flags, offset) {
	this.label  = label;
	this.color  = color;
	this.values = values;
	this.flags  = flags;
	this.offset = offset;
}


/*----------------------------------------------------------------------------\
|                            AbstractChartPainter                             |
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
| Abstract base class defining the painter API. Can not be used directly.     |
\----------------------------------------------------------------------------*/

function AbstractChartPainter() {

};


AbstractChartPainter.prototype.calc = function(w, h, xlen, ymin, ymax, xgd, ygd) {
	this.range = ymax - ymin;
	this.xstep = w / (xlen - 1);
	this.xgrid = (xgd)?w / (xgd - 1):0;
	this.ygrid = (ygd)?h / (ygd - 1):0;
	this.ymin  = ymin;
	this.ymax  = ymax;
};

function fnFindMax(tMaxVal) {
	graphDecimalsFlag = false;
	label = "";
	label2= "";
	ValScaler = 1;
	AbsMaxVal = Math.abs(tMaxVal);
	
	if(tMaxVal > 8000000000) fnSetScale(AbsMaxVal, 1000000000, true, false, 1/1000000000, 1, "B", "M");
	else if(tMaxVal > 4000000000) fnSetScale(8000000000, 1000000000, false, false, 1/1000000000, 1, "B", "M");
	else if(tMaxVal > 2400000000) fnSetScale(4000000000, 1000000000, true, false, 1/1000000000, 1, "B", "M");
	else if(tMaxVal > 1600000000) fnSetScale(2400000000, 1000000000, true, false, 1/1000000000, 1, "B", "M");
	else if(tMaxVal > 1000000000) fnSetScale(1600000000, 1000000000, true, false, 1/1000000000, 1, "B", "M");
	else if(tMaxVal > 800000000) fnSetScale(1000000000, 1000000000, false, false, 1/1000000, 1, "B", "M");
	else if(tMaxVal > 400000000) fnSetScale(800000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 240000000) fnSetScale(400000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 160000000) fnSetScale(240000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 100000000) fnSetScale(160000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 80000000) fnSetScale(100000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 40000000) fnSetScale(80000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 24000000) fnSetScale(40000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 16000000) fnSetScale(24000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 10000000) fnSetScale(16000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 8000000) fnSetScale(10000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 6000000) fnSetScale(8000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 4000000) fnSetScale(6000000, 1000000, true, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 2400000) fnSetScale(4000000, 1000000, true, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 1600000) fnSetScale(2400000, 1000000, true, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 1000000) fnSetScale(1600000, 1000000, true, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 800000) fnSetScale(1000000, 1000000, false, false, 1/1000000, 1, "M", "K");
	else if(tMaxVal > 600000) fnSetScale(800000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 400000) fnSetScale(600000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 240000) fnSetScale(400000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 160000) fnSetScale(240000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 120000) fnSetScale(160000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 80000) fnSetScale(120000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 72000) fnSetScale(80000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 64000) fnSetScale(72000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 48000) fnSetScale(64000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 32000) fnSetScale(48000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 24000) fnSetScale(32000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 16000) fnSetScale(24000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 12000) fnSetScale(16000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 8000) fnSetScale(12000, 1000, false, false, 1/1000, 1, "K", ""); //Added
	else if(tMaxVal > 4000) fnSetScale(8000, 1000, false, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 2400) fnSetScale(4000, 1000, true, false, 1/1000, 1, "K", "");
	else if(tMaxVal > 1600) fnSetScale(2400, 1000, true, false, 1/1000, 1, "K", "");
	
	else if(tMaxVal > 1000) fnSetScale(1600, 1000, true, false, 1/1000, 1, "K", "");
	
	else if(tMaxVal > 800) fnSetScale(1000, 800, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 400) fnSetScale(800, 400, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 200) fnSetScale(400, 200, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 160) fnSetScale(200, 160, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 80) fnSetScale(160, 80, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 40) fnSetScale(80, 40, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 24) fnSetScale(40, 24, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 16) fnSetScale(24, 16, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 8) fnSetScale(16, 8, false, false, 1, 1, "", ""); //Added
	
	/*
	else if(tMaxVal > 4) fnSetScale(8, 4, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 2) fnSetScale(4, 2, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 1) fnSetScale(2, 1, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 1.6) fnSetScale(2, 1.6, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 0.8) fnSetScale(1.6, 0.8, false, false, 1, 1, "", ""); //Added
	else if(tMaxVal > 0.4) fnSetScale(0.8, 0.4, false, false, 1, 1, "", ""); //Added
	*/
	
	else if(tMaxVal > 0) fnSetScale(8, 8, false, false, 1, 1, "", ""); //Added
		
	//alert("END tMaxVal: " + tMaxVal);
	//return MaxVal;
}

function fnSetScale(mval, T, flag, flag2, vscaler, vscaler2, lab, lab2) //(maxl, T, lab, vscaler, flag, lab2, vscaler2, flag2) 
{
	MaxVal = mval;
	//alert("New MaxVal: " + MaxVal);
	
	graphDecimalsFlag = flag;
	ValScaler = vscaler;
	label = lab;
	
	threshold = T;
	graphDecimalsFlag2 = flag2;
	ValScaler2 = vscaler2;
	label2 = lab2;	
}


AbstractChartPainter.prototype.create = function(el) {};
AbstractChartPainter.prototype.init = function(xlen, ymin, ymax, xgd, ygd, bLabels) {};
AbstractChartPainter.prototype.drawLegend = function(series) {};
AbstractChartPainter.prototype.drawVerticalAxis = function(ygd, precision) {};
AbstractChartPainter.prototype.drawHorizontalAxis = function(xlen, labels, xgd, precision) {};
AbstractChartPainter.prototype.drawAxis = function() {};
AbstractChartPainter.prototype.drawBackground = function() {};
AbstractChartPainter.prototype.drawChart = function() {};
AbstractChartPainter.prototype.drawArea = function(color, values) {};
AbstractChartPainter.prototype.drawLine = function(color, values) {};
AbstractChartPainter.prototype.drawBars = function(color, values, xlen, xoffset, width) {};
