if (!Atira) var Atira = {};

/**
 * Constructs a new graph
 * @constructor
 */
Atira.Graph = function() {
	/** Describes the dimensions of the graph body */
	this.body  = { width: 380, height: 250, paddingTop: 10, paddingBottom: 20, paddingLeft: 40, paddingRight: 10, innerPaddingVertical: 10, innerPaddingHorizontal: 10 };
	/** Describes the visual style of the graph */
	this.style = { border:'rgb(230,230,230)', background:'rgb(240,240,240)', colors:[['#5FBCFA','#4282AD','#7FA3BB','#AADAFA','#2F5C7A'],['#9BFA60','#6BAD42','#97BB81','#BFEEA2','#446E2A'],['#EE5D5B','#A23F3E','#B27B7B','#E58A8A','#652727'],['#E5BE0B','#997F07','#AA963C','#E6C73D','#665405'],['#000','#997F07','#AA963C','#E6C73D','#665405']] };
	/** Legends : position = right | bottom , direction = auto | vertical */
	this.style.legends = { position:'right', direction:'auto', above:false, border:'#ddd' };
	this.style.pie = { radiusFactor: .9 , valueInLegend: false , left: 0, top: 0 };
	/** Describes the appearance and labels of the x-axis */
	this.xAxis = { labels:[], grid:'#fff', concentration:.8 , maxLabels:12};
	/** Describes the appearance and labels of the y-axis */
	this.yAxis = { min:0, max:0, steps:8, above:false , factor: 10, grid:'#fff'};
	/** @private */
	this.dataSets = [];
}

/**
 * Adds a dataset to the graph
 *@param {Atira.Graph.DataSet} dataSet The dataset to add
 */
Atira.Graph.prototype.addDataSet = function(dataSet) {
	this.dataSets[this.dataSets.length] = dataSet;
}

Atira.Graph.prototype.getLabelForAxisKey = function(key) {
	for (var i=0; i < this.xAxis.labels.length; i++) {
		if (this.xAxis.labels[i].key==key) {
			return this.xAxis.labels[i].label;
		}
	};
	return null;
}

/**
 * Clears all datasets from the graph
 */
Atira.Graph.prototype.clearData = function() {
	this.dataSets = [];
	this.xAxis.labels = [];
}

Atira.Graph.prototype.setTypeOfAllDataSets = function(type) {
	for (var i=0; i < this.dataSets.length; i++) {
		this.dataSets[i].style.type=type;
	};
}

Atira.Graph.prototype.hsbToRgb = function (color){
	var br = Math.round(color[2] / 100 * 255);
	if (color[1] == 0){
		return [br, br, br];
	} else {
		var hue = color[0] % 360;
		var f = hue % 60;
		var p = Math.round((color[2] * (100 - color[1])) / 10000 * 255);
		var q = Math.round((color[2] * (6000 - color[1] * f)) / 600000 * 255);
		var t = Math.round((color[2] * (6000 - color[1] * (60 - f))) / 600000 * 255);
		switch(Math.floor(hue / 60)){
			case 0: return [br, t, p];
			case 1: return [q, br, p];
			case 2: return [p, br, t];
			case 3: return [p, q, br];
			case 4: return [t, p, br];
			case 5: return [br, p, q];
		}
	}
	return false;
}


Atira.Graph.prototype.buildColors = function(length,depth,start,degrees) {
		if (!degrees) degrees = 360;
		var result = [];
		start = start || 0;
		for (var i=0; i < length; i++) {
			result[i]=[];
			var hue = i*(degrees/length)+start;
	
			for (var j=0; j < depth; j++) {
				if (j%2 == 1) {
					var saturation = (j/depth)*40+50;
					var brightness = (1-j/depth)*10+90;
				} else {
					var saturation = (1-j/depth)*20+80;
					var brightness = (1-j/depth)*80+20;					
				}
				var color = this.hsbToRgb([hue,saturation,brightness]);
				result[i][j]='rgb('+color.join(',')+')';
			};
		};
	this.style.colors = result;
}

Atira.Graph.prototype.buildDeepColors = function(length,start,degrees) {
	if (!degrees) degrees = 360;
	var result = [];
	start = start || 0;
	for (var i=0; i < length; i++) {
		var hue = i*(degrees/length)+start;
		var color = this.hsbToRgb([hue,100,100]);
		result.push('rgb('+color.join(',')+')');
	};
	Atira.log(result);
	this.style.colors = [result];
}

Atira.Graph.prototype.hasData = function() {
	var hasData=false;
	for (var i=0;i<this.dataSets.length && !hasData;i++) {
		hasData=this.dataSets[i].hasData();
	}
	return hasData;
}

/********************************************** Loader ***************************************/

/**
 * Used to load data using Ajax
 * @constructor
 */
Atira.Graph.Loader = function() {};

/**
 * Loads data from an url and puts it on a graph and renders it on an element
 * @param {Atria.Graph} The graph to load the data into (existing graph data is cleared)
 * @param {String} url The url to load the data from
 * @param {Object} delegate
 */
