var tt = window.tt || {};


//singleton w closure - simply creates a style code
tt.anim = (function()
{
	//private
	var styleCode = "";
	var codeArray = ["P","n","000","000","0","00","000"];
	var initialCodeArray = ["P","n","DOT","000","001","0","00","030"];	
	//0-type(1)_1-timetrailOrNormal(1)_2-icon(3)_3-width(3)
	//_4-colorSeries(3)_5-startBrightTF(1)_6-fadeNum(2)_7-opacityInit(3)
	//L_t_000_030_001_1_03_100  line example
	//P_t_DOT_000_001_0_06_000  point
	//O_n_000_000_001_0_00_060  polygon
	
	
	function setStyleCode(index, code)
	{
		codeArray[index] = code;
		styleCode = codeArray.join("_");
deBugClog("ws-anim", ["setStyleCode::", "|set position:",index, "|to code:", code, "|full code:", styleCode]);
		//cheat to pop the text entry textarea
		$("#entry").val(styleCode);
	} 
	
	//public
	return {	
		SetStyleCode : function(index, code) { setStyleCode(index, code); },
		GetStyleCode : function() { return styleCode; },
		InitializeStyleCode : function() { codeArray = initialCodeArray; }
	};		
})();

/*
 * 
 * Coordset handler
 * 
 * 
 */

var coordsetHandler = function(coordsString, featureType)
{
	//must have a valid
	separator = " ";
	if(coordsString.indexOf(";") >= 0) {
		separator = ";";
	}
	this.coordset = coordsString.trim().split(separator);
	this.letterIndicator = featureType;  //will be a letter
	this.numberIndicator = 0;
	this.textIndicator = "";
	this.setIndicators();
	this.highestLat = 0;
	this.lowestLat = 0;
	this.highestLon = 0;
	this.lowestLon = 0;
	this.midLat = 0;
	this.midLon = 0;
	this.setOuters();
	//deBugClog("animation", "check Outers " + this.midLat + "  midlon " + this.midLon);

	
};
coordsetHandler.prototype.setIndicators = function()
{
	//P point 1
	//L line 2
	//O poly 3
	//C circle 4
	
	//sets the number and text indicators 
	//of the feature type or leaves it zero/blank if uncertain
	
	function findByInspection(cset) {
		if(cset[0].search("km") >= 0){
			//must be a circle
			return 4;
		}
		if(cset.length === 1){
			//must be a point
			return 1;
		}
		if(cset[0]===cset[cset.length-1]){
			//must be a poly
			return 3;
		}
		else{
			//must be a line
			return 2;
		}
	};
	
	var byInspection = findByInspection(this.coordset);
	
	var byIndicator = (this.letterIndicator === "O" ? 3 : 
		(this.letterIndicator === "P" ? 1 : 2));
	
	if((byInspection === byIndicator) || (this.letterIndicator === "X"))
	{ 
		//everything matches properly
		//or the wildcard indicator X was sent
		this.numberIndicator = byInspection;
		switch(this.numberIndicator)
		{
			case 1: this.textIndicator = "point"; break;
			case 2: this.textIndicator = "line"; break;
			case 3: this.textIndicator = "poly"; break;
			case 4: this.textIndicator = "circle"; break;
			//case 5: this.textIndicator = "square"; break;	
		}	
	}
	else
	{
		//console.log("error on feature type:");
		//console.log("coords: " + this.coordset.join(" "));
		//console.log("ind: " + this.letterIndicator);
	}

};
coordsetHandler.prototype.setOuters = function()
{

	for(var i = 0; i < this.coordset.length; i++)
	{
		var latlon = this.coordset[i].split(',');
		var lon = parseFloat(latlon[0]);
		var lat = parseFloat(latlon[1]);
		if(!this.highestLon) this.highestLon = lon;
		if(!this.lowestLon) this.lowestLon = lon;
		if(!this.highestLat) this.highestLat = lat;
		if(!this.lowestLat) this.lowestLat = lat;
		if(lon > this.highestLon) this.highestLon = lon;
		if(lon < this.lowestLon) this.lowestLon = lon;
		if(lat > this.highestLat) this.highestLat = lat;
		if(lat < this.lowestLat) this.lowestLat = lat;
	}
//deBugClog("animation", " inside setOuters " + this.highestLat + " " + this.lowestLat); 
	this.midLat = this.lowestLat + ((this.highestLat - this.lowestLat) / 2);
	this.midLon = this.lowestLon + ((this.highestLon - this.lowestLon) / 2);

};



