<html>
	<head>
		<title>The Game of Life: HTML Canvas Demo</title>
		<script language="javascript">
			var size;
			var hertz;
			var height_px;
			var width_px;
			var height_c;
			var width_c;
			var cells;
			var timer;
			var effects;

			// set start/stop button value
			function btn_control_set_name(name) {
				var btn = document.getElementById('control-btn');
				btn.value = name;
			}

			// generate an 2d array of cells based on current geometry
			function gen_cells() {
				var new_cells = new Array(height_c);
				for(var i = 0; i < height_c; i++) {
					new_cells[i] = new Array(width_c);
				}
				for (var y = 0; y < height_c; y++) {
					for (var x = 0; x < width_c; x++) {
						new_cells[y][x] = 0;
					}
				}
				return new_cells;
			}

			// init the cells	
			function init_cells() {
				cells = gen_cells();
			}

			// Generate and populate the geometry variables
			// possibly over complicated from my experiment with an elastic resizeable board
			function get_demensions() {
				var canvas = document.getElementById('board');
				size = parseInt( document.getElementById('density').value);
				width_px = canvas.offsetWidth - (canvas.offsetWidth%size);
				canvas.width = width_px;
				width_c = width_px/ size;
				height_px = canvas.offsetHeight - (canvas.offsetHeight%size);
				canvas.height = height_px;
				height_c = height_px/size;
				//console.log(width_px + 'x' + height_px + ' => ' + width_c + 'x' +height_c);
			}

			// init the board
			//   set vars
			//   get geometry
			//   generate cells for 1st time
			//   draw
			function board_init() {
				var canvas = document.getElementById('board');
				document.getElementById('density').value = 10;
				get_demensions();
				btn_control_set_name('Start');
				document.getElementById('speed').value = 8;
				change_speed();
				init_cells();
				document.getElementById('effects').value=0;
				effects=0;
				redraw();
			}

			// Draw the current board: gird, then cells
			function redraw() {
				var canvas = document.getElementById('board');
				var c = canvas.getContext('2d');
				get_demensions();
				
				c.globalCompositeOperation = 'destination-over';

				c.clearRect(0, 0, width_px, height_px);
				
				c.fillStyle='rgba(0,0,0,0.)';
				c.strokeStyle= 'rgba(192,192,192,1)';

				c.beginPath();

				draw_grid(c);
				c.stroke();
				

				draw_cells(c);
			}

			// Generate a cycling darkening with age rainbow color
			// based on math/code from http://www.krazydad.com/makecolors.php
			function setStrokeAgeRainbow(l, c) {
				var frequency = 0.3;
				var center = 200; //128;
				var width = 55; //127;

				var red =  Math.floor( (Math.sin(frequency*l + 0) * width + center) -l) ;
				var green =  Math.floor( (Math.sin(frequency*l + 2) * width + center) -l);
				var blue =  Math.floor( (Math.sin(frequency*l + 4) * width + center) -l);
							
				red = Math.max(0, red); 
				green = Math.max(0, green);
				blue = Math.max(0, blue);

				c.strokeStyle= 'rgba('+red+','+green+','+blue+','+1+')';
			}

			// Draw all the cells onto the board inside the grid
			function draw_cells(c) {
				for (var y = 0; y < height_c; y++) {
					for (var x =0; x < width_c; x ++) {
						if (cells[y][x] != 0) {

							if(effects == 0) {
								c.strokeStyle = 'rgba(0,0,0,1)';
							} else {
								setStrokeAgeRainbow(cells[y][x], c);
							}

							c.fillStyle = c.strokeStyle;
							c.beginPath();
							c.moveTo(x*size +1, y*size+1);
							c.lineTo(x*size+ size-1, y*size+1);
							c.lineTo(x*size+ size-1, y*size + size-1);
							c.lineTo(x*size+1, y*size + size-1);
							// auto return home
							c.stroke();
							c.fill();
						}
					}
				}
			}

			// draw the grid and border
			function draw_grid(c) {
				// Horizontal lines
				for (var y = 0; y <= height_px; y += size) {
					c.moveTo(0, y);
					c.lineTo(width_px, y);
				}
				// Vertical lines
				for (var x = 0; x <= width_px; x += size) {
					c.moveTo(x, 0);
					c.lineTo(x, height_px);
				}

			}

			// When we've loaded, run!
			window.onload = board_init;

			// is a cell alive or dead
			//   0 for dead, 1 for alive
			//   walls count as dead
			//   used by num_neighbours
			function cell_value(x, y) {
				//console.log('(x:' + x + ',y:' + y + ') w/s:' + (width/size) + ' h/s:' + (height/size) );
				if (x < 0 || x >= width_c || y < 0 || y >= height_c) {
					return 0;
				} else {
					return cells[y][x] == 0 ? 0 : 1;
				}
			}

			// return the number of neighbours for a cell
			//   walls count as dead
			function num_neighbours(x, y) {
				var n = 0;
				n += cell_value(x-1, y-1);
				n += cell_value(x, y-1);
				n += cell_value(x+1, y-1);
				
				n += cell_value(x-1, y);
				n += cell_value(x+1, y);
				
				n += cell_value(x-1, y+1);
				n += cell_value(x, y+1);
				n += cell_value(x+1, y+1);

				return n;
			}

			// Simulate one generation of cells
			//   create a new cells 2d array and simulate the next generation
			//   into it and then replace the current cells with it
			function step() {
				var new_cells = gen_cells();
				for(var y=0; y < height_c; y++) {
					for(var x =0 ; x < width_c; x++) {
						var n = num_neighbours(x, y);
						if (cells[y][x] != 0) {
							if (n < 2 || n > 3) {
								new_cells[y][x] = 0;
							} else {
								new_cells[y][x] = cells[y][x] +1;
							}
						} else if (n == 3) {
							new_cells[y][x] = 1;
						} else {
							new_cells[y][x] = 0;
						}
				 	}
				}
				//new_cells[Math.floor(Math.random()*8)][ Math.floor(Math.random()*8) ] = true;
				cells = new_cells;
			}

			// Stop/Start button press
			function control_click() {
				var btn = document.getElementById('control-btn');
				if (btn.value == 'Start') {
					tick();
					btn.value = 'Stop';
				} else {
					clearTimeout(timmer);
					btn.value = 'Start';
				}
			}

			// Function to call on timmer
			//  simulate on e generation, 
			//  redrawn
			//  reset timmer
			function tick() {
				step();
				redraw();
				timmer = setTimeout('tick();', 1000.0/hertz);
			}

			// Handler for a click on the board to place/unplace a cell
			function board_click(e) {
				var canvas = document.getElementById('board');
				var x = Math.floor((e.pageX-canvas.offsetLeft)/size);
				var y = Math.floor((e.pageY-canvas.offsetTop)/size);
				cells[y][x] = ! cells[y][x];
				redraw();
			}

			// Change speed handler
			function change_speed() {
				hertz = parseInt(document.getElementById('speed').value);
			}

			// Resize board handler (kind of not used)
			//   Left over from experiment in resiable elastic board
			//   Abandoned because of problems with lining up mouse clicks on board
			//   and weird stretcing issues
			function resize_cells() {
				var new_cells = gen_cells();
				var w = Math.min(width_c, cells[0].length);
				var h = Math.min(height_c, cells.length);
				for(var y = 0; y < h; y++) {
					for(x = 0; x < w; x++) {
						new_cells[y][x] = cells[y][x];
					}
				}

				cells = new_cells;
			}
			
			// What to do on a document resize (more usefull for elastic boards)
			function canvas_resize() {
				get_demensions();
				resize_cells();
				redraw();
			}

		</script>
	</head>
	<body onResize="canvas_resize();">
		Speed: <select id="speed" onChange="change_speed();"> 
			<option value="1">1 Hz</option>
			<option value="2">2 Hz</option>
			<option value="4">4 Hz</option>
			<option value="8">8 Hz</option>
			<option value="16">16 Hz</option>
			<option value="32">32 Hz</option>
			<option value="64">64 Hz</option>
		</select>
		Density: <select id="density" onChange="canvas_resize();">
				<option value="3">X-Small</option>
				<option value="5">Small</option>
				<option value="10">Medium</option>
				<option value="15">Large</option>
				<option value="20">X-Large</option>
			</select>
			Effects: <select id="effects" onChange="effects=parseInt(this.value); redraw();">
				<option value="0">None (black and white)</option>
				<option value="1">Color age (Raindbow)</option>
			</select>
			<input type="button" id="control-btn" onClick="control_click(); return false;" />

			<small style="margin-left:20px">Dan Ballard (2011) &lt;<a href="mailto:dan@mindstab.net">dan@mindstab.net</a>&gt;</small>
			<input style="left:775px; position: absolute;" type="button" value="Clear" onClick="cells = gen_cells(); redraw();" />
		<br/>
		<canvas id="board" onClick="board_click(event);" width="800" height="400" style="/*width: 100%; height: 100%*/"></canvas>
	</body>
</html>