Atira.Graph.Loader.prototype.load = function(graph,url,delegate) {
	this.graph = graph;
	this.delegate = delegate || {};
	var request = new Atira.Request();
	request.setDelegate(this);
	if (url.indexOf('?')!=-1) {
		url+='&'+new Date().getTime();
	} else {
		url+='?'+new Date().getTime();
	}
	request.request(url);
}


/**
 * Sets a renderer to render the gaph when it is loaded
 */
Atira.Graph.Loader.prototype.setRenderer = function(renderer) {
	this.renderer = renderer;
}

/**
 * @private
 */
Atira.Graph.Loader.prototype.onSuccess = function(response) {
	this.graph.clearData();
	if (!response.responseXML || response.responseXML.firstChild==null) {
		if (this.delegate.onFailure) {
			this.delegate.onFailure(this.graph);
		}
		return;
	}
	var doc = response.responseXML.documentElement;
	var dataSets = this.getChildren(doc,'dataset');
	for (var i=0; i < dataSets.length; i++) {
		var set = this.parseDataset(dataSets[i]);
		this.graph.addDataSet(set);
	};
	var xAxes = doc.getElementsByTagName('x-axis');
	if (xAxes.length>0) {
		var labels = []
		var labelNodes = xAxes[0].getElementsByTagName('label');
		for (var i=0; i < labelNodes.length; i++) {
			labels[labels.length] = {
				key:labelNodes[i].getAttribute('key'),
				label:labelNodes[i].getAttribute('label')
			};
		};
		this.graph.xAxis.labels = labels;
	}
	if (this.delegate.modifyGraph) {
		this.delegate.modifyGraph(this.graph);
	}
	if (this.renderer) {
		
	}
	if (this.renderer) {
		this.renderer.setGraph(this.graph);
		this.renderer.render();
	}
	if (this.delegate.onSuccess) {
		this.delegate.onSuccess(this.graph);
	}
}

/**
 * @private
 */
Atira.Graph.Loader.prototype.parseDataset = function(node) {
	var type = node.getAttribute('type');
	var legend = node.getAttribute('legend');
	var dataSet = new Atira.Graph.DataSet(type);
	dataSet.setLegend(legend);
	var entries = this.getChildren(node,'entry');
	for (var i=0; i < entries.length; i++) {
		var entry = entries[i];
		dataSet.addEntry(entry.getAttribute('key'),parseFloat(entry.getAttribute('value')))
	};
	var subSets = this.getChildren(node,'dataset');
	for (var i=0; i < subSets.length; i++) {
		var subSet = this.parseDataset(subSets[i]);
		dataSet.addDataSet(subSet);
	};
	return dataSet;
}

/**
 * @private
 */
Atira.Graph.Loader.prototype.getChildren = function(node,nodeName) {
	var result = [];
	var children = node.childNodes;
	for (var i=0; i < children.length; i++) {
		if (children[i].nodeName==nodeName) {
			result[result.length] = children[i];
		}
	};
	return result;
}


/*********************************************************************/
/*                             Data set                              */
/*********************************************************************/
/**
 * @constructor
 * @param {String} type The type of the dataset [ 'line' | 'column' | 'pie' ]
 */
Atira.Graph.DataSet = function(type) {
	/** @private */
	type = type ? type : 'line';
	/** @private */
	this.dataSets = [];
	/** @private */
	this.entries = [];
	/** @private */
	this.legend = null;
	/** @private */
	this.style = {type:type,colors:null};
}


Atira.Graph.DataSet.prototype.hasData = function() {
	if (this.entries.length>0) {
		return true;
	}
	var hasData=false;
	for (var i=0;i<this.dataSets.length && !hasData;i++) {
		hasData=this.dataSets[i].hasData();
	}
	return hasData;
}

/**
 * Adds a dataset as a subset of this dataset
 * @param {Atira.Graph.DataSet} dataSet The data set to add
 */
Atira.Graph.DataSet.prototype.addDataSet = function(dataSet) {
	this.dataSets[this.dataSets.length] = dataSet;
}

/**
 * Sets the legend of the data set
 * @param {String} legend The legend of the data set
 */
Atira.Graph.DataSet.prototype.setLegend = function(legend) {
	this.legend = legend;
}

Atira.Graph.DataSet.prototype.getLegends = function(graph,colors) {
	var legends = [];
	
	if (this.style.colors) {
		colors = this.style.colors;
	}
	if (this.style.type=='pie') {
		for (var i=0;i<graph.xAxis.labels.length;i++) {
			legends[legends.length] = {color:colors[i % colors.length],title:this.getEntryValue(graph.xAxis.labels[i].key)+' : '+graph.getLabelForAxisKey(graph.xAxis.labels[i].key)};
		}
	}
	else if (this.isMultiDimensional()) {
		for (var i=0; i < this.dataSets.length; i++) {
			var set = this.dataSets[i];
			if (set.legend) {
				legends[legends.length] = {color:colors[i % colors.length],title:set.legend};
			}
		};
	} else if (this.legend!=null) {
		legends[0]={color:colors[0],title:this.legend};
	}
	return legends;
}

/**
 * @private
 */
Atira.Graph.DataSet.prototype.isMultiDimensional = function() {
	return this.dataSets.length>0;
}

/**
 * Adds an entry to the data set
 * @param {String} key The key of the entry
 * @param {Number} value The value of the entry
 */