//singleton for making timetrails kml
tt.animation = (function()
{
	//private
	var featureset = null; //holds the current array of feature objects
	
	var feature = null; //holds the current feature
	var coordset = null; //holds the current coordset of that feature
	var coordHandle = null; //holds the coordset handler
	var aSeq = []; //animation sequence
	var kml = []; //kml text
	
	//global config
	var optimumPointStills = 6;
	var frameRate = 5.0;
	var inverseFrameRate = 1/frameRate; 
	var speedSetting = 30;
	var speedSettingMSec = speedSetting * 1000; //in msecs
	
	//style related 
	var baseStyle = ""; 
	var styleBits = "";
	var featureType = "";
	var animationType = "";
	var iconType = "";
	var width = 1.0;
	var colorSeries = "000";	
	var colorSeriesBright = 0;	
	var fadeNumber = 0;
	var opacityInitialPercent = 100;	
	var opacityInitial = 0;
	var fill = true;
	var outline = false;
	
	//limit and box related
	var firstDate = null;
	var lastDate = null;
	
	//to calculate initial color and opacity values
	//example: initVals = [[0,255],[255,255],[255,255],[255,0]];
	var initVals = [];
	var nextVals = [];
	
	function setFeatures(incomingFeatures)
	{
		featureset = null;
		featureset = incomingFeatures;
	}
	
	function makeTimeTrail()
	{
		
		deBugClog("animation","top of makeTimeTrail");

		kml.length = 0; //clear any existing
		
		kml.push('<?xml version="1.0" encoding="UTF-8"?>');
		kml.push('<kml xmlns="http://www.opengis.net/kml/2.2">');
		kml.push('<Document>');	
		
		//get first and last date
		for(var t = 0; t <  featureset.features.length; t++)
		{
			
			//NOTE - need to compare first and start dates to 
			// tt.getcurrentstartdate and choose the narrowest to use here
			
			var early = isoToDate(featureset.features[t].Begin);
			if(!firstDate) firstDate = early;
			var late = isoToDate(featureset.features[t].End);
			if(!lastDate) lastDate = late;
			if(early.getTime() < firstDate.getTime()) firstDate = early;
			if(late.getTime() > lastDate.getTime()) lastDate = late;		
		}
//deBugClog("animation","firstDate:",firstDate,"lastDate:",lastDate);
		
		for(var x = 0; x <  featureset.features.length; x++)
		{		
			feature = featureset.features[x];
			decodeStyle();
			coordHandle = new coordsetHandler(feature.Coords, featureType);
			coordset = coordHandle.coordset;
//deBugClog("animation","coordHandler midLat " + coordHandle.midLat);	

			aSeq.length = 0; //reset animation sequence
//deBugClog("animation","featureType + animationType",featureType + animationType);
			switch(featureType + animationType)
			{
				case "Pn": createASeqPointNormal(); break;
				case "Pt": createASeqPointTrail(); break;
				case "Ln": createASeqLineNormal(); break;
				case "Lt": createASeqLineTrail(); break;
				case "On": createASeqPolyNormal(); break;	
				case "Ot": createASeqPolyNormal(); break; //no trail animation for polys yet
			}
				
			for(var y = 0; y < aSeq.length; y++)
			{
				
				//create kml for this placemark
				switch(featureType)
				{
					case "P": createKmlPoint(aSeq[y]); break;
					case "L": createKmlLine(aSeq[y]); break;
					case "O": createKmlPoly(aSeq[y]); break;
				}
			}	
		}
				
		for (var i in tt.cg.styleList)
		{
			kml.push(tt.cg.styleList[i]);
		}
		//clear styles;
		tt.cg.styleList.length = 0;
		
		//test add an overlay
		kml.push(addGroundOverlayKml());
		
		kml.push('</Document>');
		kml.push('</kml>');
		
deBugClog("animation","kml: " + kml.join(''));
//alert("kml:" + kml.join(''));		
		var doc = ge.parseKml(kml.join(''));
		ge.getFeatures().appendChild(doc);
		
	}
	
	
	function decodeStyle()
	{
		
		baseStyle = feature.Style;
		//read stylebits
		styleBits = feature.Style.split("_");
		featureType = styleBits[0];
		animationType = styleBits[1];
		iconType = styleBits[2];
		width = parseFloat(styleBits[3]) / 10;
		colorSeries = styleBits[4];
		colorSeriesBright = parseInt(styleBits[5]);	
		fadeNumber = parseFloat(styleBits[6]);
		opacityInitialPercent = parseFloat(styleBits[7]);
		//calculate additional
		opacityInitial = Math.floor((opacityInitialPercent * 255) / 100);
		//determine fill and out for poly
		fill = true;
		outline = false;
		if(featureType === "O" && iconType == "111")
		{
			fill = false;
			outline = true;
		}		

	}
	
	
	function createKmlPoint(aDef)
	{
		kml.push('<Placemark>');
		kml.push('<name>' + aDef.showName + '</name>');
		kml.push('<description>' + aDef.showDesc + '</description>');
		kml.push('<styleUrl>#' + aDef.styleName + '</styleUrl>');
		kml.push('<TimeSpan>');
		kml.push('<begin>' + aDef.begin + '</begin>');
		kml.push('<end>' + aDef.end + '</end>');
		kml.push('</TimeSpan>');
		kml.push('<Point>');
		kml.push('<coordinates>' + aDef.coords + '</coordinates>');
		kml.push('</Point>');
		kml.push('</Placemark>');
	}
	function createKmlLine(aDef)
	{
		kml.push('<Placemark>');
		kml.push('<name>' + aDef.showName + '</name>');
		kml.push('<description>' + aDef.showDesc + '</description>');
		kml.push('<styleUrl>#' + aDef.styleName + '</styleUrl>');
		kml.push('<TimeSpan>');
		kml.push('<begin>' + aDef.begin + '</begin>');
		kml.push('<end>' + aDef.end + '</end>');
		kml.push('</TimeSpan>');
		kml.push('<LineString>');
		kml.push('<coordinates>' + aDef.coords + '</coordinates>');
		kml.push('</LineString>');
		kml.push('</Placemark>');	
	}
	function createKmlPoly(aDef)
	{
		kml.push('<Placemark>');
		kml.push('<name>' + aDef.showName + '</name>');
		kml.push('<description>' + aDef.showDesc + '</description>');
		kml.push('<styleUrl>#' + aDef.styleName + '</styleUrl>');
		kml.push('<TimeSpan>');
		kml.push('<begin>' + aDef.begin + '</begin>');
		kml.push('<end>' + aDef.end + '</end>');
		kml.push('</TimeSpan>');
		kml.push('<MultiGeometry>');
		kml.push('<Polygon>');
		kml.push('<tessellate>1</tessellate>');
		kml.push('<outerBoundaryIs>');
		kml.push('<LinearRing>');
		kml.push('<coordinates>' + aDef.coords + '</coordinates>');
		kml.push('</LinearRing>');
		kml.push('</outerBoundaryIs>');
		kml.push('</Polygon>');
		kml.push('<Point>');
		kml.push('<coordinates>' + aDef.midLon + ',' + aDef.midLat + '</coordinates>');
		kml.push('</Point>');
		kml.push('</MultiGeometry>');
		kml.push('</Placemark>');
	}
	

	
	function createASeqPointNormal()
	{
		
		initVals[0] = [opacityInitial,opacityInitial];
		colorDef = eval("tt.c" + colorSeries + colorSeriesBright);
		initVals[1] = colorDef.vals[0];
		initVals[2] = colorDef.vals[1];
		initVals[3] = colorDef.vals[2];
		
		//create the style				
		var currStyle = new featureStyle(
				baseStyle,
				getColor(0,0),
				getScale(0,0),
				iconType,
				fill,
				outline, width);
		
		//add a single aDef object to aSeq
		aSeq.push({
				coords :  coordset.join(" "),
				styleName : baseStyle,
				color : getColor(0,0),
                scale : getScale(0,0),
                icon : iconType,
                begin : feature.Begin,
                end : feature.End,
                showName : feature.Name,
                showDesc : feature.Position,
                midLat : 0,
                midLon : 0});
	}
	
	function createASeqPointTrail()
	{
		//calculate initial opacity and color values	
		if(opacityInitialPercent < 50.0) //opacity starts low and goes higher
		{
			initVals[0] = [opacityInitial,255];
		}
		else //opacity starts high and goes lower
		{
			initVals[0] = [opacityInitial,0];
		}
		var colorDef = eval("tt.c" + colorSeries + colorSeriesBright);
		initVals[1] = colorDef.vals[0];
		initVals[2] = colorDef.vals[1];
		initVals[3] = colorDef.vals[2];
		
	
		var startDate = isoToDate(feature.Begin);
		var endDate = isoToDate(feature.End);
		var totalTimeSpan = endDate.getTime() - startDate.getTime();
		
		var animTimeSpan = lastDate.getTime() - firstDate.getTime();
		var animToRealTimeRatio = animTimeSpan / speedSettingMSec;
		var popTimeSpan = inverseFrameRate * animToRealTimeRatio * 1000;
		
		var maxNumberStills = Math.floor(totalTimeSpan / popTimeSpan);
		if(maxNumberStills > optimumPointStills)
		{
			maxNumberStills = optimumPointStills;
		}
		
//deBugClog("animation","totalTimeSpan",totalTimeSpan,"speedSettingMSec",speedSettingMSec,
//		"animTimeSpan",animTimeSpan,"animToRealTimeRatio",
//		animToRealTimeRatio,"popTimeSpan",popTimeSpan,"maxNumberStills",maxNumberStills);	

		var showName = "";
		var showDesc = feature.Position;	
		

		for(var j = 0; j < maxNumberStills; j++)
		{			
			//calculate standard times
			var beginTime = new Date(startDate.getTime() + (popTimeSpan * (j)));
			var endTime = new Date(startDate.getTime() + (popTimeSpan * (j + 1)));			

			if(j === (maxNumberStills - 1))
			{
				//on last thru use totalspan for end
				beginTime = new Date(startDate.getTime() + (popTimeSpan * (j)));
				endTime = new Date(startDate.getTime() + totalTimeSpan);
				
				var showName = feature.Name;
				var showDesc = feature.Position;
			}
			else //not last
			{
				//use popspan instead
				beginTime = new Date(startDate.getTime() + (popTimeSpan * (j)));
				endTime = new Date(startDate.getTime() + (popTimeSpan * (j + 1)));				
			}
//deBugClog("animation",beginTime,endTime,iconType);
			//create the style				
			var currStyle = new featureStyle(
					baseStyle + "_" + j,
					getColor(j,maxNumberStills),
					getScale(j,maxNumberStills),
					iconType,
					fill,
					outline, width);
			
			
			//add a styleDef object to styleSet
			aSeq.push({
					coords :  coordset.join(" "),
					styleName : baseStyle + "_" + j,
					color : getColor(j,maxNumberStills),
	                scale : getScale(j,maxNumberStills),
	                icon : iconType,
	                begin : dateToISO(beginTime),
	                end : dateToISO(endTime),
	                showName : showName,
	                showDesc : feature.Position,
	                midLat : 0,
	                midLon : 0});			
		}		
		
	}
	
	
	function createASeqLineNormal()
	{
		
		initVals[0] = [opacityInitial,opacityInitial];
		colorDef = eval("tt.c" + colorSeries + colorSeriesBright);
		initVals[1] = colorDef.vals[0];
		initVals[2] = colorDef.vals[1];
		initVals[3] = colorDef.vals[2];
		
		//create the style				
		var currStyle = new featureStyle(
				baseStyle,
				getColor(0,0),
				getScale(0,0),
				iconType,
				fill,
				outline, width);
		
		//add a single styleDef object to aSeq
		aSeq.push({
				coords :  coordset.join(" "),
				styleName : baseStyle,
				color : getColor(0,0),
                scale : getScale(0,0),
                icon : iconType,
                begin : feature.Begin,
                end : feature.End,
                showName : "",
                showDesc : feature.Position,
                midLat : 0,
                midLon : 0});
		
	}
	
	function createASeqLineTrail()
	{
		//derive terms
		//set number of cuts to make on a line	
		
		//this feature takes up what percent of total features
		
		var targetRate = 8; // changes per second
		var targetSpeed = 3;  // total seconds for GE plugin animation - fastest setting	
		
		//set dates
		var startDate = isoToDate(feature.Begin);
		var endDate = isoToDate(feature.End);
		
		//var maximumCuts = coordset.length - 1; // maximum possible for this line			
		//var optimumCuts = Math.floor(targetRate * targetSpeed * percentTotalTime);
		//var numberCuts = (optimumCuts > maximumCuts ? maximumCuts : optimumCuts);
		
		var featureTimeSpan = endDate.getTime() - startDate.getTime();
		var animTimeSpan = lastDate.getTime() - firstDate.getTime();
		
		var featureToTotalTimeRatio = featureTimeSpan / animTimeSpan;
		var featureDurationMSec = featureToTotalTimeRatio * speedSettingMSec;

//deBugClog("animation", "name: " + feature.Name + "	featureTimeSpan: " + featureTimeSpan + " animTimeSpan: " + animTimeSpan);	

		
		var recommendedStills = Math.floor(frameRate * (featureDurationMSec / 1000));

//deBugClog("animation", "name: " + feature.Name + "	featureDurationMSec: " + featureDurationMSec + " recommendedStills: " + recommendedStills);	
		
		var numberCoordsToStart = coordset.length;
		var minimumCoordsNeeded = recommendedStills - 1;
				
		var coordsToAdd = (numberCoordsToStart > minimumCoordsNeeded ? 0 : minimumCoordsNeeded - numberCoordsToStart);		
		
		var newcoords = [];
		//NOTES
		//floor of (numPoints + (ratio of numPoints/numPointsToAdd)) according to numPointsToAdd
		//make a matrix according to numPoints

//deBugClog("animation", "name: " + feature.Name + "  coordsToAdd: " + coordsToAdd + "	numberCoordsToStart: " + numberCoordsToStart);		
		if(coordsToAdd > 0)
		{
			var coordsToAddArray = [];
			var addRatio = (numberCoordsToStart - 1)/coordsToAdd;
			var addAboveLast = 1;
			//loop thru each coord needed to add - make a table showing the num to add after each exitsing point
			for(var i = 0; i <= coordsToAdd; i++)
			{
				var addAbove = Math.floor(1 + (i * addRatio));
//deBugClog("animation", "name: " + feature.Name + "	addAbove (floor): " + addAbove + " i: " + i + " calced: " + (1 + (i * addRatio)));				
				
				if(addAbove != addAboveLast) //create a new ToAdd for the previous series
				{
					coordsToAddArray.push([addAboveLast,i]);
//deBugClog("animation", "name: " + feature.Name + "	addAboveLast: " + addAboveLast + " i: " + i);	
					addAboveLast = addAbove;
				} 
			}
			
			
			//loop thru each existing point - add any needed coords after that point
			for(var i = 0; i < numberCoordsToStart - 1; i++)
			{
				var arrCoordsToAdd = coordsToAddArray[i];
				var numCoordsToAdd = arrCoordsToAdd[1];

//deBugClog("animation", "name: " + feature.Name + "	incrCoordLon: " + incrCoordLon + " incrCoordLat: " + incrCoordLat);	
				
				//determine how many new coords to add between each coord in coordset
				thisCoord = coordset[i];
				lastCoord = coordset[i+1];
				thisCoordLon = parseFloat(thisCoord.split(",")[0].trim());
				thisCoordLat = parseFloat(thisCoord.split(",")[1].trim());
				lastCoordLon = parseFloat(lastCoord.split(",")[0].trim());
				lastCoordLat = parseFloat(lastCoord.split(",")[1].trim());

//deBugClog("animation", "name: " + feature.Name + "	addAboveLast: " + addAboveLast + " i: " + i);

				incrCoordLon = (lastCoordLon - thisCoordLon) / (numCoordsToAdd + 1);
				incrCoordLat = (lastCoordLat - thisCoordLat) / (numCoordsToAdd + 1);
				
//deBugClog("animation", "name: " + feature.Name + "	incrCoordLon: " + incrCoordLon + " incrCoordLat: " + incrCoordLat);			
				for(var j = 0; j < numCoordsToAdd; j++)
				{
					var newCoordLon = thisCoordLon + incrCoordLon * (j+1);
					var newCoordLat = thisCoordLat + incrCoordLat * (j+1);
//deBugClog("animation", "name: " + feature.Name + "	newCoordLon: " + newCoordLon + " newCoordLat: " + newCoordLat);						
					newcoords.push(newCoordLon + ',' + newCoordLat);
					
				}
				
			}
			
		} else {
			
			newcoords = coordset;
		}
		
		
		recommendedStills = (recommendedStills <= 0 ? 1 : recommendedStills);
			
		var numberCoords = numberCoordsToStart + coordsToAdd;
		
		var coordsPerStill  = numberCoords / recommendedStills;
		
		var animToRealTimeRatio = animTimeSpan / speedSettingMSec;
		var stillTimeSpan = inverseFrameRate * animToRealTimeRatio * 1000;
		
		var showName = "";
		var showDesc = feature.Position;
		
		//cut up coords
		//loop to create each cut in line
		var beginIndex = 0;								
		for(var i = 0; i < recommendedStills; i++)
		{
			//determine the coordset index to use for this cut
			var endIndex = Math.floor((newcoords.length / (recommendedStills)) * (i+1));
		
			var cutCoords = [];
			for(j = beginIndex; j < endIndex; j++)
			{			
				cutCoords.push(newcoords[j]);		
			}

			//calculate initial opacity and color values	
			if(opacityInitialPercent < 50.0) //opacity starts low and goes higher
			{
				initVals[0] = [opacityInitial,255];
			}
			else //opacity starts high and goes lower
			{
				initVals[0] = [opacityInitial,0];
			}
			var colorDef = eval("tt.c" + colorSeries + colorSeriesBright);
			initVals[1] = colorDef.vals[0];
			initVals[2] = colorDef.vals[1];
			initVals[3] = colorDef.vals[2];
			if(fadeNumber === 0) fadeNumber = 1;  //need at least one style

			
			//go through each fade		
			for(var k = 0; k < fadeNumber; k++)
			{			
	//deBugClog("animation",k,feature.Begin,startDate,stillTimeSpan);
				
				var fadeFactor = 6 * k;
				
				//calculate standard times	
				var beginTime = new Date(startDate.getTime() + (stillTimeSpan * i) + (stillTimeSpan * k));
				var endTime = new Date(startDate.getTime() + (stillTimeSpan * (i + 1)) + (stillTimeSpan * (k + 1) + (stillTimeSpan * fadeFactor)));
				var currStyle = new featureStyle(
						baseStyle + "_" + k + "_" + fadeNumber,
						getColor(k,fadeNumber),
						getScale(k,fadeNumber),
						iconType,
						fill,
						outline, width);
				
				//add a styleDef object to aSeq
				aSeq.push({
						coords :  cutCoords.join(" "),
						styleName : baseStyle + "_" + k + "_" + fadeNumber,
						color : getColor(k,fadeNumber),
		                scale : getScale(k,fadeNumber),
		                icon : iconType,
		                begin : dateToISO(beginTime),
		                end : dateToISO(endTime),
		                showName : showName,
		                showDesc : feature.Position,
		                midLat : 0,
		                midLon : 0});			
			}
			
			beginIndex = endIndex - 1;				
		}				
	}
	
	
	function createASeqPolyNormal()
	{
		initVals[0] = [opacityInitial,opacityInitial];
		colorDef = eval("tt.c" + colorSeries + colorSeriesBright);
		initVals[1] = colorDef.vals[0];
		initVals[2] = colorDef.vals[1];
		initVals[3] = colorDef.vals[2];
		
		//create the style				
		var currStyle = new featureStyle(
				baseStyle,
				getColor(0,0),
				"1.8",
				"INV",
				fill,
				outline, width);
		
//deBugClog("animation", " here before ");
//deBugClog("animation", " test lon " + coordHandle.midLon);
		
		//add a single styleDef object to aSeq
		aSeq.push({
				coords :  coordset.join(" "),
				styleName : baseStyle,
				color : getColor(0,0),
                scale : getScale(0,0),
                icon : iconType,
                begin : feature.Begin,
                end : feature.End,
                showName : feature.Name,
                //showDesc : feature.Position,
                //NOTE: only store the position key in Desc
                showDesc : feature.Position, 
                midLat : coordHandle.midLat,
                midLon : coordHandle.midLon});
		
//deBugClog("animation", " check mids " + coordHandle.midLat + "  midlon " + coordHandle.midLon);
	}


	
	function getColor(idx, totalStills)
	{	
		//calculate next values	
		for(var i = 0; i < 4; i++)
		{
			var begin = initVals[i][0];
			var end = initVals[i][1];
			var look = (begin < end ? 1 : -1);
			var jump = 0;
			if(totalStills > 0)
				jump = look * Math.floor(Math.abs(end - begin) / totalStills);	
			nextVals[i] = begin + (jump * idx);
		}	
	
		// tr bb gg rr	- translate to hex
		var trComp = nextVals[0].toString(16);
		var bbComp = nextVals[1].toString(16);
		var ggComp = nextVals[2].toString(16);
		var rrComp = nextVals[3].toString(16);
		// make hex color string	
		var color = ""
			+ (trComp.length == 1 ? "0" + trComp : trComp)
			+ (bbComp.length == 1 ? "0" + bbComp : bbComp)
			+ (ggComp.length == 1 ? "0" + ggComp : ggComp)
			+ (rrComp.length == 1 ? "0" + rrComp : rrComp);
	
	
		//alert(color);
		return color;
	}
	
	function getScale(idx, totalStills)
	{
		var scaleMax = 8.0;
		var scaleMin = 1.0;
		var scaleStep = (scaleMax - scaleMin)/totalStills;
		var scale = scaleMin
		if(totalStills > 1)
		{
			scale = scaleMax - (scaleStep * idx);
//deBugClog("animation","idx",idx,"scaleStep",scaleStep,"totalStills",totalStills,"scale",scale);				
		}
		
		//alert(scale);
		return Math.round(scale*100)/100;
	}
	
	function toHex(dec)
	{
		return parseInt(dec.toString(16),16);
	}
	
	//public
	return{
		SetFeatures : function(featureset) { setFeatures(featureset); },
		MakeTimeTrail : function() { makeTimeTrail(); }	
	};	
})();



