/*
	Copyright (c) 2004-2008, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing
*/


if(!dojo._hasResource["dojox.charting.Theme"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.charting.Theme"] = true;
dojo.provide("dojox.charting.Theme");
dojo.require("dojox.charting._color");

(function(){
	var dxc=dojox.charting;
	//	TODO: Legend information

	dxc.Theme = function(/*Object?*/ kwArgs){
		kwArgs=kwArgs||{};
		var def = dxc.Theme._def;
		dojo.forEach(["chart", "plotarea", "axis", "series", "marker"], function(n){
			this[n] = dojo.mixin(dojo.clone(def[n]), kwArgs[n]||{});
		}, this);
		this.markers = dojo.mixin(dojo.clone(dxc.Theme.Markers), kwArgs.markers||{});
		this.colors = [];
		this.antiAlias = ("antiAlias" in kwArgs)?kwArgs.antiAlias:true;
		this.assignColors = ("assignColors" in kwArgs)?kwArgs.assignColors:true;
		this.assignMarkers = ("assignMarkers" in kwArgs)?kwArgs.assignMarkers:true;
		this._colorCache = null;

		//	push the colors, use _def colors if none passed.
		kwArgs.colors = kwArgs.colors||def.colors;
		dojo.forEach(kwArgs.colors, function(item){ 
			this.colors.push(item); 
		}, this);

		//	private variables for color and marker indexing
		this._current = { color:0, marker: 0 };
		this._markers = [];
		this._buildMarkerArray();
	};

	//	"static" fields
	//	default markers.
	//	A marker is defined by an SVG path segment; it should be defined as
	//		relative motion, and with the assumption that the path segment
	//		will be moved to the value point (i.e prepend Mx,y)
	dxc.Theme.Markers={
		CIRCLE:		"m-3,0 c0,-4 6,-4 6,0 m-6,0 c0,4 6,4 6,0", 
		SQUARE:		"m-3,-3 l0,6 6,0 0,-6 z", 
		DIAMOND:	"m0,-3 l3,3 -3,3 -3,-3 z", 
		CROSS:		"m0,-3 l0,6 m-3,-3 l6,0", 
		X:			"m-3,-3 l6,6 m0,-6 l-6,6", 
		TRIANGLE:	"m-3,3 l3,-6 3,6 z", 
		TRIANGLE_INVERTED:"m-3,-3 l3,6 3,-6 z"
	};
	dxc.Theme._def={
		//	all objects are structs used directly in dojox.gfx
		chart:{ 
			stroke:null,
			fill: "white"
		},
		plotarea:{ 
			stroke:null,
			fill: "white"
		},
		//	TODO: label rotation on axis
		axis:{
			stroke:	{ //	the axis itself
				color:"#333",
				width:1
			},
			line:	{ //	gridlines
				color:"#ccc",
				width:1,
				style:"Dot",
				cap:"round"
			},
			majorTick:	{ //	major ticks on axis
				color:"#666",
				width:1, 
				length:6, 
				position:"center"
			},
			minorTick:	{ //	minor ticks on axis
				color:"#666", 
				width:0.8, 
				length:3, 
				position:"center"
			},	
			font: "normal normal normal 7pt Tahoma", //	labels on axis
			fontColor:"#333"						//	color of labels
		},
		series:{
			outline: {width: 0.1, color: "#ccc"},							//	line or outline
			stroke: {width: 1.5, color: "#333"},							//	line or outline
			fill: "#ccc",												//	fill, if appropriate
			font: "normal normal normal 7pt Tahoma",					//	if there's a label
			fontColor: "#000"											// 	color of labels
		},
		marker:{	//	any markers on a series.
			stroke: {width:1},											//	stroke or outline
			fill: "#333",												//	fill if needed
			font: "normal normal normal 7pt Tahoma",					//	label
			fontColor: "#000"
		},
		colors:[
			"#000","#111","#222","#333",
			"#444","#555","#666","#777",
			"#888","#999","#aaa","#bbb",
			"#ccc"
		]
	};
	
	//	prototype methods
	dojo.extend(dxc.Theme, {
		defineColors: function(obj){
			//	summary:
			//		Generate a set of colors for the theme based on keyword
			//		arguments
			var kwArgs=obj||{};

			//	deal with caching
			var cache = false;
			if(kwArgs.cache === undefined){ cache = true; }
			if(kwArgs.cache == true){ cache = true; }
			
			if(cache){
				this._colorCache=kwArgs;
			} else {
				var mix=this._colorCache||{};
				kwArgs=dojo.mixin(dojo.clone(mix), kwArgs);
			}

			var c=[], n=kwArgs.num||32;	//	the number of colors to generate
			if(kwArgs.colors){
				//	we have an array of colors predefined, so fix for the number of series.
				var l=kwArgs.colors.length;
				for(var i=0; i<n; i++){
					c.push(kwArgs.colors[i%l]);
				}
				this.colors=c;
			}else if(kwArgs.hue){
				//	single hue, generate a set based on brightness
				var s=kwArgs.saturation||100;	//	saturation
				var st=kwArgs.low||30;
				var end=kwArgs.high||90;
				var step=(end-st)/n;			//	brightness steps
				for(var i=0; i<n; i++){
					c.push(dxc._color.fromHsb(kwArgs.hue, s, st+(step*i)).toHex());
				}
				this.colors=c;
			}else if(kwArgs.stops){
				//	create color ranges that are either equally distributed, or
				//	(optionally) based on a passed "offset" property.  If you
				//	pass an array of Colors, it will equally distribute, if
				//	you pass an array of structs { color, offset }, it will
				//	use the offset (0.0 - 1.0) to distribute.  Note that offset
				//	values should be plotted on a line from 0.0 to 1.0--i.e.
				//	they should be additive.  For example:
				//	[ {color, offset:0}, { color, offset:0.2 }, { color, offset:0.5 }, { color, offset:1.0 } ]
				//	
				//	If you use stops for colors, you MUST have a color at 0.0 and one
				//	at 1.0.
			
				//	figure out how many stops we have
				var l=kwArgs.stops.length;
				if(l<2){
					throw new Error(
						"dojox.charting.Theme::defineColors: when using stops to "
						+ "define a color range, you MUST specify at least 2 colors."
					);
				}

				//	figure out if the distribution is equal or not.  Note that
				//	colors may not exactly match the stops you define; because
				//	color generation is linear (i.e. evenly divided on a linear
				//	axis), it's very possible that a color will land in between
				//	two stops and not exactly *at* a stop.
				//
				//	The only two colors guaranteed will be the end stops (i.e.
				//	the first and last stop), which will *always* be set as
				//	the end stops.
				if(typeof(kwArgs.stops[0].offset) == "undefined"){ 
					//	set up equal offsets
					var off=1/(l-1);
					for(var i=0; i<l; i++){
						kwArgs.stops[i]={
							color:kwArgs.stops[i],
							offset:off*i
						};
					}
				}
				//	ensure the ends.
				kwArgs.stops[0].offset=0;
				kwArgs.stops[l-1].offset=1;
				kwArgs.stops.sort(function(a,b){ return a.offset-b.offset; });

				//	create the colors.
				//	first stop.
				c.push(kwArgs.stops[0].color.toHex());

				//	TODO: calculate the blend at n/steps and set the color

				//	last stop
				c.push(kwArgs.stops[l-1].color.toHex());
				this.colors=c;
			}
		},
	
		_buildMarkerArray: function(){
			this._markers = [];
			for(var p in this.markers){ this._markers.push(this.markers[p]); }
			//	reset the position
			this._current.marker=0;
		},

		addMarker:function(/*String*/ name, /*String*/ segment){
			//	summary:
			//		Add a custom marker to this theme.
			//	example:
			//	|	myTheme.addMarker("Ellipse", foo);
			this.markers[name]=segment;
			this._buildMarkerArray();
		},
		setMarkers:function(/*Object*/ obj){
			//	summary:
			//		Set all the markers of this theme at once.  obj should be a
			//		dictionary of keys and path segments.
			//
			//	example:
			//	|	myTheme.setMarkers({ "CIRCLE": foo });
			this.markers=obj;
			this._buildMarkerArray();
		},

		next: function(/*String?*/ type){
			//	summary:
			//		get either the next color or the next marker, depending on
			//		what was passed. If type is not passed, it assumes color.
			//	type:
			//		Optional. One of either "color" or "marker". Defaults to
			//		"color".
			//	example:
			//	|	var color = myTheme.next();
			//	|	var color = myTheme.next("color");
			//	|	var marker = myTheme.next("marker");
			if(type == "marker"){
				return this._markers[ this._current.marker++ % this._markers.length ];
			}else{
				return this.colors[ this._current.color++ % this.colors.length ];
			}
		},
		clear: function(){
			// summary:
			//		resets both marker and color counters back to the start.
			//		Subsequent calls to `next` will retrievie the first value
			//		of each depending on the passed type.
			this._current = {color: 0, marker: 0};
		}
	});
})();

}