Atira.Graph.DataSet.prototype.addEntry = function(key,value) {
	this.entries[this.entries.length] = {key:key,value:value};
}


/**
 * Adds entries to the data set based on the labels of the x-axis of a graph
 * @param {Atira.Graph} graph The grpah
 * @param {Array} values An array of values
 */
Atira.Graph.DataSet.prototype.setValues = function(graph,values) {
	for (var i=0; i < graph.xAxis.labels.length; i++) {
		if (values[i]) {
			this.addEntry(graph.xAxis.labels[i].key,values[i])
		}
	};
}

Atira.Graph.DataSet.prototype.getKeys = function(key) {
	var out = [];
	for (var i=0;i<this.entries.length;i++) {
		out[out.length] = this.entries[i].key;
	}
	return out;
}

Atira.Graph.DataSet.prototype.getEntry = function(key) {
	for (var i=0;i<this.entries.length;i++) {
		if (this.entries[i].key==key) {
			return this.entries[i]
		}
	}
	return null;
}

/**
 * @private
 */
Atira.Graph.DataSet.prototype.getEntryValue = function(key) {
	var value = 0;
	for (var i=0;i<this.entries.length;i++) {
		if (this.entries[i].key==key) {
			return this.entries[i].value;
		}
	}
	return value;
}

/**
 * @private
 */
Atira.Graph.DataSet.prototype.getEntryValue2D = function(key) {
	var value = [];
	for (var i=0;i<this.dataSets.length;i++) {
		var set = this.dataSets[i];
		for (var j=0;j<set.entries.length;j++) {
			if (set.entries[j].key==key) {
				value[i] = set.entries[j].value;
			}
		}
		if (!value[i]) {
			value[i]=0;
		}
	}
	return value;
}

/**
 * @private
 */
Atira.Graph.DataSet.prototype.keysToValues = function(keys) {
	var values = [];
	for (var i=0;i<keys.length;i++) {
		values[i] = this.getEntryValue(keys[i].key);
	}
	return values;
}

/**
 * @private
 */
Atira.Graph.DataSet.prototype.keysToValues2D = function(keys) {
	var values = [];
	for (var i=0;i<keys.length;i++) {
		values[i] = this.getEntryValue2D(keys[i].key);
	}
	return values;
}


/**
 * @private
 */
Atira.Graph.DataSet.prototype.getValueRange = function(keys) {
	var vals = [];
	if (this.isMultiDimensional()) {
		var vals2D = this.keysToValues2D(keys);
		for (var i=0;i<vals2D.length;i++) {
			var sum = 0;
			for (var j=0;j<vals2D[i].length;j++) {
				sum+=vals2D[i][j];
			}
			vals[i] = sum;
		}
	} else {
		vals = this.keysToValues(keys);
	}
	var min = Number.MAX_VALUE;
	var max = Number.MIN_VALUE;
	for (var i=0;i<vals.length;i++) {
		if (vals[i]<min) {
			min = vals[i];
		}
		if (vals[i]>max) {
			max = vals[i];
		}
	}
	return {min:min,max:max};
}


/**
 * @private
 */
Atira.Graph.DataSet.prototype.getSubLegends = function() {
	var value = [];
	for (var i=0;i<this.dataSets.length;i++) {
		if (this.dataSets[i].legend) {
			value[value.length] = this.dataSets[i].legend;
		}
	}
	if (value.length>0) {
		return value;
	} else {
		return null;
	}
}






/*********************************************************************/
/*                             Renderer                              */
/*********************************************************************/

/**
 * @constructor
 * @param {Atira.Graph} graph The graph to render
 */
Atira.Graph.Renderer = function(graph) {
	/** @private */
	this.graph = graph;
	/** @private */
	this.hotspots = [];
	/** @private */
	this.noDataReplacement = null;
	/**
	* Whether the graph should be rendered as whole pixels
	* @type {Boolean}
	*/
	this.crisp = false;
	/** @private */
	this.state = { numColumns:0, currColumn:0, numPies:0, currPie:0, xLabels:[], yLabels:[], innerBody:{}, coordinateSystem: false, currColor:0, extraPadding: {top:0,bottom:0,left:0,right:0}};
}

Atira.Graph.Renderer.prototype.setGraph = function(graph) {
	this.graph = graph;
}

Atira.Graph.Renderer.prototype.setElement = function(element) {
	this.container = $id(element);
}

Atira.Graph.Renderer.prototype.setNoDataReplacement = function(html) {
	this.noDataReplacement = html;
}

/**
 * @private
 */
Atira.Graph.Renderer.prototype.createCanvas = function(element) {
	var canvas = document.createElement('canvas');
	canvas.setAttribute('width',this.graph.body.width);
	canvas.setAttribute('height',this.graph.body.height);
	element.appendChild(canvas);
	if (typeof G_vmlCanvasManager != 'undefined') {
   		var newCanvas = G_vmlCanvasManager.initElement(canvas);
		canvas = newCanvas;
	}
	return canvas;
}