/*
 * 
 * Animation Helper functions and constants
 * 
 * 
 */

//color series defs
tt.c0011 = {name : "blue_001_StartBrite", vals : [[255,255],[255,255],[200,0]]};
tt.c0010 = {name : "blue_001_EndBrite", vals : [[255,255],[255,255],[0,200]]};
tt.c0021 = {name : "green_002_StartBrite", vals : [[200,0],[255,255],[200,0]]};
tt.c0020 = {name : "green_002_EndBrite", vals : [[0,200],[255,255],[0,200]]};
tt.c0031 = {name : "red_003_StartBrite", vals : [[200,0],[200,0],[255,255]]};
tt.c0030 = {name : "red_003_EndBrite", vals : [[0,200],[0,200],[255,255]]};
tt.c0041 = {name : "yellow-red_004_StartBrite", vals : [[200,0],[255,255],[255,255]]};
tt.c0040 = {name : "yellow-red_004_EndBrite", vals : [[0,200],[255,255],[255,255]]};
tt.c0051 = {name : "trueblue_005_StartBrite", vals : [[255,255],[50,0],[200,0]]};
tt.c0050 = {name : "trueblue_005_EndBrite", vals : [[255,255],[0,50],[0,200]]};
tt.c0061 = {name : "red-blue_006_StartBrite", vals : [[255,255],[200,0],[255,255]]};
tt.c0060 = {name : "red-blue_006_EndBrite", vals : [[255,255],[0,200],[255,255]]};
tt.c0071 = {name : "brown_007_StartBrite", vals : [[155,0],[203,87],[255,174]]};
tt.c0070 = {name : "brown_007_EndBrite", vals : [[0,155],[87,203],[174,255]]};
tt.c0081 = {name : "orange_008_StartBrite", vals : [[187,23],[222,92],[255,255]]};
tt.c0080 = {name : "orange_008_EndBrite", vals : [[23,187],[92,222],[255,255]]};




