var BOARD_SIZE = {'WIDTH':20, 'HEIGHT':20};
var CELL_SIZE = {'BORDER':1, 'OUTER':12, 'INNER':10}; //OUTER = 2*BORDER + INNER
var DIR = {'N':1, 'S':2, 'E':3, 'W':4};
var STATE = {'PLAY':0, 'PAUSED':1, 'END':2, 'RESTART':3};
var CELL = {'ON':true, 'OFF':false};
var count = 0;

window.addEvent('domready',onDomReady);
window.addEvent('keydown',onKeyDown);

var snakeGame;

function onDomReady() {
	
	snakeGame = new SnakeGame($('mySnakeGame'));
	
}

function onKeyDown(event){
	var event = new Event(event);
	snakeGame.dir.second = snakeGame.dir.first;
	switch(event.key){
		case 'up':
			directionChange(DIR.N);
			break;
		case 'down':
			directionChange(DIR.S);
			break;
		case 'left':
			directionChange(DIR.W);
			break;
		case 'right':
			directionChange(DIR.E);
			break;
		case 'r':
			if(snakeGame.gameState == STATE.PLAY){
				snakeGame.killSnake(snakeGame.snake.head);
			}
			snakeGame.newGame();
			break;
		case 'space':
			snakeGame.startStop();
			break;
	}
	
}

function directionChange(dir) {
	snakeGame.dir.first = dir;
	snakeGame.keysPressed++;
	if(snakeGame.gameState == STATE.RESTART || snakeGame.gameState == STATE.PAUSED){
		snakeGame.start();
	}
}

function debug(msg) {
	$('debug').innerHTML += msg+'<br>';
}


/*
The Cell class is the basic building block of a board.  Each grid square is one cell and can be turned on or off
how a cell looks is defined in snake.css
*/
function Cell () {
	this.on = false;
	this.border = new Element('DIV',{
		'class':'cell-border',
		'styles':{
			'width':CELL_SIZE.OUTER,
			'height':CELL_SIZE.OUTER
		}
	});
	this.block = new Element('DIV',{
		'class':'cell',
		'styles':{
			'width':CELL_SIZE.INNER,
			'height':CELL_SIZE.INNER,
			'top':CELL_SIZE.BORDER,
			'left':CELL_SIZE.BORDER
		}
	});
	this.block.injectInside(this.border);
}

Cell.prototype.injectInside = function (ele) {
	this.border.injectInside(ele);
}

Cell.prototype.toggle = function () {
	this.block.toggleClass('cell-on');
}

Cell.prototype.toggleNibble = function () {
	this.block.toggleClass('cell-nibble');
}

Cell.prototype.isOn = function () {
	return this.block.hasClass('cell-on');
}

function Board (ele) {
	this.ele = ele;

	
	var tot_width = BOARD_SIZE.WIDTH*CELL_SIZE.OUTER;
	var tot_height = BOARD_SIZE.HEIGHT*CELL_SIZE.OUTER;
	this.ele.setStyles({'width':tot_width, 'height':tot_height});
	
	this.msgBackground = new Element('DIV',{
		'id':'msgBackground',
		
	});
	
	this.msgHolder = new Element('DIV',{
		'id':'msgHolder',
		'style':{
			'position':'absolute',
			'width':'100%',
			'height':'100%',
			'background-color':'red'
		}
	});
	
	this.msg = new Element ('DIV',{
		'id':'msg'
	});
	
	this.fadBack = new Fx.Style(this.msgBackground, 'opacity', {duration:500});
	this.msgOn = false;
	
	this.cells = new Array(BOARD_SIZE.HEIGHT);
	for(var r=0; r<BOARD_SIZE.HEIGHT; r++){
		this.cells[r] = new Array(BOARD_SIZE.WIDTH);
		for(var c=0; c<BOARD_SIZE.WIDTH; c++){
			this.cells[r][c] = new Cell();
			this.cells[r][c].injectInside(ele);
		}
	}
	
	this.msg.injectInside(this.msgHolder);
	this.msgBackground.injectInside(ele);
	this.msgHolder.injectInside(ele);
	this.msgBackground.setOpacity(0);
	this.msg.setStyle('top',BOARD_SIZE.HEIGHT*CELL_SIZE.OUTER/2-msg.getSize().size.y/2);
}

Board.prototype.changeCellState = function(cellOn,r,c) {
	if(r < 0 || r >= BOARD_SIZE.HEIGHT || c < 0 || c >= BOARD_SIZE.WIDTH)
		return false; // new cell is out of bounds 
	if(this.cells[r][c].isOn() == cellOn)
		return false; // new cell is already in the change to state
	this.cells[r][c].toggle()
	return true
}

Board.prototype.getCell = function(r,c) {
	if(r < 0 || r >= BOARD_SIZE.HEIGHT || c < 0 || c >= BOARD_SIZE.WIDTH)
		return false; // new cell is out of bounds 
	return this.cells[r][c];
}

Board.prototype.display = function(str,color) {
	this.msg.setHTML(str);
	this.fadBack.start(0,.5);
	this.msgOn = true;
	this.msg.setStyle('top',BOARD_SIZE.HEIGHT*CELL_SIZE.OUTER/2-msg.getSize().size.y/2);
	this.msgBackground.setStyle('backgroundColor',color);
}

Board.prototype.clearMsg = function() {
	this.msg.setHTML('');
	this.fadBack.start(.5,0);
	this.msgOn = false;
}


function SnakeGame(ele) {
	this.board = new Board(ele);
	this.newGame();
}