Atira.Graph.Renderer.prototype.initHover = function() {
	var self = this;
	if (!Atira.Graph.Renderer.tooltip) {
		Atira.Graph.Renderer.tooltip = document.createElement('div');
		document.body.appendChild(Atira.Graph.Renderer.tooltip);
	}
	this.container.onmousemove = function(e) {
		self.hover(e);
	}
	this.container.onmouseout = function(e) {
		Atira.Graph.Renderer.tooltip.style.display='none';
	}
	with (Atira.Graph.Renderer.tooltip.style) {
		position='absolute';
		display='none';
		zIndex='1000';
		textAlign='left';
		whiteSpace='nowrap';
		backgroundColor='#ffd';
		border='1px solid #dda';
		color='#660';
		font='9pt Verdana';
		padding='2px 5px';
	}
}

/**
 * Renders the graph onto an element
 * @param {Element} canvas (optional) The element to render the graph onto (must be a canvas-tag)
 */
Atira.Graph.Renderer.prototype.render = function(element) {
	if (element) {
		this.container = $id(element);
	}
	this.container.innerHTML='';
	
	
	// Dont draw if no data and replacement exists
	if (!this.graph.hasData()) {
		if (this.noDataReplacement) {
			this.container.innerHTML=this.noDataReplacement;
			return;
		}
	}
	
	this.canvas = this.createCanvas(this.container);
	
	this.initHover();
	
	var old = $class('atira_graph',this.container);
	for (var i=0; i < old.length; i++) {
		old[i].parentNode.removeChild(old[i]);
	};

	this.ctx = this.canvas.getContext("2d");
	var legends = [];
	// Extract basic info about the chart
	for (var i=0;i<this.graph.dataSets.length;i++) {
		var set = this.graph.dataSets[i];
		if (set.style.type=='line' || set.style.type=='column') {
			this.state.coordinateSystem = true;
		}
		if (set.style.type=='column') {
			this.state.numColumns++;
		} else if (set.style.type=='pie') {
			this.state.numPies++;
		}
		var legs = set.getLegends(this.graph,this.graph.style.colors[this.state.currColor]);
		for (var j=0; j < legs.length; j++) {
			legends.push(legs[j]);
		};
		this.nextColor();
	}
	this.state.extraPadding = this.renderLegends(legends);
	
	this.state.xLabels = this.graph.xAxis.labels;
	this.state.yLabels = Atira.Graph.Util.generateYLabels(this.graph);
	this.state.innerBody = this.getInnerBody();

	// Render the coordinate system (below)
	if (this.state.coordinateSystem) {
		this.renderBody();
	}
	
	// Reset state
	this.state.currColor = 0;

	// Loop through data sets and render them
	var xLabels = this.state.xLabels;
	for (var i=0;i<this.graph.dataSets.length;i++) {
		var set = this.graph.dataSets[i];
		if (set.isMultiDimensional()) {
			var values = set.keysToValues2D(xLabels);
		} else {
			var values = set.keysToValues(xLabels);
		}
		if (set.style.type=='line') {
			this.renderLineGraph( { values:values, style:set.style  , legends: set.getLegends(this.graph,['x'])} );
		} else if (set.style.type=='column') {
			this.renderColumnGraph( { values:values, style:set.style , legends: set.getLegends(this.graph,['x'])} );	
		} else if (set.style.type=='pie') {
			this.renderPie( { values:values, style:set.style , legends: set.getLegends(this.graph,['x'])} );
		}
		this.nextColor();
	}
	
	// Render the coordinate system (above)
	if (this.state.coordinateSystem) {
		this.renderPostBody();
	}
	
	// Render possible lengends
	//this.renderLegends();
}

Atira.Graph.Renderer.prototype.addDegree = function(value,degree) {
	value+=degree;
	if (value>360) value-=360;
	if (value<0) value=360-value;
	return value;
}

Atira.Graph.Renderer.prototype.hover = function(e) {
	if (!e) e = window.event;
	var scrollLeft = Atira.Window.getScrollLeft();
	var scrollTop = Atira.Window.getScrollTop();
	var x = e.clientX-Atira.Element.getLeft(this.container)+scrollLeft;
	var y = e.clientY-Atira.Element.getTop(this.container)+scrollTop;
	var found = [];
	for (var i=0; i < this.hotspots.length; i++) {
		var spot = this.hotspots[i];
		
		if (spot.type=='square' && x>=spot.left && x<=spot.right && y>=spot.top && y<=spot.bottom) {
			found.push(spot);
		} else if (spot.type=='arc') {
			var a = x-spot.left;
			var b = y-spot.top;
			var dist = Math.sqrt(Math.pow(a,2)+Math.pow(b,2));
			if (dist<spot.radius) {
				var degree = (Math.atan2(b,a)*180/Math.PI)+90;
				if (degree<=0) degree+=360;
			
				var to = (spot.to*180/Math.PI);
				var from = (spot.from*180/Math.PI);
				
				if (degree>=from && degree<=to) {
					found.push(spot);
				}
			}
		}
	};
	if (found.length>0) {
		if (found.action) {
			this.container.style.cursor='pointer';
		}
		var label = '';
		for (var i=0;i<found.length;i++) {
			label+='<div>'+found[i].label+'</div>';
		}
		Atira.Graph.Renderer.tooltip.innerHTML = label;
		Atira.Graph.Renderer.tooltip.style.display='';
		Atira.Graph.Renderer.tooltip.style.top=(e.clientY+10+scrollTop)+'px';
		Atira.Graph.Renderer.tooltip.style.left=(e.clientX+10+scrollLeft)+'px';
	} else {
		this.container.style.cursor='';
		Atira.Graph.Renderer.tooltip.style.display='none';
	}
}