var featureStyle = function(style, color, scale, iconType, fill, outline, width)
{
	this.styleString = style;
	this.color = color;
	this.iconScale = scale;
	this.icon = tt.cg.mapimg + iconType.toLowerCase() + ".png";
	this.fill = (fill ? "1" : "0");
	this.outline = (outline ? "1" : "0");
	this.width = width;
	this.makeStyle();
};
featureStyle.prototype.makeStyle = function()
{
	if(!tt.cg.styleList[this.styleString]) //then add it
	{
		styleKML = [];
		
		styleKML.push('');
	
		styleKML.push('<Style xmlns="http://www.opengis.net/kml/2.2" id="' + this.styleString + '">');
		
		styleKML.push('<IconStyle id="">');
		styleKML.push('<Icon>');
		styleKML.push('<href>' + this.icon + '</href>');
		styleKML.push('</Icon>');
		styleKML.push('<color>'+ this.color +'</color>');
		styleKML.push('<scale>'+ this.iconScale +'</scale>');
		styleKML.push('<hotSpot x="0.5" y="0.5" xunits="fraction" yunits="fraction" />');	
		styleKML.push('</IconStyle>');
		
		styleKML.push('<LabelStyle id="">');
		styleKML.push('<color>'+ makeFullOpacity(this.color) +'</color>');
		styleKML.push('<scale>'+ this.width +'</scale>');
		styleKML.push('<scale>1.0</scale>');
		styleKML.push('<outline>1</outline>');
		styleKML.push('</LabelStyle>');
		
		styleKML.push('<LineStyle id="">');
		styleKML.push('<color>'+ this.color +'</color>');
		styleKML.push('<width>' + this.width + '</width>');
		styleKML.push('</LineStyle>');
		
		styleKML.push('<PolyStyle>');
		styleKML.push('<color>'+ this.color +'</color>');
		styleKML.push('<outline>' + this.outline + '</outline>');
		styleKML.push('<fill>' + this.fill + '</fill>');
		styleKML.push('</PolyStyle>');
		
		
		
		styleKML.push('</Style>');
		
		tt.cg.styleList[this.styleString] = styleKML.join("");
	
	}

};

function makeFullOpacity(clr)
{
	return 'ff' + clr.substring(2);

}


function clearFeatures()
{
//NOTE - this does not seem to work right
	//clear all features
	var geNodes = ge.getFeatures().getChildNodes();
	var count = geNodes.getLength(); 

	for(i = 0; i < count; i++)
	{
		//alert("in " + count);
		ge.getFeatures().removeChild(geNodes.item(i));
	}
	
//alert("cleared " + count);
}















