function getTicks() {
	var t = new Date();
	return t.getTime();
}


var charHeight = new Array();

charHeight["green"] = {"default": 35};
charHeight["red"] = {"default": 28};

var charWidth = new Array();

charWidth["green"] = {
  "I": 25,
  "1": 21, 
  "colon": 15,
  "apostrophe": 15,
  "exclamation":15,
  "space": 15,
  "comma": 20, 
  "period": 15,
  "double-quote": 25,
  "default": 35};
  
charWidth["red35"] = {
  "I": 20,
  "1": 21, 
  "colon": 12,
  "apostrophe": 12,
  "exclamation":12,
  "space": 16,
  "comma": 20, 
  "period": 12,
  "double-quote": 20,
  "default": 35};
  
  charWidth["red"] = {
  "I": 20,
  "1": 28, 
  "colon": 12,
  "apostrophe": 4,
  "exclamation":12,
  "space": 16,
  "comma": 16, 
  "period": 12,
  "double-quote": 20,
  "default": 28};

function getCharWidth (font, c) {
	var charName = charToName(c)
	var width = charWidth[font][charName];
	// alert("char width:"+font+", "+c+"="+width);
	if (!width) {
		width = charWidth[font]["default"];
	}
	if (!width) {
		alert("Can't determine width for "+font+", "+c);
		return;
	} else {
		return width;
	}
}

var charNameMap = {
	" ": "space",
	"?": "question",
	"'": "apostrophe",
	",": "comma",
	"-": "minus",
	".": "period",
	":": "colon",
	"+": "plus",
	"%": "percent",
	'"': "double-quote",
	"!": "exclamation"};

var debug = "";	

	
function charToName(c) {
	// if (!c) {alert("not c!"+debug);}
	var cup = c.toUpperCase();
	if ((cup >= "A" && cup <= "Z")  || (cup >= "0" && cup <= "9")) {
		return cup;
	}
	var name = charNameMap[c];
	if (!name) {
		return "question";
	}
	
	return name;
}

function charToImage(font, c) {
	var name = charToName(c);
	
	if (font == "green") {
		return "/i/digits/green/"+name+".jpg";
	} else if (font == "red") {		
		return "/i/digits/red2/"+name+".jpg";
	}
}

function charImage(font,c) {
	return "<img src='"+charToImage(font, c)+"'>";
}

var dm = {};
/*
id = id of the object that should be scrolled
rate = time in milliseconds between moves
jump = size in pixels of the move
*/

function dm_init(id, rate, jump, initialLeft) {
	if (dm[id]) {
		alert("sorry, this animation is already running.");
		return null;
	}
	var ticker = Ticker[id];
	
	animation = new Object();
	animation.id = id;
	animation.initialLeft = initialLeft;
	animation.rate = ticker.requestedRate;
	animation.state = 0;
	animation.jump = jump;
	animation.timer = null;
	dm[id] = animation;	
	return animation;
}

function dm_start(id) {
	var anim = dm[id];
	el = document.getElementById(id);
	el.style.left = anim.initialLeft+"px";
	anim.state = 1;
	dm_animate(id);
}

function dm_animate(id) {
	var anim = dm[id];
	if (anim && anim.state) {
		var display = document.getElementById(anim.id);
		// alert("here: "+display.style.left);
		var left = display.offsetLeft;
		
		
		var jump = Ticker[id].currentJumpSize;
		
		display.style.left = (left - jump) + "px";
		
		
		updateRate2(id);
		
		// Calculate the jump...
		
		
		
		
		
		if (display.offsetWidth + left <= 0) {	
			dm_start(id);
		} else {
			Ticker[id].startFrameTime = getTicks();
			
			// get timer time
			var t = Ticker[id].currentTimerTime;
			
			
			anim.timer = setTimeout(function() {dm_animate(id);}, t);
		}
	}
}