Atira.Graph.Renderer.prototype.nextColor = function() {
	if (this.state.currColor+2>this.graph.style.colors.length) {
		this.state.currColor = 0;
	} else {
		this.state.currColor++;
	}
}

/**
 * Renders a legend box
 * @private
 */
Atira.Graph.Renderer.prototype.renderLegends = function(legends) {
	var spaceConsumed = {top:0,bottom:0,left:0,right:0};
	if (legends.length>0) {
		var position = this.graph.style.legends.position;
		var box = document.createElement('div');
		box.style.position='absolute';
		box.style.zIndex = 5;
		box.style.width=this.graph.body.width+'px';
		
		var html='<div class="atira_graph atira_graph_legend" style="float: right;'+(this.graph.style.legends.border ?  'border:1px solid '+ this.graph.style.legends.border+';' : '')+' background: #fff; position: relative; padding: 3px; font-size: 10px; font-family: Tahoma,Verdana,Geneva,sans-serif; overflow: hidden;">';
		for (var i=0;i<legends.length && i<50;i++) {
			var color=legends[i].color;
			if (position=='bottom') {
				var style = 'padding: 2px;'; // white-space: nowrap;
				if (this.graph.style.legends.direction=='auto') {
					style+=' float: left; padding-right: 8px;';
				} else {
					style+=' overflow: hidden; padding-right: 4px;';
				}
				if (i==legends.length-1) style+='padding-right: 3px';
			} else {
				var style = 'padding: 2px;';
			}
			html+='<div style="'+style+'"><div style="float: left; border: 1px solid #aaa; background: '+color+'; width: 12px; height: 12px; margin-right: 4px;"></div>'+legends[i].title+'</div>'
		}
		html+='</div>';
		box.innerHTML = html;
		if (position=='right') {
			box.firstChild.style.marginTop = this.graph.body.paddingTop+'px';
			box.firstChild.style.right = this.graph.body.paddingRight+'px';
			this.container.insertBefore(box,this.canvas);
			if (!this.graph.style.legends.above) {
				spaceConsumed.right = Atira.Element.getWidth(box.firstChild)+10;
			}
		} else if (position=='bottom') {
			this.container.appendChild(box);
			var y = document.createElement('div');
			y.appendChild(box);
			this.container.appendChild(y);
			box.firstChild.style.maxWidth = this.graph.body.width+'px';
			box.firstChild.style.right = this.graph.body.paddingRight+'px';
			box.firstChild.style.marginLeft = '20px';
			var height = Atira.Element.getHeight(box.firstChild);
			box.firstChild.style.marginTop = '-'+(height+2+(this.graph.body.paddingBottom/2))+'px';
			if (!this.graph.style.legends.above) {
				spaceConsumed.bottom = height+12;
			}
		}
	}
	return spaceConsumed;
}

/**
 * Renders the body of the chart
 * @private
 */
