/*	Author: Benjamin Ranck
*	E-mail: b ++ A-T ++ benjaminranck ++ D-O-T ++ com
*	http://www.benjaminranck.com March 2004
*
*	Some demonstration of javascript's Document Object Model, DOM.
*	Pretty memory intensive, but a fun way to show the power of DOM,
*	depending on how much RAM and CPU power you have well depend on the
*	level of resolution. Lower power boxes use rand4KColor and higher
*	interpolation, faster boxes can use the randColor and much lower
*	interpol values
*
*	A lot of the stuff in here is given as examples and for my own
*	experimentation.
*
*	You'll notice a lot of things are referenced by id instead of
*	passed directly as a reference to the array, although slower it
*	increases flexibility, although in the future it may be sensible
*	to have both types of functions.
*
*	I've also found that referencing by id solves scoping problems
*	when doing any kind of animation using setTimout and setInterval.
*	Basically in Javascript, there is no scope... other then the objects
*	which hang on to the document
*
*	The slashed-out document.writes and appendBody are debug statements, 
*	very useful, but they do slow the whole process down.
*
*	This is easily extendable to other html elements.
*/

document.write("Pure JavaScript, no graphics, just code :-)");

var newPara = document.createElement("p"); //creates a paragraph object
newText = document.createTextNode("BoxesAlive By Benjamin Ranck ");
newPara.appendChild(newText);
newLink = document.createElement("a"); //create an anchor object
newLink.setAttribute('href', 'http://www.benjaminranck.com'); //sets the objects html attribute
newLinkText = document.createTextNode("http://www.benjaminranck.com");
newLink.appendChild(newLinkText);


bodyRef = document.getElementsByTagName("body").item(0);
bodyRef.appendChild(newPara);
bodyRef.appendChild(newLink);
bodyRef.appendChild(document.createElement("br") );
bodyRef.appendChild(document.createTextNode(" 2004 ") );

/*
var newDiv = document.createElement("div");

newDiv.style.backgroundColor="Pink";
newDiv.style.width="200px";
newDiv.style.height="200px";

bodyRef.appendChild(newDiv);
*/

/*Globals -- (those you can and should make use of) */
boxArray = null; //this is a 2d array of the box objects
boxArrayX = null; // this is the length of the array in the X direction, ie boxArray[x.length][]
boxArrayY = null; // this is the lenght of the array in the Y direction, ie boxArray[][y.length]

/* Globals -- ones you shouldn't really touch */
hexArray = new Array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f');


function decToHex(dec) {
//appendBody(document.createTextNode("dec"+dec+" // "));
	var res = '';
	while(dec > 0) {
		var modDec = dec % 16;
		dec = (dec-modDec)/16;
		res = hexArray[modDec] + res;
//document.write("dec now is: "+dec+"  ");
	}
//appendBody(document.createTextNode("hexres"+res+" xx "));
	return res;
}

function randColor() {
	var res = "#";
	for(k=0; k<6; k++) {
		var num = Math.round(Math.random()*15);
		res += hexArray[num];
	}
	return res;
}

function rand4KColor() {
	var res = "#";
	for(k=0; k<3; k++) {
		var num = Math.round(Math.random()*15);
		res += hexArray[num]+hexArray[num];
	}
	return res;
}