function start(id) {
	if (dm_init(id, 10, 1, 700)) {
		dm_start(id);
	}
}
function stop(id) {
	var anim = dm[id];
	
	if (anim) {
		anim.state = 0;		
		if (anim.timer) {
			clearTimeout(anim.timer);
		}
		dm[id] = null;
		
	}
}
function restart(id) {
	stop(id);
	Ticker[id].speed=0;
	resetRate(id);	
	start(id);
}
function faster(id) {
	// We now do pixels per second, so we can just fiddle with the rate
	// simply.
	increaseSpeed(id);
	return;
	
	
	Ticker[id].requestedRate += Ticker[id].speedAdjustment;
	
	
}
function slower(id) {
	decreaseSpeed(id);
	return

	if (Ticker[id].requestedRate == 0) {
		return;
	}
	
	if ((Ticker[id].requestedRate - Ticker[id].speedAdjustment) > 0) {	
		Ticker[id].requestedRate -= Ticker[id].speedAdjustment;
	}
	
}
function pause(id) {
	dm[id].state = 0;
	Ticker[id].state = 'paused';
} 
function play(id) {
	Ticker[id].state = 'playing';
	resetRate(id);
	dm[id].state = 1;
	dm_animate(id);
}

var Ticker = {};

// use this after a pause or when just starting.
function resetRate(id) {
	var ticker = document.getElementById(id);
	var currentLeft = ticker.offsetLeft;
	var currentTick = getTicks();
	Ticker[id].previousLeft = currentLeft;
	Ticker[id].previousTick = currentTick;
		
	Ticker[id].startFrameTime = getTicks();

	Ticker[id].frameCount = 1;
}


/*

if timer time > requested, decrease by increment.
if timer time = requested, increase pixel move size by 1.

*/
function increaseSpeed (id) {
	if (Ticker[id].currentTimerTime > Ticker[id].requestedTimerTime) {
		Ticker[id].currentTimerTime -= Ticker[id].timerIncrement;
		if (Ticker[id].currentTimerTime < Ticker[id].requestedTimerTime) {
			Ticker[id].currentTimerTIme = Ticker[id].requestedTimerTime;
		}
	} else {
		Ticker[id].currentJumpSize++;
	}
	Ticker[id].speed++;
	
}

/*
if pixel move size > 1, reduce by 1.
if pixel move size == 1, increase timer time by increment.
*/
function decreaseSpeed (id) {
	if (Ticker[id].currentJumpSize > 2) {
		Ticker[id].currentJumpSize--;
	} else {
		Ticker[id].currentTimerTime += Ticker[id].timerIncrement;
	}
	Ticker[id].speed--;
}

function updateRate2(id) {
	var val = null;
	
	var debug = '';
	if (!Ticker[id].frameCount) {
		resetRate(id);		
	} else {
		calcStats(id);
	}
	
	if (Ticker[id].showGauge) {
		var gauge = document.getElementById("gauge");
		// gauge.innerHTML = formatNumber(Ticker[id].currentTimerTime, 2, 8)+":"+formatNumber(Ticker[id].currentJumpSize, 0, 4);
		gauge.innerHTML = formatNumber(Ticker[id].currentRate, 1, 8)+"px/sec";
	}
}

function calcStats(id) {
	var ticker = document.getElementById(id);
	var currentLeft = ticker.offsetLeft;
	var currentTick = getTicks();
		
	// Measurements of current behavior.
	var moved = Ticker[id].previousLeft - currentLeft;
	var ticks = currentTick - Ticker[id].previousTick;
	
	var currentRate = 1000*moved/ticks;
		
	Ticker[id].frameCount++;
	
	Ticker[id].frameTime = currentTick - Ticker[id].startFrameTime;	
	Ticker[id].pixelsMoved += moved;
	Ticker[id].elapsedTime += ticks;
	
	Ticker[id].pixelRate = currentRate;
	Ticker[id].previousLeft = currentLeft;
	Ticker[id].previousTick = currentTick;
		
	Ticker[id].currentTimePerFrame = ticks;
	Ticker[id].totalTime += ticks;

	var realTimeElapsed = Ticker[id].frameTime / 1000;
	var renderTime = realTimeElapsed - Ticker[id].currentTimerTime/1000;
	Ticker[id].avgRenderTime = renderTime / Ticker[id].frameCount;
	
	var averageRate = 1000*Ticker[id].pixelsMoved / Ticker[id].elapsedTime;
	
	Ticker[id].currentRate = currentRate;
	Ticker[id].averageRate = averageRate;
	
	// Adjust thinkgs to reach ideal frame rate when we are at speed 0.
	if (Ticker[id].speed == 0) {
		var requiredJumpSize = (renderTime + Ticker[id].requestedTimerTime/1000) * Ticker[id].requestedRate;
		Ticker[id].currentJumpSize = Math.round(requiredJumpSize);
		Ticker[id].currentTimerTime = Ticker[id].requestedTimerTime;
	}
}