Atira.Graph.Renderer.prototype.renderBody = function() {

	var body = this.graph.body;

	if (this.graph.style.background) {
		this.ctx.fillStyle=this.graph.style.background;
		this.ctx.fillRect(body.paddingLeft,body.paddingTop,body.width-body.paddingLeft-body.paddingRight-this.state.extraPadding.right,body.height-body.paddingTop-body.paddingBottom-this.state.extraPadding.bottom);
	}
	var innerBody = this.state.innerBody;
	
	var mod = 1;
	/* Build X-axis*/
	var xLabels = this.state.xLabels;
	if (xLabels.length>this.graph.xAxis.maxLabels) {
		mod = Math.ceil(xLabels.length/this.graph.xAxis.maxLabels);
	}
	for (var i=0;i<xLabels.length;i++) {
		var left = 0;
		if (xLabels.length>1) {
			left = i*((innerBody.width)/(xLabels.length-1))+innerBody.left;
		} else if (xLabels.length==1) {
			left = i*((innerBody.width)/2)+innerBody.left;
		}
		left = Math.round(left);

		// Draw grid
		if (this.graph.xAxis.grid) {
			this.ctx.strokeStyle=this.graph.xAxis.grid;
			this.ctx.beginPath();
			this.ctx.moveTo(.5+left,body.paddingTop+.5);
			this.ctx.lineTo(.5+left,body.paddingTop+.5+body.height-body.paddingTop-body.paddingBottom-this.state.extraPadding.bottom);
			this.ctx.stroke();
			this.ctx.closePath();
		}
		if ((i % mod) ==0) {
		// Draw label
		var label = document.createElement('span');
		label.appendChild(document.createTextNode(xLabels[i].label));
		label.style.position='absolute';
		label.style.marginLeft=left-25+'px';
		label.style.textAlign = 'center';
		label.style.width = '50px';
		label.style.font='9px Tahoma';
		label.style.marginTop=body.height-body.paddingBottom-this.state.extraPadding.bottom+4+'px';
		label.className = "atira_graph";
		this.container.insertBefore(label,this.canvas);
		}
	}
	
	/* Build Y-axis*/
	var yLabels = this.state.yLabels.concat();
	yLabels.reverse();
	for (var i=0;i<yLabels.length;i++) {
		// Draw grid
		var top = i*((body.height-body.innerPaddingVertical*2-body.paddingTop-body.paddingBottom-this.state.extraPadding.bottom)/(yLabels.length-1))+body.paddingTop+body.innerPaddingVertical;
		top = Math.round(top);
		if (!this.graph.yAxis.above && this.graph.yAxis.grid) {
			this.ctx.strokeStyle=this.graph.yAxis.grid;
			this.ctx.beginPath();
			this.ctx.moveTo(.5+body.paddingLeft,top+.5);
			this.ctx.lineTo(.5+body.width-body.paddingRight-this.state.extraPadding.right,top+.5);
			this.ctx.stroke();
			this.ctx.closePath();
		}
		// Draw label
		var label = document.createElement('span');
		label.appendChild(document.createTextNode(yLabels[i]));
		label.style.position='absolute';
		label.style.textAlign='right';
		label.style.width=body.paddingLeft-3+'px';
		label.style.font='9px Tahoma';
		label.style.marginTop=top-5+'px';
		label.className = "atira_graph";
		this.container.insertBefore(label,this.canvas);
	}
	// Draw a line at 0 if 
	if (!this.graph.yAxis.above && yLabels[0]>0 && yLabels[yLabels.length-1]<0) {
		var top = (body.height-body.innerPaddingVertical*2-body.paddingTop-body.paddingBottom-this.state.extraPadding.bottom)*yLabels[0]/(yLabels[0]-yLabels[yLabels.length-1])+body.paddingTop+body.innerPaddingVertical;
		top = Math.round(top);
		this.ctx.lineWidth = 2;
		this.ctx.strokeStyle='rgb(255,255,255)';
		this.ctx.beginPath();
		this.ctx.moveTo(.5+body.paddingLeft,top);
		this.ctx.lineTo(.5+body.width-body.paddingRight-this.state.extraPadding.right,top);
		this.ctx.stroke();
		this.ctx.closePath();
	}
}


/**
 * @private
 */
Atira.Graph.Renderer.prototype.renderPostBody = function() {
	var body = this.graph.body;
	if (this.graph.yAxis.above) {

		this.ctx.strokeStyle=this.graph.style.background || '#fff';
		var yLabels = this.state.yLabels.concat();
		yLabels.reverse();
		for (var i=0;i<yLabels.length;i++) {
			var top = i*((body.height-body.innerPaddingVertical*2-body.paddingTop-body.paddingBottom-this.state.extraPadding.bottom)/(yLabels.length-1))+body.paddingTop+body.innerPaddingVertical;
			top = Math.round(top);
			this.ctx.lineWidth = 1;
			this.ctx.beginPath();
			this.ctx.moveTo(.5+body.paddingLeft,top+.5);
			this.ctx.lineTo(.5+body.width-body.paddingRight-this.state.extraPadding.right,top+.5);
			this.ctx.stroke();
			this.ctx.closePath();
		}
		if (yLabels[0]>0 && yLabels[yLabels.length-1]<0) {
			var top = (body.height-body.innerPaddingVertical*2-body.paddingTop-body.paddingBottom-this.state.extraPadding.bottom)*yLabels[0]/(yLabels[0]-yLabels[yLabels.length-1])+body.paddingTop+body.innerPaddingVertical;
			top = Math.round(top);
			this.ctx.lineWidth = 2;
			this.ctx.strokeStyle='rgb(255,255,255)';
			this.ctx.beginPath();
			this.ctx.moveTo(.5+body.paddingLeft,top);
			this.ctx.lineTo(.5+body.width-body.paddingRight-this.state.extraPadding.right,top);
			this.ctx.stroke();
			this.ctx.closePath();
		}
	}
	if (this.graph.style.border) {
		this.ctx.lineWidth = 1;
		this.ctx.strokeStyle=this.graph.style.border;
		this.ctx.strokeRect(body.paddingLeft+.5,body.paddingTop+.5,body.width-body.paddingLeft-body.paddingRight-this.state.extraPadding.right-1,body.height-body.paddingTop-body.paddingBottom-this.state.extraPadding.bottom);
	}
}


/**
 * @private
 */
Atira.Graph.Renderer.prototype.getInnerBody = function() {
	var body = this.graph.body;
	var xLabels = this.state.xLabels;
	var space = 0;
	if (this.state.numColumns>0) {
		space = (body.width-2*body.innerPaddingHorizontal-body.paddingLeft-body.paddingRight-this.state.extraPadding.right)/xLabels.length;
	}
	var innerBody = {
		left:(body.innerPaddingHorizontal+body.paddingLeft+space/2),
		top:(body.paddingTop+body.innerPaddingVertical),
		width:(body.width-2*body.innerPaddingHorizontal-body.paddingLeft-body.paddingRight-this.state.extraPadding.right-space),
		height:(body.height-body.innerPaddingVertical*2-body.paddingTop-body.paddingBottom-this.state.extraPadding.bottom)
	};
	return innerBody;
}


/**
 * @private
 */
