dojo.provide("dojo.charting.PlotArea");
dojo.require("dojo.lang.common");
dojo.require("dojo.gfx.color");
dojo.require("dojo.gfx.color.hsl");
dojo.require("dojo.charting.Plot");

dojo.charting.PlotArea = function(){
	//	summary
	//	Creates a new PlotArea for drawing onto a Chart.
	var id="dojo-charting-plotarea-"+dojo.charting.PlotArea.count++;
	this.getId=function(){ return id; };
	this.setId=function(key){ id = key; };
	this.areaType = "standard"; 	//	standard || radar
	this.plots = [];	//	plots that will be drawn on this area

	this.size={ width:600, height:400 };
	this.padding={ top:10, right:10, bottom:20, left:20 };

	//	drawing node references.
	this.nodes = {
		main:null,
		area:null,
		background: null,
		axes: null,
		plots: null
	};

	//	this is preset for a limited color range (green to purple), 
	//	anticipating a max of 32 series on this plot area.
	//	if you need more flexibility, override these numbers.
	this._color = { h: 140, s: 120, l: 120, step: 27 };
};
dojo.charting.PlotArea.count = 0;

dojo.extend(dojo.charting.PlotArea, {
	nextColor: function(){
		//	summary
		//	Advances the internal HSV cursor and returns the next generated color.
		var rgb=dojo.gfx.color.hsl2rgb(this._color.h, this._color.s, this._color.l);
		this._color.h = (this._color.h + this._color.step)%360;
		while(this._color.h < 140){ 
			this._color.h += this._color.step; 
		}
		return dojo.gfx.color.rgb2hex(rgb[0], rgb[1], rgb[2]);	//	string
	},
	getArea:function(){
		//	summary
		//	Return an object describing the coordinates of the available area to plot on.
		return {
			left: this.padding.left,
			right: this.size.width - this.padding.right,
			top: this.padding.top,
			bottom: this.size.height - this.padding.bottom,
			toString:function(){ 
				var a=[ this.top, this.right, this.bottom, this.left ];
				return "["+a.join()+"]";
			}
		};	//	object
	},
	getAxes: function(){
		//	summary
		//	get the unique axes for this plot area.
		var axes={};
		for(var i=0; i<this.plots.length; i++){
			var plot=this.plots[i];
			axes[plot.axisX.getId()] = {
				axis: plot.axisX,
				drawAgainst: plot.axisY,
				plot: plot,
				plane: "x"
			};
			axes[plot.axisY.getId()] = {
				axis: plot.axisY,
				drawAgainst: plot.axisX,
				plot: plot,
				plane: "y"
			};
		}
		return axes;	//	object 
	},
	getLegendInfo: function(){
		//	summary
		//	return an array describing all data series on this plot area.
		var a=[];
		for(var i=0; i<this.plots.length; i++){
			for(var j=0; j<this.plots[i].series.length; j++){
				var data = this.plots[i].series[j].data;
				a.push({ label:data.label, color:data.color });
			}
		}
		return a;	//	array
	},
	setAxesRanges: function(){
		//	summary
		//	Find and set the ranges on all axes on this plotArea.
		//	We do this because plots may have axes in common; if you
		//	want to use this, make sure you do it *before* initialization.
		var ranges={};
		var axes={};
		for(var i=0; i<this.plots.length; i++){
			var plot = this.plots[i];
			var ranges=plot.getRanges();
			var x=ranges.x;
			var y=ranges.y;
			var ax, ay;
			if(!axes[plot.axisX.getId()]){
				axes[plot.axisX.getId()]=plot.axisX;
				ranges[plot.axisX.getId()]={upper: x.upper, lower:x.lower};
			}
			ax=ranges[plot.axisX.getId()];
			ax.upper=Math.max(ax.upper, x.upper);
			ax.lower=Math.min(ax.lower, x.lower);

			if(!axes[plot.axisY.getId()]){
				axes[plot.axisY.getId()]=plot.axisY;
				ranges[plot.axisY.getId()]={upper: y.upper, lower:y.lower};
			}
			ay=ranges[plot.axisY.getId()];
			ay.upper=Math.max(ay.upper, y.upper);
			ay.lower=Math.min(ay.lower, y.lower);
		}
		
		//	now that we have all the max/min ranges, set the axes
		for(var p in axes){
			axes[p].range=ranges[p];
		}
	},

	render: function(/* object? */kwArgs, /* function? */applyToData){
		//	summary
		//	Render this plotArea.  Optional kwArgs are the same as that taken for Series.evaluate;
		//	applyToData is a callback function used by plotters for customization.
		if(!this.nodes.main
			|| !this.nodes.area 
			|| !this.nodes.background 
			|| !this.nodes.plots 
			|| !this.nodes.axes
		){ this.initialize(); }

		//	do any size fixes.
		this.resize();

		//	plot it.
		for(var i=0; i<this.plots.length; i++){
			var plot=this.plots[i];
			if(plot.dataNode){
				this.nodes.plots.removeChild(plot.dataNode);
			}
			var target = this.initializePlot(plot);
			switch(plot.renderType){
				case dojo.charting.RenderPlotSeries.Grouped:	{
					// ALWAYS plot using the first plotter, ignore any others.
					if(plot.series[0]){
						target.appendChild(plot.series[0].plotter(this, plot, kwArgs, applyToData));
					}
					break;
				}
				case dojo.charting.RenderPlotSeries.Singly:
				default: {
					for(var j=0; j<plot.series.length; j++){
						var series = plot.series[j];
						var data = series.data.evaluate(kwArgs);
						target.appendChild(series.plotter(data, this, plot, applyToData));
					}
				}
			}
			this.nodes.plots.appendChild(target);
		}
	},
	destroy: function(){
		//	summary
		//	Clean out any existing DOM references.
		for(var i=0; i<this.plots.length; i++){
			this.plots[i].destroy();
		};
		//	clean out any child nodes.
		for(var p in this.nodes){
			var node=this.nodes[p];
			if(!node) continue;
			if(!node.childNodes) continue;
			while(node.childNodes.length > 0){ 
				node.removeChild(node.childNodes[0]); 
			}
			this.nodes[p]=null;
		}
	}
});

dojo.requireIf(dojo.render.svg.capable, "dojo.charting.svg.PlotArea");
dojo.requireIf(dojo.render.vml.capable, "dojo.charting.vml.PlotArea");