function updateRate(id) {
	var val = null;
	var ticker = document.getElementById(id);
	var debug = '';
	if (!Ticker[id].frameCount) {
		resetRate(id);
		
		val = 'n/a';
	} else {
		var currentLeft = ticker.offsetLeft;
		var currentTick = getTicks();
		
		// Measurements of current behavior.
		var moved = Ticker[id].previousLeft - currentLeft;
		var ticks = currentTick - Ticker[id].previousTick;
		
		
		// See what the current speed settings are.
		
		
		


		var currentRate = 1000*moved/ticks;
		
		Ticker[id].frameCount++;

		// Calculations and tracking.
		Ticker[id].frameTime = currentTick - Ticker[id].startFrameTime;	
		Ticker[id].pixelsMoved += moved;
		Ticker[id].elapsedTime += ticks;
		
		
		// Save some of this for next iteration.
		Ticker[id].pixelRate = currentRate;
		Ticker[id].previousLeft = currentLeft;
		Ticker[id].previousTick = currentTick;
		
		Ticker[id].currentTimePerFrame = ticks;
		Ticker[id].totalTime += ticks;

	
		// Get next jump size.
		// var currentJumpSize = Ticker[id].requestedRate * Ticker[id].frameTime;
		var currentJumpSize = Ticker[id].currentJumpSize;
		
		
		
		// var realPixelsMoved = Ticker[id].currentPixelMoveSize;
		
		// convert to seconds.
		var realTimeElapsed = Ticker[id].frameTime / 1000;
		var renderTime = realTimeElapsed - Ticker[id].currentTimerTime/1000;
		Ticker[id].avgRenderTime = renderTime / Ticker[id].frameCount;
		
		// var requiredJumpSize = Ticker[id].requestedRate * realTimeElapsed;
		
		var realRate = currentJumpSize / realTimeElapsed;
		
		
		// The jump size we should use for the current system behavior (renderTime)
		// and the requested rate (may have been adjusted since it was started)
		// and the ideal timer time (requested timer time).
		var requiredJumpSize = (renderTime + Ticker[id].requestedTimerTime/1000) * Ticker[id].requestedRate;
		
		var diff = requiredJumpSize - currentJumpSize;
		
		
		var recommendedJumpSize = Math.round(requiredJumpSize);
		if (diff > 1) {
			recommendedJumpSize++;
		} else if (diff < -1) {
			recommendedJumpSize--;
		}
		
		var newTimerTime;
		
		
		// Adjustments to the timer time.
		if (recommendedJumpSize < 1) {
			recommendedJumpSize = 1;
			var frameRenderTime = realTimeElapsed - Ticker[id].currentTimerTime/1000;
			var newTimerTime = recommendedJumpSize/Ticker[id].requestedRate-frameRenderTime;			
			Ticker[id].currentTimerTime = newTimerTime*1000;

			debug = 'rt:'+formatNumber(frameRenderTime,2,8)+", timer:"+
			        formatNumber(Ticker[id].currentTimerTime,2,8)+
			        ", rate:"+formatNumber(Ticker[id].requestedRate,2,8)+
			        ", new:"+formatNumber(Ticker[id].currentTimerTIme,2,8);
		} else {
			newTimerTime = Ticker[id].requestedTimerTime;
			Ticker[id].currentTimerTime = Ticker[id].requestedTimerTime;
		}
		
	 	Ticker[id].currentJumpSize = recommendedJumpSize;
		
		
		var averageRate = 1000*Ticker[id].pixelsMoved / Ticker[id].elapsedTime;
		
		val = "rate (current):"+formatNumber(currentRate, 2, 8) +", rate (avg):"+ formatNumber(averageRate,2, 8);
		
		val += ", elapsed (real)"+formatNumber(realTimeElapsed, 2, 8);
		
		val += ", t (new)"+formatNumber(newTimerTime, 2, 8);
		
		// val = moved+", "+currentTick;
		
	}
	
	if (Ticker[id].showGauge) {
		var gauge = document.getElementById("gauge");
		gauge.innerHTML = "["+formatNumber(Ticker[id].requestedRate, 2 ,8)+" px/sec]";
	}
	
	//var rate = document.getElementById("rate");
	
	//val += ", rate (requested):"+formatNumber(Ticker[id].requestedRate, 2 ,8)+",  time (frame)"+formatNumber(Ticker[id].frameTime, 2, 8)+", jump (current) "+formatNumber(Ticker[id].currentJumpSize,2,8)+", (req)"+formatNumber(requiredJumpSize,2,8)+", (rec)"+formatNumber(recommendedJumpSize,2,8)+"<br>"+debug;
	
	//rate.innerHTML = ''+val;
	
	
	/*
	YUI.use('node', function(Y) {
		var rateSelector = '#'+id+' #rate';
		var rate = Y.one(rateSelector);
		if (!rate) {
			alert("can't find rate box! "+rateSelector);
		} else {
			rate.set('innerHTML', val);
		}
	});
	*/
	
}