function grayCodeGradient(r, g, b, id, val) {
/*follows gray code to get a smooth gradient, ie, in binary 0,1,3,2,6,7,5,4,0...
However I a start at the 7 mark ie 111 as it's easier to get to from a 
randomly generated color, val is the amount it increase by*/
	var res = "";
	var box = document.getElementById(id);
	var colorPos = parseInt(box.title);
	val = parseInt(val);
	r= parseInt(r);
	g= parseInt(g);
//appendBody(document.createTextNode("beforeParse: "+b+" val "+val+"  "));
	b= parseInt(b);
//appendBody(document.createTextNode("title"+colorPos+" :|blue "+b+" "));
//document.write("r"+r+"g"+g+"b"+b);
	switch(colorPos) {
		case 0:	/*this is priming the colors for the gradient from wherever
				they where randomly generated from, to 111*/
				if(r<255){r+=val;}
				if(g<255){g+=val;}
				if(b<255){b+=val;}
				if(r * g * b >= 255 * 255 * 255) {colorPos = 1;}
				break;
		case 1:	if(g <= 255){g= g-val;}
				if(g <= 40){colorPos++;} //we don't go to zero because otherwise there's too much black
				break;
		case 2:	if(b <= 255){b= b-val;}
				if(b <= 40){colorPos++;}
				break;
		case 3:	if(r<=255){r= r-val;}
				if(r<=40){colorPos++;}
				break;
		case 4:	if(b<=255){b+=val;}
				if(b>=255){colorPos++;}
				break;
		case 5:	if(g<=255){g+=val;}
				if(g>=255){colorPos++;}
				break;
		case 6:	if(b<=255){b= b-val;}
				if(b<=40){colorPos++;}
				break;
		case 7:	if(r<=255){r+=val;}
				if(r>=255){colorPos++;}
				break;
		case 8:	if(b<=255){b+=val;}
				if(b>=255){colorPos = 1;}
				break;
	}
	//don't want it bigger then 255
	r=(r>255)? 255 : r;
	g=(g>255)? 255 : g;
	b=(b>255)? 255 : b;
	
	//or less than 0
	r=(r<0)? 0 : r;
	g=(g<0)? 0 : g;
	b=(b<0)? 0 : b;
	
	r = decToHex(r); 
	g = decToHex(g);
	b = decToHex(b);

	r=(r.length < 1)? ("0") : r ; //this fixes a stupid netscape bug with 0's
	g=(g.length < 1)? ("0") : g ;
	b=(b.length < 1)? ("0") : b ;

	r=(r.length < 2)? ("0"+r+"") : r ; //make sure they fill out properly
	g=(g.length < 2)? ("0"+g+"") : g ;
	b=(b.length < 2)? ("0"+b+"") : b ;
	res = ('#'+r+""+g+""+b+"");
//appendBody(document.createTextNode("gray"+res+" :| "));
	box.title = colorPos;
	return res;
}

function cycleColor(id, val) {
/*pass the box's id then the val to added the rate at which the 
colors are cycles through the gray code cycling*/
		var box = document.getElementById(id);
		var old = box.style.backgroundColor;
		var r = "";
		var g = "";
		var b = "";
		val = Math.abs(parseInt(val));// ensure it's a positive int
		//for netscape, which returns an rgb value (why can't we all just get along
		if(old.charAt(0) == 'r') {
			r = parseInt(old.substring(4,7));
			g = parseInt(old.substring(9,12));
			b = parseInt(old.substring(14,17));				
		}
		//ie returns a more standard #aabbcc value
		else {
			old = old.substring(1); //Cut the '#' out
			r = parseInt(old.substring(0,2), 16);
			g = parseInt(old.substring(2,4), 16);
			b = parseInt(old.substring(4,6), 16);
		}
		var newVal = grayCodeGradient(r, g, b, id, val);
//document.write(newVal);
	box.style.backgroundColor = newVal;
}

function setBoxStyle(box, xpos, ypos, xwidth, ywidth, boxColor, id) {
	box.style.backgroundColor=boxColor;
	box.style.position="absolute";
	box.style.left=(xpos+"px");
	box.style.top=(ypos+"px");
	box.style.width=(xwidth+"px");
	box.style.height=(ywidth+"px");
	box.style.zIndex = "99";
	box.title = 0; //using this to store extra persistent data for gradient cycling
	box.id = id;
//document.write("BoxStyleId: "+id+"<br/>");
}

function Box(xpos, ypos, xwidth, ywidth, boxColor, id) {
	element = document.createElement("div");
	setBoxStyle(element, xpos, ypos, xwidth, ywidth, boxColor, id);
//newText = document.createTextNode(id);
//element.appendChild(newText);
	return element;
}


function appendBody(elem){
	bodyRef = document.getElementsByTagName("body").item(0);
	bodyRef.appendChild(elem);
}

function removeFromBody(elem){
	bodyRef = document.getElementsByTagName("body").item(0);
	bodyRef.removeChild(elem);
}