Atira.Graph.Renderer.prototype.renderLineGraph = function(data) {
	var values = data.values;
	var xLabels = this.state.xLabels;
	var yLabels = this.state.yLabels;
	var yMin = yLabels[0];
	var yMax = yLabels[yLabels.length-1];
	var body = this.graph.body;
	var innerBody = this.state.innerBody;
	if (data.style.colors) {
		var color = data.style.colors[0];
	} else {
		var color = this.graph.style.colors[this.state.currColor][0];
	}
	this.ctx.strokeStyle = color;
	this.ctx.lineWidth = data.width ? data.width : 2;
	this.ctx.lineCap = this.ctx.lineJoin = 'round';
	this.ctx.beginPath();
	for (var i=0;i<xLabels.length;i++) {
		var val = values[i];
		if (val instanceof Array) {
			val = Atira.Graph.Util.arraySum(val);
		}
		val = (val==undefined ? 0 : val);
		var value = (val-yMin)/(yMax-yMin);
		var top = body.height-value*(innerBody.height)-body.innerPaddingVertical-body.paddingBottom-this.state.extraPadding.bottom;
		var left = 0;
		if (xLabels.length>1) {
			left = i*(innerBody.width/(xLabels.length-1))+innerBody.left;
		} else if (xLabels.length==1) {
			left = i*(innerBody.width/2)+innerBody.left;
		}
		if (i==0) {
			this.ctx.moveTo(left+.5,top+.5);
		} else {
			this.ctx.lineTo(left+.5,top+.5);
		}
		var label = val+(data.legends.length>0 ? ' : '+data.legends[0].title : '');
		this.hotspots[this.hotspots.length] = {type:'square',label:label,top:top-4,bottom:top+4,left:left-4,right:left+4};

	}
	this.ctx.stroke();
	this.ctx.closePath();
}



/**
 * @private
 */
Atira.Graph.Renderer.prototype.renderColumnGraph = function(data) {
	
	var values = data.values;
	var xLabels = this.state.xLabels;
	var yLabels = this.state.yLabels;
	var yMin = yLabels[0];
	var yMax = yLabels[yLabels.length-1];
	var body = this.graph.body;
	var colors = data.style.colors ? data.style.colors : this.graph.style.colors[this.state.currColor];
	this.state.currColumn++;
	var innerBody = this.state.innerBody;
	var space = (body.width-body.paddingLeft-body.paddingRight-this.state.extraPadding.right)/xLabels.length*this.graph.xAxis.concentration;
	var thickness = space/this.state.numColumns;
	this.ctx.lineCap = this.ctx.lineJoin = 'round';
	this.ctx.beginPath();
	for (var i=0;i<xLabels.length;i++) {
		if (values[i]) {
			var colorIndex = 0;
			var currTop = 0;
			if (values[i] instanceof Array) {
				for (var j=0;j<values[i].length;j++) {
					var val = values[i][j];
					currTop+=this.renderOneColumn(val,colors[colorIndex],body,innerBody,yMin,yMax,currTop,i,xLabels,space,thickness,data.legends[j]);
					
					if (colorIndex+2>colors.length) {
						colorIndex = 0;
					} else {
						colorIndex++;
					}
				}
			} else {
				currTop+=this.renderOneColumn(values[i],colors[colorIndex],body,innerBody,yMin,yMax,currTop,i,xLabels,space,thickness,data.legends[0]);
			}
		}
	}
	this.ctx.stroke();
	this.ctx.closePath();
}

/**
 * @private
 */
Atira.Graph.Renderer.prototype.renderOneColumn = function(val,color,body,innerBody,yMin,yMax,currTop,i,xLabels,space,thickness,legend) {
	var value = (val-yMin)/(yMax-yMin);
	if (yMin<=0 && val<=0) {
		var top = innerBody.top+(innerBody.height)*yMax/(yMax-yMin)+currTop;
		var height = innerBody.height*Math.abs(val)/(yMax-yMin);
	} else if (yMin<=0) {
		var top = body.height-body.innerPaddingVertical-body.paddingBottom-this.state.extraPadding.bottom-value*(innerBody.height)-currTop;
		var height = (innerBody.height)*Math.abs(val)/(yMax-yMin);
	}
	else {
		var top = body.height-value*(body.height-body.innerPaddingVertical*2-body.paddingTop-body.paddingBottom-this.state.extraPadding.bottom)-body.innerPaddingVertical-body.paddingBottom-this.state.extraPadding.bottom-currTop;
		var height = (body.height-body.paddingBottom-this.state.extraPadding.bottom-top);
	}
	var left = 0;
	if (xLabels.length>1) {
		left = i*((innerBody.width)/(xLabels.length-1))+innerBody.left;
	} else if (xLabels.length==1) {
		left = i*((innerBody.width)/2)+innerBody.left;
	}
	
	this.ctx.fillStyle = color;
	if (this.crisp) {
		this.ctx.fillRect(Math.round(left-space/2+thickness*(this.state.currColumn-1)),Math.floor(top),Math.ceil(thickness),Math.ceil(height));
	} else {
		this.ctx.fillRect(left-space/2+thickness*(this.state.currColumn-1),top,thickness,height);
	}
	var label = val+(legend ? ' : '+legend.title : '');
	this.hotspots[this.hotspots.length] = {type:'square',label:label,top:top,bottom:top+height,left:Math.round(left-space/2+thickness*(this.state.currColumn-1)),right:Math.round(left-space/2+thickness*(this.state.currColumn-1))+Math.ceil(thickness)};
	return height;
}

