// Keenan Tims ktims@gotroot.ca
// 2009-02-05
//
// This code is hereby released into the public domain.

var gameWidth = 42;
var gameHeight = 27;
var gameClass = "lifeGame";

var blockClass = "lifeBlock";
var blockFirstClass = "lifeBlockFirst";
var blockOnColour = "#000000";
var blockOffColour = "#ffffff";

var startState = new Array(gameWidth);
for (var i = 0; i < gameWidth; i++) {
	startState[i] = new Array(gameHeight);
}

startState[0][6] = true;
startState[0][7] = true;
startState[1][6] = true;
startState[1][7] = true;

startState[10][6] = true;
startState[10][7] = true;
startState[10][8] = true;
startState[11][5] = true;
startState[11][9] = true;
startState[12][4] = true;
startState[12][10] = true;
startState[13][4] = true;
startState[13][10] = true;

startState[14][7] = true;

startState[15][5] = true;
startState[15][9] = true;
startState[16][6] = true;
startState[16][7] = true;
startState[16][8] = true;
startState[17][7] = true;

startState[20][4] = true;
startState[20][5] = true;
startState[20][6] = true;
startState[21][4] = true;
startState[21][5] = true;
startState[21][6] = true;
startState[22][3] = true;
startState[22][7] = true;

startState[24][2] = true;
startState[24][3] = true;
startState[24][7] = true;
startState[24][8] = true;

startState[34][4] = true;
startState[34][5] = true;
startState[35][4] = true;
startState[35][5] = true;

function LifeGame(container) {
	this.c = container;
	// The new div we create just for the game
	this.g = container.ownerDocument.createElement("div");
	this.g.className = gameClass;

	this.running = false;

	// Create space to store all the blocks, then initialize them all
	this.blocks = new Array(gameWidth);
	this.changes = new Array();

	for (var i = 0; i < gameWidth; i++)
		this.blocks[i] = new Array(gameHeight);

	for (var y = 0; y < gameHeight; y++) {
		for (var x = 0; x < gameWidth; x++) {
			this.blocks[x][y] = new LifeBlock(this);
		}
		this.blocks[0][y].setFirst(true);
	}
	this.c.appendChild(this.g);
}

LifeGame.prototype.setState = function(state) {
	for (var y = 0; y < gameHeight; y++) {
		for (var x = 0; x < gameWidth; x++) {
			if (state[x][y] == true) {
				this.blocks[x][y].turnOn();
			} else {
				this.blocks[x][y].turnOff();
			}
		}
	}
	this.drawAll();
}

LifeGame.prototype.drawAll = function() {
	for (var y = 0; y < gameHeight; y++)
		for (var x = 0; x < gameWidth; x++)
			this.blocks[x][y].update();
}

LifeGame.prototype.step = function() {
	if (this.running) {
		this.update();
		setTimeout("game.step()", 250);
		this.iterate();
	}
}

LifeGame.prototype.iterate = function() {
	// Actual game logic
	for (var y = 0; y < gameHeight; y++) {
		for (var x = 0; x < gameWidth; x++) {
			var neighbours = this.countNeighbours(x,y);
			if (this.blocks[x][y].on) {
			// Kill blocks with < 2 or > 3 neighbours
				if (neighbours < 2 || neighbours > 3)
					this.changes.push([x,y]);
			} else {
				// Make alive blocks with 3 neighbours
				if (neighbours == 3)
					this.changes.push([x,y]);
			}
		}
	}
}

LifeGame.prototype.update = function() {
	for (var i;i = this.changes.pop();) {
		this.blocks[i[0]][i[1]].toggle();
		this.blocks[i[0]][i[1]].update();
	}
}

LifeGame.prototype.countNeighbours = function(x,y) {
	var count = 0;

	// up-left
	if (x!=0 && y!=0 && this.blocks[x-1][y-1].on) count++;
	// up-centre
	if (y!=0 && this.blocks[x][y-1].on) count++;
	// up-right
	if (x!=gameWidth-1 && y!=0 && this.blocks[x+1][y-1].on) count++;
	// middle-right
	if (x!=gameWidth-1 && this.blocks[x+1][y].on) count++;
	// down-right
	if (x!=gameWidth-1 && y!=gameHeight-1 && this.blocks[x+1][y+1].on) count++;
	// down-centre
	if (y!=gameHeight-1 && this.blocks[x][y+1].on) count++;
	// down-left
	if (x!=0 && y!=gameHeight-1 && this.blocks[x-1][y+1].on) count++;
	// middle-left
	if (x!=0 && this.blocks[x-1][y].on) count++;

	return count;
}

LifeGame.prototype.run = function() {
	if (!this.running) {
		this.running = true;
		this.step();
	}
}

LifeGame.prototype.pause = function() {
	this.running = false;
}

function LifeBlock(game) {
	this.game = game;
	this.on = false;
	this.isFirst = false;

	this.element = this.game.g.ownerDocument.createElement("div");
	this.element.block = this;
	this.element.className = blockClass;
	this.element.onclick = function() { this.block.toggle(); this.block.update(); }
	
	this.game.g.appendChild(this.element);
}

LifeBlock.prototype.update = function() {
	if (this.on == true)
		this.element.style.setProperty('background-color', blockOnColour, null);
	else
		this.element.style.setProperty('background-color', blockOffColour, null);
}

LifeBlock.prototype.turnOn = function() {
	this.on = true;
}

LifeBlock.prototype.turnOff = function() {
	this.on = false;
}
LifeBlock.prototype.toggle = function() {
	this.on = !this.on;
}

LifeBlock.prototype.setFirst = function(isFirst) {
	if (isFirst) {
		this.isFirst = true;
		this.element.className += " " + blockFirstClass;
	} else {
		this.isFirst = false;
		this.element.className = blockClass;
	}
}

function RunGame() {
	cont = document.getElementById("main");
	game = new LifeGame(cont);
	game.setState(startState);
	game.run();
}

