/*
Software License Agreement (BSD License)
Copyright (c) 2009, Conquex Ltd
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Conquex Ltd nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY Conquex Ltd ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Conquex Ltd BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
Cnx.Chart = function(options) {
	this.options=Cnx.Chart.defaultOptions;
	this.setOptions(options);
};

Cnx.Chart.defaultOptions = {
		values: [],
		titles: [],
		style: "verticalBars",
		width: 0,
		height: 0,
		spaceTop: 50,
		spaceLeft: 80,
		spaceRight: 10,
		spaceBottom: 40,
		yCaption: "",
		xCaption: "",
		mainCaption: [],
		mainCaptionFont: {fontFamily: "Verdana", fontSize: 20},
		axisVerticalMax: 0,
		axisVerticalMin: 0,
		axisVerticalValuesDisplay: [],
		valueFormat: null,
		colors: [
			{start: "#ff0000", end: "#6f0000", light: "#ffaaaa"}, 
			{start: "#00ff00", end: "#006f00", light: "#aaffaa"}, 
			{start: "#0000ff", end: "#00006f", light: "#aaaaff"}, 
			{start: "#ffff00", end: "#6f6f00", light: "#ffffaa"}, 
			{start: "#ff00ff", end: "#6f006f", light: "#ffaaff"}, 
			{start: "#00ffff", end: "#006f6f", light: "#aaffff"}, 
			{start: "#6f6f6f", end: "#ffffff", light: "#aaaaaa"}
		],
		legendFlag: false,
		legendHeight: 0,
		controlLineFlag: false,
		controlLineValues: [],
		controlLineColor: "#ffffff",
		controlLineIncludeInLegend: true,
		controlLineLegendTitle: "Control line"
};

Cnx.Chart.prototype.setOptions = function(options) {
	// override default options
	for(var p in options) this.options[p]=options[p];
	this.resetRendering();
};

Cnx.Chart.prototype.resetRendering = function() {
	this.currentY=this.options.height-this.options.legendHeight+4;
};

Cnx.Chart.prototype.renderAxis = function() {
	var scalingFactor=(this.options.height-this.options.spaceTop-((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize)-this.options.spaceBottom-this.options.legendHeight)/(this.options.axisVerticalMax-this.options.axisVerticalMin);

	this.renderer.drawImage("images/chart_background.jpg", 0, 0, this.options.width, this.options.height, 0.3);
	this.renderer.drawLine(this.options.spaceLeft, this.options.spaceTop+((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize), this.options.spaceLeft, this.options.height-this.options.spaceBottom-this.options.legendHeight, 1, "#000000");
	this.renderer.drawLine(this.options.spaceLeft, this.options.spaceTop+((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize), this.options.spaceLeft-3, this.options.spaceTop+8+((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize), 1, "#000000");
	this.renderer.drawLine(this.options.spaceLeft, this.options.spaceTop+((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize), this.options.spaceLeft+3, this.options.spaceTop+8+((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize), 1, "#000000");

	this.renderer.drawLine(this.options.spaceLeft, this.options.height-this.options.spaceBottom-this.options.legendHeight, this.options.width-this.options.spaceRight, this.options.height-this.options.spaceBottom-this.options.legendHeight, 1, "#000000");
	this.renderer.drawLine(this.options.width-this.options.spaceRight, this.options.height-this.options.spaceBottom-this.options.legendHeight, this.options.width-this.options.spaceRight-8, this.options.height-this.options.spaceBottom-3-this.options.legendHeight, 1, "#000000");
	this.renderer.drawLine(this.options.width-this.options.spaceRight, this.options.height-this.options.spaceBottom-this.options.legendHeight, this.options.width-this.options.spaceRight-8, this.options.height-this.options.spaceBottom+3-this.options.legendHeight, 1, "#000000");

	this.renderer.drawText(""+this.options.yCaption, 0, 0, "Verdana", 11, "#000000", true, true, "middle", 15, Math.floor((this.options.height-this.options.legendHeight)/2), -90);
	this.renderer.drawText(""+this.options.xCaption, 0, 0, "Verdana", 11, "#000000", true, true, "middle", Math.floor(this.options.width/2), (this.options.height-this.options.legendHeight-10), 0);

	for(var i=0;i<this.options.mainCaption.length;i++) {
		this.renderer.drawText(""+this.options.mainCaption[i], Math.floor(this.options.width/2), 25+i*this.options.mainCaptionFont.fontSize, this.options.mainCaptionFont.fontFamily, this.options.mainCaptionFont.fontSize, "#444444", true, false, "middle", 0, 0, 0);
	}

	for(var i=0;i<this.options.axisVerticalValuesDisplay.length;i++) {
		var y=(this.options.height-Math.floor((this.options.axisVerticalValuesDisplay[i]-this.options.axisVerticalMin)*scalingFactor)-this.options.spaceBottom-this.options.legendHeight);
		this.renderer.drawDashedLine(this.options.spaceLeft, y, this.options.width-this.options.spaceRight, y, 0.4, "#000000");
		
		var displayValue=this.options.axisVerticalValuesDisplay[i];
		if(this.options.valueFormat!=null) displayValue=this.options.valueFormat(displayValue);
		this.renderer.drawText(""+displayValue, this.options.spaceLeft-5, y+3, "Verdana", 9, "#000000", false, false, "end", 0, 0, 0);
	}
};

Cnx.Chart.prototype.renderVerticalRegions = function(count) {
	var scalingFactor=(this.options.height-this.options.spaceTop-((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize)-this.options.spaceBottom-this.options.legendHeight)/(this.options.axisVerticalMax-this.options.axisVerticalMin);
	var boxWidth=Math.floor((this.options.width-this.options.spaceLeft-this.options.spaceRight-20)/count);

	for(var i=0;i<count;i++) {
		var startColor=this.options.colors[i%this.options.colors.length].light;

		this.renderer.drawRectangleWithGradientAndTransform(
			(i*boxWidth+this.options.spaceLeft), this.options.spaceTop+((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize), boxWidth, this.options.height-this.options.spaceBottom-this.options.spaceTop-this.options.legendHeight-((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize), 1, 
			startColor, 1, 
			startColor, 1, 
			0,
			0,
			0,
			0,
			0
		);
	}
	this.legendXLightColors=true;
};

Cnx.Chart.prototype.renderXTitles = function(data) {
	var boxWidth=Math.floor((this.options.width-this.options.spaceLeft-this.options.spaceRight-20)/data.length);
	var barSize=boxWidth-10;

	for(var i=0;i<data.length;i++) {
		var x=Math.round(i*boxWidth+2+barSize/2+this.options.spaceLeft);
		
		var displayText=data[i];
		if(this.options.legendFlag) displayText=(i+1);
		this.renderer.drawText(""+displayText, x, this.options.height-this.options.legendHeight-this.options.spaceBottom+12, "Verdana", 9, "#000000", false, false, "middle", 0, 0, 0);
	}

	if(this.options.legendFlag) {
		for(var i=0;i<data.length;i++) {
			var startColor=this.options.colors[i%this.options.colors.length].start;
			if(this.legendXLightColors) startColor=this.options.colors[i%this.options.colors.length].light;
			this.renderer.drawText(""+(i+1)+".", 24, this.currentY, "Verdana", 9, "#000000", false, false, "end", 0, 0, 0);

			this.renderer.drawRectangleWithGradientAndTransform(
				25, this.currentY-7, 10, 10, 1, 
				startColor, 1, 
				startColor, 1, 
				0,
				0,
				0,
				0,
				0
			);

			this.renderer.drawText(""+data[i], 38, this.currentY, "Verdana", 9, "#000000", false, false, "start", 0, 0, 0);

			this.currentY+=12;				
		}
	}
};

Cnx.Chart.prototype.renderLine = function(data, color, hasLegendFlag, legendTitle) {
	if(hasLegendFlag && this.options.legendFlag) {
			this.renderer.drawText(legendTitle, 38, this.currentY, "Verdana", 9, "#000000", false, false, "start", 0, 0, 0);
			this.renderer.drawLine(25, this.currentY-3, 35, this.currentY-3, 2, color);
			this.renderer.drawLine(25, this.currentY-4, 35, this.currentY-4, 1, "#000000");
			this.renderer.drawLine(25, this.currentY-2, 35, this.currentY-2, 1, "#000000");
			this.currentY+=12;				
	}

	var boxWidth=Math.floor((this.options.width-this.options.spaceLeft-this.options.spaceRight-20)/data.length);
	var scalingFactor=(this.options.height-this.options.spaceTop-((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize)-this.options.spaceBottom-this.options.legendHeight)/(this.options.axisVerticalMax-this.options.axisVerticalMin);
	var barSize=boxWidth-10;

	var drawLineFlag=true;
	var prevX=-1;
	var prevY=-1;
	for(var i=0;i<data.length;i++) {
		if(data[i]==null) continue;
		
		var y=this.options.height-this.options.spaceBottom-Math.floor((data[i]-this.options.axisVerticalMin)*scalingFactor)-this.options.legendHeight;
		var x=Math.round(i*boxWidth+boxWidth/2+this.options.spaceLeft);

		if( (i>0) && drawLineFlag && (prevX!=-1) && (prevY!=-1) ) {
			this.renderer.drawLine(prevX, prevY-1, x, y-1, 2, "#444444");
			this.renderer.drawLine(prevX-1, prevY, x-1, y, 2, "#444444");
			this.renderer.drawLine(prevX, prevY+1, x, y+1, 2, "#444444");
			this.renderer.drawLine(prevX+1, prevY, x+1, y, 2, "#444444");
		}

		this.renderer.drawRectangleWithGradientAndTransform(
			x-5, y-5, 
			10, 10, 1, 
			"#444444", 1, 
			"#444444", 1, 
			0,
			0,
			0,
			0,
			0
		);

		if( (i>0) && drawLineFlag && (prevX!=-1) && (prevY!=-1) ) {
			this.renderer.drawLine(prevX, prevY, x, y, 2, color);

			this.renderer.drawRectangleWithGradientAndTransform(
				prevX-4, prevY-4, 
				8, 8, 1, 
				color, 1, 
				color, 1, 
				0,
				0,
				0,
				0,
				0
			);
		}

		this.renderer.drawRectangleWithGradientAndTransform(
			x-4, y-4, 
			8, 8, 1, 
			color, 1, 
			color, 1, 
			0,
			0,
			0,
			0,
			0
		);

		if( (i==0) || ( (i>0) && (data[i]!=data[i-1]) ) ) {
			var displayValue=data[i];
			if(this.options.valueFormat!=null) displayValue=this.options.valueFormat(displayValue);
			if(this.renderer.rendererName=="Cnx.PrintRenderer") {
				if(data[i]!=null)
					this.renderer.drawText(""+displayValue, x-10, y-10, "Verdana", 9, "#444444", true, false, "middle", 0, 0, 0);
			} else {
				if(data[i]!=null) {
					this.renderer.drawText(""+displayValue, x-10-1, y-10-1, "Verdana", 9, "#ffffff", true, false, "middle", 0, 0, 0);
					this.renderer.drawText(""+displayValue, x-10+1, y-10-1, "Verdana", 9, "#ffffff", true, false, "middle", 0, 0, 0);
					this.renderer.drawText(""+displayValue, x-10-1, y-10+1, "Verdana", 9, "#ffffff", true, false, "middle", 0, 0, 0);
					this.renderer.drawText(""+displayValue, x-10+1, y-10+1, "Verdana", 9, "#ffffff", true, false, "middle", 0, 0, 0);
					this.renderer.drawText(""+displayValue, x-10, y-10, "Verdana", 9, "#444444", true, false, "middle", 0, 0, 0);
				}
			}
		}
		prevX=x;
		prevY=y;
		drawLineFlag=true;
	}
};

Cnx.Chart.prototype.renderVerticalBars = function(data) {
	var boxSpaceTotal=Math.floor((this.options.width-this.options.spaceLeft-this.options.spaceRight)/data.length);
	var boxWidth=Math.floor((this.options.width-this.options.spaceLeft-this.options.spaceRight-20)/data.length);
	var scalingFactor=(this.options.height-this.options.spaceTop-((this.options.mainCaption.length-1)*this.options.mainCaptionFont.fontSize)-this.options.spaceBottom-this.options.legendHeight)/(this.options.axisVerticalMax-this.options.axisVerticalMin);

	for(var i=0;i<data.length;i++) {
		//if(data[i]==null) continue;
		var pi=3.14159265;

		var barSize=Math.floor(2*boxWidth/3);
		var barDepth=barSize/6;
		var startColor=this.options.colors[i%this.options.colors.length].start;
		var endColor=this.options.colors[i%this.options.colors.length].end;

		var val=data[i];
		if(val==null) val=this.options.axisVerticalMin;

		this.renderer.drawRectangleWithGradientAndTransform(
			0, 0, Math.floor((val-this.options.axisVerticalMin)*scalingFactor), barSize, 1, 
			startColor, 0.9, 
			endColor, 0.9, 
			(i*boxWidth+barSize+5+this.options.spaceLeft),
			(this.options.height-this.options.spaceBottom-Math.floor((val-this.options.axisVerticalMin)*scalingFactor)-this.options.legendHeight),
			90,
			0,
			0
		);

		this.renderer.drawRectangleWithGradientAndTransform(
			i*boxWidth+5+this.options.spaceLeft, this.options.height-this.options.spaceBottom-Math.floor((val-this.options.axisVerticalMin)*scalingFactor)-this.options.legendHeight, 
			Math.floor((barSize)/3), Math.floor((val-this.options.axisVerticalMin)*scalingFactor), 0, 
			"#000000", 0.35, 
			"#000000", 0, 
			0,
			0,
			0,
			0,
			0
		);


		this.renderer.drawRectangleWithGradientAndTransform(
			0, 0, 
			barSize, barDepth, 1, 
			startColor, 1, 
			startColor, 1, 
			((i)*boxWidth+6+(barDepth)/Math.tan(30*pi/180.0)+this.options.spaceLeft),
			(this.options.height-this.options.spaceBottom-Math.floor((val-this.options.axisVerticalMin)*scalingFactor)-barDepth-this.options.legendHeight),
			0,
			-60,
			0
		);

		this.renderer.drawRectangleWithGradientAndTransform(
			0, 0, 
			Math.floor((barSize)/3), barDepth, 0, 
			"#000000", 0.35, 
			"#000000", 0, 
			(i*boxWidth+6+(barDepth)/Math.tan(30*pi/180.0)+this.options.spaceLeft),
			(this.options.height-this.options.spaceBottom-Math.floor((val-this.options.axisVerticalMin)*scalingFactor)-barDepth-this.options.legendHeight),
			0,
			-60,
			0
		);
	
		this.renderer.drawRectangleWithGradientAndTransform(
			0, 0, 
			Math.floor((barDepth)/2), barSize, 0, 
			"#000000", 0.35, 
			"#000000", 0, 
			(i*boxWidth+6+(barDepth)/Math.tan(30*pi/180.0)+barSize+this.options.spaceLeft),
			(this.options.height-this.options.spaceBottom-Math.floor((val-this.options.axisVerticalMin)*scalingFactor)-barDepth-this.options.legendHeight),
			90,
			0,
			60,
			-180
		);

		this.renderer.drawRectangleWithGradientAndTransform(
			0, 0, 
			Math.floor((val-this.options.axisVerticalMin)*scalingFactor), (barDepth)/Math.tan(30*pi/180.0)+1, 1, 
			startColor, 0.9, 
			endColor, 0.9, 
			(i*boxWidth+6+barSize+(barDepth)/Math.tan(30*pi/180.0)+this.options.spaceLeft),
			(this.options.height-this.options.spaceBottom-Math.floor((val-this.options.axisVerticalMin)*scalingFactor)-barDepth-this.options.legendHeight),
			90,
			30,
			0
		);

		this.renderer.drawRectangleWithGradientAndTransform(
			0, 0,
			Math.floor((barDepth)/Math.tan(30*pi/180.0)/2)+1, Math.floor((val-this.options.axisVerticalMin)*scalingFactor), 0,
			"#000000", 0.35,
			"#000000", 0,
			(i*boxWidth+6+barSize+(barDepth)/Math.tan(30*pi/180.0)+this.options.spaceLeft),
			(this.options.height-this.options.spaceBottom-barDepth-this.options.legendHeight),
			180,
			0,
			-30,
			0
		);

		var displayValue=data[i];
		if(this.options.valueFormat!=null) displayValue=this.options.valueFormat(displayValue);
		if(this.renderer.rendererName=="Cnx.PrintRenderer") {
			if(data[i]!=null)
				this.renderer.drawText(""+displayValue, Math.floor(i*boxWidth+barSize/2+5+this.options.spaceLeft), (this.options.height-this.options.spaceBottom-Math.floor((data[i]-this.options.axisVerticalMin)*scalingFactor/2)-this.options.legendHeight), "Verdana", 9, "#444444", true, false, "middle", 0, 0, 0);
		} else {
			if(data[i]!=null) {
				this.renderer.drawText(""+displayValue, Math.floor(i*boxWidth+barSize/2+5+this.options.spaceLeft)-1, (this.options.height-this.options.spaceBottom-Math.floor((data[i]-this.options.axisVerticalMin)*scalingFactor/2)-this.options.legendHeight)-1, "Verdana", 9, "#444444", true, false, "middle", 0, 0, 0);
				this.renderer.drawText(""+displayValue, Math.floor(i*boxWidth+barSize/2+5+this.options.spaceLeft)+1, (this.options.height-this.options.spaceBottom-Math.floor((data[i]-this.options.axisVerticalMin)*scalingFactor/2)-this.options.legendHeight)-1, "Verdana", 9, "#444444", true, false, "middle", 0, 0, 0);
				this.renderer.drawText(""+displayValue, Math.floor(i*boxWidth+barSize/2+5+this.options.spaceLeft)-1, (this.options.height-this.options.spaceBottom-Math.floor((data[i]-this.options.axisVerticalMin)*scalingFactor/2)-this.options.legendHeight)+1, "Verdana", 9, "#444444", true, false, "middle", 0, 0, 0);
				this.renderer.drawText(""+displayValue, Math.floor(i*boxWidth+barSize/2+5+this.options.spaceLeft)+1, (this.options.height-this.options.spaceBottom-Math.floor((data[i]-this.options.axisVerticalMin)*scalingFactor/2)-this.options.legendHeight)+1, "Verdana", 9, "#444444", true, false, "middle", 0, 0, 0);
				this.renderer.drawText(""+displayValue, Math.floor(i*boxWidth+barSize/2+5+this.options.spaceLeft), (this.options.height-this.options.spaceBottom-Math.floor((data[i]-this.options.axisVerticalMin)*scalingFactor/2)-this.options.legendHeight), "Verdana", 9, "#ffffff", true, false, "middle", 0, 0, 0);
			}
		}
	}
};

Cnx.Chart.prototype.setRenderer = function(pRenderer) {
	this.renderer=pRenderer;
};

Cnx.Chart.prototype.setValues = function(pValues) {
	this.options.values=pValues;
};

Cnx.Chart.prototype.render = function(data) {
	this.renderAxis();
	this.renderXTitles(this.options.titles);
	this.renderVerticalBars(this.options.values);
	if(this.options.controlLineFlag) this.renderLine(this.options.controlLineValues, this.options.controlLineColor, this.options.controlLineIncludeInLegend, this.options.controlLineLegendTitle);
	this.renderer.finish();
};