/**
 * @private
 */
Atira.Graph.Renderer.prototype.renderPie = function(data) {
	var values = data.values;
	var colors = data.style.colors ? data.style.colors : this.graph.style.colors[this.state.currColor];
	var total = Atira.Graph.Util.arraySum(values);

	var colorIndex = 0;
	var current = 0;
	var vert = this.graph.body.height-this.state.extraPadding.bottom+this.graph.body.paddingTop-this.graph.body.paddingBottom;
	var horz = this.graph.body.width-this.state.extraPadding.right-this.graph.body.paddingLeft-this.graph.body.paddingRight;
	var horzx = (horz/this.state.numPies)*(this.state.currPie+.5)

	var cTop = (vert)/2+this.graph.style.pie.top;
	var cLeft = (horzx)+this.graph.style.pie.left+this.graph.body.paddingLeft;
	var radius = Math.min(vert,horz/this.state.numPies)/2*this.graph.style.pie.radiusFactor;

	for (var i=0;i<values.length;i++) {
		var value = values[i];
		if (value instanceof Array) {
			value = Atira.Graph.Util.arraySum(value);
		}
		if (value==0) continue;
		this.ctx.beginPath();
		var color = colors[colorIndex];
		this.ctx.fillStyle = color;
		var rads = (total>0 ? value/total : 1)*(Math.PI*2);
		this.ctx.moveTo(cLeft,cTop);
		this.ctx.arc(cLeft,cTop,radius,current+Math.PI*1.5,Math.PI*1.5+current+rads-.0001,false); // .0001 = IE fix
		this.ctx.lineTo(cLeft,cTop);
		this.ctx.fill();
		this.ctx.closePath();
		if (colorIndex+2>colors.length) {
			colorIndex = 0;
		} else {
			colorIndex++;
		}
		var spot = {type:'arc',label:data.legends[i].title,top:cTop,left:cLeft,radius:radius,from:current,to:current+rads};
		this.hotspots[this.hotspots.length] = spot;
		current+=rads;
	}
	this.state.currPie++;
	
}





/*********************************************************************/
/*                           Utitlities                              */
/*********************************************************************/

/**
 * @class
 * @constructor
 */
Atira.Graph.Util = function() {}

Atira.Graph.Util.generateYLabels = function(graph) {
	var range = Atira.Graph.Util.getYrange(graph);
	var labels = [];
	for (var i=0;i<=graph.yAxis.steps;i++) {
		labels[labels.length] = Math.round(range.min+(range.max-range.min)/graph.yAxis.steps*i);
	}
	return labels;
}

Atira.Graph.Util.getYrange = function(graph) {
	var min=graph.yAxis.min;
	var max=graph.yAxis.max;
	for (var i=0;i<graph.dataSets.length;i++) {
		var range = graph.dataSets[i].getValueRange(graph.xAxis.labels);
		if (range.min<min) {
			min=range.min;
		}
		if (range.max>max) {
			max=range.max;
		}
	}
	var factor = max/graph.yAxis.steps;
	if (factor<graph.yAxis.factor) {
		factor = Math.ceil(factor);
	} else {
		factor = graph.yAxis.factor;
	}
	if (max!=Number.MIN_VALUE) {
		max = Math.ceil(max/factor/graph.yAxis.steps)*factor*graph.yAxis.steps;
	} else {
		max = graph.yAxis.steps;
	}
	return {min:min,max:max};
}

Atira.Graph.Util.arraySum = function(values) {
	var total = 0;
	for (var i=0;i<values.length;i++) {
		if (values[i] instanceof Array) {
			for (var j=0; j < values[i].length; j++) {
				total+=values[i][j]
			};
		} else {
			total+=values[i];
		}
	}
	return total;
}

Atira.Graph.Util.makeDataSetsPercentWise = function(dataSets) {
	var keysCombined = [];
	for (var i=0; i < dataSets.length; i++) {
		var keys = dataSets[i].getKeys();
		Atira.Graph.Util.addToArray(keysCombined,keys);
	};
	
	for (var i=0; i < keysCombined.length; i++) {
		var key = keysCombined[i];
		var sum = 0;
		for (var j=0; j < dataSets.length; j++) {
			var entry = dataSets[j].getEntry(key);
			if (entry!=null) {
				sum+=entry.value;
			}
		};
		for (var j=0; j < dataSets.length; j++) {
			var entry = dataSets[j].getEntry(key);
			if (entry!=null) {
				entry.value = Math.round(entry.value/sum*10000)/100;
			}
		};
	};
	Atira.log(keysCombined);
}

Atira.Graph.Util.addToArray = function(arr,arrToAdd) {
	for (var i=0; i < arrToAdd.length; i++) {
		var found = false;
		for (var j=0; j < arr.length; j++) {
			if (arrToAdd[i]==arr[j]) {
				found=true;
				break;
			}
		};
		if (!found) {
			arr[arr.length]=arrToAdd[i];
		}
	};
}