function makeBoxes(width, height, interpol, spacing) {
/*width and height refer to the size you want the box array on the screen, interpol
refers to how much interpolation you wish, ie 1 means a 1 to 1 correspondence
with width and height 2 means each pixel is twice as large etc. I recommend 20 and above
depending on how much memory you have. Spacing is optional and puts white space between each 
pixel */
	boxArrayX = Math.round(width/interpol);
	boxArrayY = Math.round(height/interpol);
	boxArrayInterpol = interpol;
	if (spacing == null)
		spacing = 0;
	boxArray = new Array(boxArrayX);
//document.write("BoxArray: "+boxArrayX*boxArrayY+" boxArrayX: "+boxArrayX+" boxArrayY: "+boxArrayY+"<br/>");
	for(i=0; i<boxArrayX; i++) {
	boxArray[i] = new Array(boxArrayY);
		for(j=0; j<boxArrayY; j++) {
			id = i.toString() +","+ j.toString();
			boxArray[i][j] = new Box(i*(interpol+spacing), j*(interpol+spacing), interpol, interpol, "#000000", id);

//document.write("i j index xpos ypos: "+i+" "+j+" "+(i*interpol)+" "+(j*interpol)+" id: "+id+" || "); 
		}
	}
}

function drawBoxes() {
	for (i=0; i<boxArrayX; i++) {
		for(j=0; j<boxArrayY; j++) {
			appendBody(boxArray[i][j]);
		}
	}
}	

function changeBox(id, boxColor, xpos, ypos, xwidth, ywidth) {
	var box = document.getElementById(id);
	if(boxColor != null)
		box.style.backgroundColor = boxColor;
	if(xpos != null)
		box.style.left = xpos;
	if(ypos != null)
		box.style.top = ypos;
	if(xwidth != null)
		box.style.width = xwidth;
	if(ywidth != null)
		box.style.height = ywidth;		
}

function setHidden(id, hiddenBool) {
	var box = document.getElementById(id);
	if(hiddenBool == '1')
		box.style.display = "none";
	else
		box.style.display = "inline";
}

function cycleSize(id, val) {
	var box = document.getElementById(id);
	var size = box.style.width;
	size = size.substring(0, (size.length-2));
	var newVal = parseInt(val) + parseInt(size);
	changeBox(id, null, null, null, newVal, newVal);
	
}

function clearBoxes() {
	for(j=0; j<boxArrayY; j++) {
		for(i=0; i<boxArrayX; i++){
			id = i.toString() +","+ j.toString();
			removeFromBody(document.getElementById(id));
			//not the cleanest way, as we still have all the timeIntervals
			//still trying to run, but it's easy and dirty :-P
		}
	}
}


function runColorChange() {
	var timeout = 1;
	for(j=0; j<boxArrayY; j++) {
		for(i=0; i<boxArrayX; i++){	
			var col = rand4KColor();
			id = i.toString() +","+ j.toString();
			tString = "setHidden('"+id+"', '1')";
			changeBox(id, col);
			timeIDs = setInterval(tString, timeout*20);
			tString = "setHidden('"+id+"', '0')";
			timeIDs = setInterval(tString, timeout*25);
			tString = "changeBox('"+id+"', rand4KColor() )";
			timeIDs = setInterval(tString, timeout*30);
			timeout++;
		}
	}
}

function runColorCycle() {
	for(j=0; j<boxArrayY; j++) {
		for(i=0; i<boxArrayX; i++){
			var col = rand4KColor();
			id = i.toString() +","+ j.toString();
			changeBox(id, col);
			tString = "cycleColor('"+id+"', '5')";
			timeIDs = setInterval(tString, 3);
		}
	}
}

function runGrowShrink() {
	for(j=0; j<boxArrayY; j++) {
		for(i=0; i<boxArrayX; i++){
			var col = rand4KColor();
			id = i.toString() +","+ j.toString();
			changeBox(id, col);
			tString = "cycleSize('"+id+"', '1')";
			tout = Math.round(Math.random()*60);
			timeIDs = setInterval(tString, tout);
		}
	}
}


function rollYourOwn() {
	alert("Enter valid numbers, you're old enough and this is only experimental...")
	var xres = prompt("X Res, int", 400);
	var yres = prompt("Y Resolution, int", 400);
	var sze = prompt("Interpolation, this is the x*y/interpol, recommend high values unless you've got a crazy fast machine", 40);
	var spc = prompt("Spacing (that appears between squares)", 3); 
	makeBoxes(parseInt(xres), parseInt(yres), parseInt(sze), parseInt(spc));
}

makeBoxes(600, 600, 60, 0);

//drawBoxes();
//runGrowShrink();
//runColorCycle();
//runColorChange();