function formatNumber (num, decimalPlaces, width, spacer) {
	if (!spacer) {spacer=" "};
	if (!num) {
		return (new Array(width).join(spacer));
	}

	var fnum = ''+num.toFixed(decimalPlaces);
	
	if (fnum.length < width) {
		fnum2 =  (new Array(width - fnum.length).join(spacer))+fnum;
	} else {
		fnum2 = fnum;
	}
	// alert(fnum+", "+fnum.length+" < "+width+"="+fnum2);
	return fnum2;
}

function buildTicker(id, messages, messageSpacer, rate, timerTime, showGauge) {
	
	var totalWidth = 0;
	var dm = document.getElementById(id);
	
	Ticker[id] = {};
	Ticker[id].messages = messages;
	Ticker[id].messageSpacer = messageSpacer;
	Ticker[id].pixelRate = 0;
	Ticker[id].pixelsMoved = 0;
	Ticker[id].elapsedTime = 0;
	Ticker[id].previousLeft = 0;
	Ticker[id].previousTick = 0;
	Ticker[id].requestedRate = rate; // in pixels per second
	Ticker[id].currentRate = rate;
	Ticker[id].requestedTimerTime = timerTime; // in milliseconds.
	Ticker[id].currentTimerTime = timerTime;
	Ticker[id].state = 'new';
	Ticker[id].speedAdjustment = 5;
	Ticker[id].timerIncrement = 25;
	
	Ticker[id].showGauge = showGauge;
	
	// Runtime adjustable (need to be reset on a restart).
	Ticker[id].speed = 0;

	// Set initial frame pixel rate.
	// rate (pixels/sec) * initial timer time (millisec)/1000
	// e.g. 400px/sec * (20ms/1000) = 8/2 = 4.
	Ticker[id].currentJumpSize = Ticker[id].currentRate * (Ticker[id].currentTimerTime / 1000);
	
	
	var content = "";

	for (j in messages) {
		var font = messages[j].font;
		
		var message = messages[j].text + messageSpacer;
		
		for (i = 0; i < message.length; i++) {
			var c = message.charAt(i);
			// debug=j+", "+i;
		
			content += charImage(font, c);
			
			totalWidth += getCharWidth(font, c);
		}
		
	}
	

	dm.style.width = totalWidth+"px";
	dm.innerHTML = content;
	
	updateRate2(id);
}