SnakeGame.prototype.newGame = function () {
	this.snake = new Snake();
	if(this.nibble) // clear old nibble
		this.board.getCell(this.nibble.row,this.nibble.col).toggleNibble();
	this.nibble = this.getNibble();
	
	this.dir = {'first':DIR.E, 'second':DIR.E};
	this.keysPressed = 0;
	this.grow = 3;
	this.gameState = STATE.RESTART;
	
	this.board.changeCellState(CELL.ON,this.snake.head.row,this.snake.head.col);
	this.board.changeCellState(CELL.ON,this.snake.tail.row,this.snake.tail.col);
}

SnakeGame.prototype.start = function () {
	if(this.board.msgOn){
		this.board.clearMsg();
	}
	this.gameState = STATE.PLAY;
	this.run();
}

SnakeGame.prototype.addCell = function(dir,keepTail) {
	this.snake.newSnakeCell(dir);
	if(!this.board.changeCellState(CELL.ON,this.snake.head.row,this.snake.head.col)){
		this.gameState = STATE.END;
		this.snake.head = this.snake.head.behind;
		delete this.snake.head.infront;
		return;
	}
	else if(this.snake.head.row == this.nibble.row && this.snake.head.col == this.nibble.col){
		this.grow += 10;
		this.board.cells[this.nibble.row][this.nibble.col].toggleNibble();
		this.nibble = this.getNibble();
	}	
	else if (keepTail != true){
		this.board.changeCellState(CELL.OFF,this.snake.tail.row,this.snake.tail.col);
		this.snake.removeTail();
	}	
}

SnakeGame.prototype.run = function(game) {
	switch (this.gameState) {
		case STATE.PLAY:
			if(this.keysPressed > 1){
				this.addCell(this.dir.second,this.keepTail());
				this.addCell(this.dir.first,this.keepTail());
			}
			else{
				this.addCell(this.dir.first,this.keepTail());
			}
			this.keysPressed = 0;
			setTimeout('snakeGame.run()',150);
			break;
		case STATE.PAUSED:
			break;
		case STATE.RESTART:
			break;
		case STATE.END:
			this.gameOver();
			this.killSnake(this.snake.head);
	}
}

SnakeGame.prototype.getNibble = function() {
	var r = Math.ceil(Math.random() * BOARD_SIZE.HEIGHT) - 1;
	var c = Math.ceil(Math.random() * BOARD_SIZE.WIDTH) - 1;
	if(this.board.cells[r][c].isOn()){
		return this.getNibble();
	}
	this.board.cells[r][c].toggleNibble();
	return {'row':r, 'col':c};
}

SnakeGame.prototype.keepTail = function () {
	var keepTail = false;
	if (this.grow > 0){
		keepTail = true;
		this.grow--;
	}
	return keepTail;
}

SnakeGame.prototype.startStop = function (move){
	switch (this.gameState){
		case STATE.END:
			return;
			break;
		case STATE.PLAY:
			this.gameState = STATE.PAUSED;
			this.board.display("<h1>Game Paused</h1>","white");
			break;
		default:
			this.start();
	}
} 


SnakeGame.prototype.gameOver = function () {
	this.board.display("<h1>Game Over</h1>","red");
}

SnakeGame.prototype.killSnake = function (cell) {
	if(cell){
		var time = (this.gameState == STATE.END)?50:0;
		if(this.board.getCell(cell.row,cell.col))
			this.board.getCell(cell.row,cell.col).toggle();
		window.setTimeout(function (cell){snakeGame.killSnake(cell)},time,cell.behind);
	}
	else {
		if(this.gameState == STATE.END) { // if a restart has already occured this will delete the new game settings
			this.newGame();
		}
	}
}



/*
The Snake class is a filo queue of SnakeCells.  Adding a new cell makes the new cell the snake head
and removing the tail makes the second ot last cell the new tail.  The head can not be removed and cells
can not be added to the head. 
*/
function Snake () {
	var srow = BOARD_SIZE.HEIGHT/2;
	var scol = BOARD_SIZE.WIDTH/2-2;
	
	this.head = new SnakeCell(null,null,srow,scol);
	//alert('stop '+(count++));
	this.tail = this.head;
	this.tail.infront = this.head;
	//this.tail = new SnakeCell(null,this.head,srow,scol-1);
	this.head.behind = this.tail;
}

//adds new cell to snake in direction dir
Snake.prototype.newSnakeCell = function(dir) {
	switch(dir){
		case DIR.N:
			r = this.head.row-1;
			c = this.head.col;
			break;
		case DIR.E:
			r = this.head.row;
			c = this.head.col+1;
			break;
		case DIR.S:
			r = this.head.row+1;
			c = this.head.col;
			break;
		case DIR.W:
			r = this.head.row;
			c = this.head.col-1;
	}
	
	var tmpHead = new SnakeCell(this.head,null,r,c);
	this.head.infront = tmpHead;
	this.head = tmpHead;
}

Snake.prototype.removeTail = function() {
	this.tail = this.tail.infront;
	delete  this.tail.behind;
}

Snake.prototype.toString = function() {
	var strOut = "Snake:\n";
	var snakePtr = this.tail;
	while(snakePtr){
		if(snakePtr == this.head) strOut += "Head ";
		else if (snakePtr == this.tail) strOut += "Tail "; 
		strOut += "Row: "+snakePtr.row+" Col: "+snakePtr.col+"\n";
		snakePtr = snakePtr.infront;
	}
	return strOut;
}	



function SnakeCell(cellBehind,cellInfront,r,c) {
	this.behind = cellBehind;
	this.infront = cellInfront;
	this.row = r;
	this.col = c;
}



