// ------------------------------- CONSTANTS ----------------------------------
// ----------------------------------------------------------------------------
var BOARDSZ = 6;
var GameTurn = {"Player1": 0, "Player2": 1};
var GameMode = {"Easy": 0, "Medium": 1, "Hard": 2, "TwoPlayer": 3};


// ------------------------------- VARIABLES ----------------------------------
// ----------------------------------------------------------------------------
var maingame;	// Akihabara game construct
/*maingame.gamemode = -1;		// two player, or some level of CPU?
maingame.gameturn = -1;	// whose turn is it, &c.
maingame.winner = -1;		// store the winner, when there is one*/

var gameboard;	// array containing tiles
var selectcol, selectrow;	// the tile we have selected

var isFlipping, isGameOver;
var updatelist = new Array();	// list of tiles to update

var offx, offy;	// offsets rendering position

// create a logging console only if it's available
var console = window["console"];
var DOCONSOLE = console && console.log;


// --------------------------------- HELPERS ----------------------------------
// ----------------------------------------------------------------------------

//load resources immediately on page load
window.addEventListener("load", loadResources, false);


function loadResources() {
	if(DOCONSOLE) console.log("Loading resources...");

	// Initialise Akihabara w/default settings
	help.akihabaraInit("Cube Jumpr!");

	// load sprites from files
	gbox.addImage("font", "font.png");
	gbox.addImage("logo", "logo.png");
	gbox.addImage("sprAlldice", "alldice.png");
	gbox.addImage("sprSelectbox", "selectbox.png");

	// snip the dice sprites
	gbox.addTiles({
		id: "diceTiles", // unique ID
		image: "sprAlldice", // use label defined above
		tileh: 32,
		tilew: 32,
		tilerow: 5,
		gapx: 0,
		gapy: 0
	});
	
	// snip the select box sprite
	//TODO: really need to do this?
	gbox.addTiles({
		id: "selectTile",
		image: "sprSelectbox",
		tileh: 32,
		tilew: 32,
		tilerow: 1,
		gapx: 0,
		gapy: 0
	});

	// create the fonts
	gbox.addFont({
		id: "small",
		image: "font",
		firstletter: " ",
		tileh:8, tilew:8, tilerow:255,
		gapx:0, gapy:0
	});
	gbox.addFont({
		id: "small-blue",
		image: "font",
		firstletter: " ",
		tileh: 8, tilew: 8, tilerow: 255,
		gapx: 0, gapy: 32
	});
	gbox.addFont({
		id: "small-red",
		image: "font",
		firstletter: " ",
		tileh: 8, tilew: 8, tilerow: 255,
		gapx: 0, gapy: 16
	});

	// main function registered as callback after this function finishes
	gbox.setCallback(main);

	// when everything's ready, loadAll() downloads all needed resources
	gbox.loadAll();

	if(DOCONSOLE) console.log("  Done.");
}


// increment the next tile that needs updating
function UpdateNextTile() {
	// get the next tile to update
	var next = updatelist[0];
	
	// move all tiles towards [0], and remove the one at the end
	if(updatelist.length > 1) {
		for(var i=0; i < updatelist.length-1; i++) {
			updatelist[i] = updatelist[i+1];
		}
	}
	updatelist.pop();
	
	// increment the new tile
	gameboard[next.row][next.col].Increment(next.player);
}


// check if anyone has won the game
// return: 1 if Player 1 wins, 2 if Player 2 wins, -1 if no one
function CheckWinner() {
	// no one claimed the first tile, so obviously no one wins
	if("none" == gameboard[0][0].owner) {
		return -1;
	}
	
	var winner = gameboard[0][0].owner;
	for(row=0; row < BOARDSZ; ++row) {
		for(col=0; col < BOARDSZ; ++col) {
			// if we found a tile that doesn't belongs to a different player, the game's not over
			if(gameboard[row][col].owner != winner) {
				return -1;
			}
		}
	}
	
	// return the winner
	if("p1" == winner) {
		return 1;
	} else if("p2" == winner) {
		return 2;
	}
}


// -------------------------------- TILE OBJECT -------------------------------
// ----------------------------------------------------------------------------

// --- CONSTRUCTOR ---
function JTile(row, col) {
	this.row = row;
	this.col = col;
	
	// find out how many neighbours this tile has
	if( (this.row == 0 || this.row == BOARDSZ-1) && (this.col == 0 || this.col == BOARDSZ-1) ) {
		this.numneighbours = 2;  // it's in one of the corners
	} else if(this.row == 0 || this.row == BOARDSZ-1 || this.col == 0 || this.col == BOARDSZ-1) {
		this.numneighbours = 3;  // one of the edges
	} else {
		this.numneighbours = 4;  // interior tile
	}
	
	// defaults
	this.owner = "none";
	this.value = 1;
	this.frame = 10;
	this.Increment = IncrementTile;
	this.RecalculateFrame = RecalculateFrame;
}


// figure out which frame of the tileset (i.e., which die face) to show
function RecalculateFrame() {
	if(this.owner == "p1") {
		switch(this.value) {
			case 1: this.frame = 0; break;
			case 2: this.frame = 1; break;
			case 3: this.frame = 2; break;
			case 4: this.frame = 3; break;
			case 5: this.frame = 4; break;
		}
		return;
	} else if(this.owner == "p2") {
		switch(this.value) {
			case 1: this.frame = 5; break;
			case 2: this.frame = 6; break;
			case 3: this.frame = 7; break;
			case 4: this.frame = 8; break;
			case 5: this.frame = 9; break;
		}
		return;
	} else {	// no owner, show white 1 dot
		this.frame = 10;
	}
}


// add one to this tile
function IncrementTile(owner) {
	this.owner = owner;
	this.value = (this.value) % 5 + 1;

	// we may have to increment the neighbours and reset this tile
	if(this.value > this.numneighbours) {
		this.value = 1;
		
		if(-1 == maingame.gamewinner) {
			if(this.row > 0) {
				updatelist.push(new TileToUpdate(this.row-1, this.col, this.owner));
			}
			if(this.row < BOARDSZ-1) {
				updatelist.push(new TileToUpdate(this.row+1, this.col, this.owner));
			}
			if(this.col > 0) {
				updatelist.push(new TileToUpdate(this.row, this.col-1, this.owner));
			}
			if(this.col < BOARDSZ-1) {
				updatelist.push(new TileToUpdate(this.row, this.col+1, this.owner));
			}
		}
	}
	this.RecalculateFrame();
}


// this is simply a structure to indicate which tile is to be updated next,
// and by which player
function TileToUpdate(row, col, player) {
	this.row = row;
	this.col = col;
	this.player = player;
}


// -------------------------------- BOARD OBJECT ------------------------------
// ----------------------------------------------------------------------------

// reset our basic game variables
function ResetGame() {	
	// reset the tiles
	for(row=0; row < BOARDSZ; ++row) {
		for(col=0; col < BOARDSZ; ++col) {
			gameboard[row][col] = new JTile(row, col);
		}
	}
	
	// set defaults
	maingame.gameturn = GameTurn.Player1;
	maingame.winner = -1;		// store the winner, when there is one
	isFlipping = isGameover = false;
	selectcol = selectrow = 0;
}


// whenever a player selects a tile
function ClickTile() {
	// if P1 clicks a P2 tile, nothing should happen (and vice versa), but setting 
	// isFlipping=true regardless causes a slight flicker, which I kind of like.
	isFlipping = true;
	
	// make sure the players are allowed to click this tile
	if(maingame.gameturn == GameTurn.Player1 && gameboard[selectrow][selectcol].owner != "p2") {
		updatelist.push(new TileToUpdate(selectrow, selectcol, "p1"));
		maingame.gameturn = GameTurn.Player2;
		
	} else if(maingame.gameturn == GameTurn.Player2 && gameboard[selectrow][selectcol].owner != "p1") {
		updatelist.push(new TileToUpdate(selectrow, selectcol, "p2"));
		maingame.gameturn = GameTurn.Player1;
	}
}


// implement some basic form of AI
function DoCPUTurn() {
	var alltiles = new Array();
	
	// evaluate every tile
	for(row=0; row < BOARDSZ; ++row) {
		for(col=0; col < BOARDSZ; ++col) {
			// only consider this tile if it's not owned by Player 1
			if("p1" != gameboard[row][col].owner) {
				var tile = {r: row, c: col, val: gameboard[row][col].value};
				alltiles.push(tile);
			}
		}
	}
	
	// Fisher-Yates Shuffle
	// Tiles were added in row-col order. If we don't randomise, we'll always only select
	//   from the last X tiles. Need to switch it up a bit.
	for(var i = alltiles.length-1; i >= 1; --i) {
		var j = Math.floor(Math.random()*(i+1));
		var temp = alltiles[i];
		alltiles[i] = alltiles[j];
		alltiles[j] = temp;	
	}

	// now sort the array
	alltiles.sort(function(a, b) {return b.val - a.val;});

	// choose a random tile to click
	var max;
	switch(maingame.gamemode) {
	case GameMode.Easy:
		max = Math.min(8, alltiles.length);
		break;
	case GameMode.Medium:
		max = Math.min(4, alltiles.length);
		break;
	case GameMode.Hard:
		max = Math.min(2, alltiles.length);
		break;
	default:
		if(DOCONSOLE) console.log("ERROR: Doing CPU turn with Game Mode = " + maingame.gamemode + "!!");
		break;
	}
	
	ind = Math.floor(Math.random() * max);
	
	selectrow = alltiles[ind].r;
	selectcol = alltiles[ind].c;
	ClickTile();
}


// implement the Update() function
function BoardUpdate() {
	if(isGameOver) {	// on game over, just wait for a keypress
		if(gbox.keyIsHit("a")) {
			isFlipping = isGameOver = false;
			ResetGame();
		}
		
	} else if(isFlipping) {	// flipping tiles, just flip the next one in line 
		if(updatelist.length > 0) {
			UpdateNextTile();
		} else {	// finished updating
			isFlipping = false;
			if(-1 != maingame.gamewinner) {
				isGameOver = true;
			}
		}
		
		maingame.gamewinner = CheckWinner();
		
	} else {	// p1 or p2 are selecting/clicking
		if(GameTurn.Player1 == maingame.gameturn || GameMode.TwoPlayer == maingame.gamemode) {
			if(gbox.keyIsHit("a")) {
				ClickTile();
			} else if(gbox.keyIsHit("up")) {
				if(selectrow > 0) {
					--selectrow;
				}
			} else if(gbox.keyIsHit("down")) {
				if(selectrow < BOARDSZ-1) {
					++selectrow;
				}
			} else if(gbox.keyIsHit("left")) {
				if(selectcol > 0) {
					--selectcol;
				}
			} else if(gbox.keyIsHit("right")) {
				if(selectcol < BOARDSZ-1) {
					++selectcol;
				}
			}
			
		} else {	// it's player 2's turn...and it's a CPU!
			DoCPUTurn();
		}	// end(is game over, flipping, or turn)
	}
}


// implement the Render() function
function BoardRender() {
	
	// if it's game over, just render a message and be done with it
	if(isGameOver) {
		gbox.blitFade(gbox.getBufferContext(),{alpha:0.5});
		
		var winfont, wintext;
		if(1 == maingame.gamewinner) {
			winfont = "small-blue";
			wintext = "PLAYER 1 WINS!";
		} else if(2 == maingame.gamewinner) {
			winfont = "small-red";
			wintext = "PLAYER 2 WINS!";
		}
		
		gbox.blitText(gbox.getBufferContext(), {
			font: winfont,
			text: wintext,
			valign: gbox.ALIGN_BOTTOM,
			halign: gbox.ALIGN_CENTER,
			dx: gbox.getScreenW()/2, dy: 10,
			dw: 0, dh: 0
		});
		
		gbox.blitText(gbox.getBufferContext(), {
			font: "small",
			text: "Press A to reset",
			valign: gbox.ALIGN_BOTTOM,
			halign: gbox.ALIGN_CENTER,
			dx: gbox.getScreenW()/2, dy: 30,
			dw: 0, dh: 0
		});
		
		return;
	}
	
	// --- doing a player turn or updating, and we display the tiles for either

	// clear screen
	gbox.blitFade(gbox.getBufferContext(), {});
	
	// draw all the tiles
	for(row=0; row < BOARDSZ; ++row) {
		for(col=0; col < BOARDSZ; ++col) {
			// render individual tile
			gbox.blitTile(gbox.getBufferContext(), {
				tileset: this.tileset,
				tile: gameboard[row][col].frame,
				dx: offx + col * 32,
				dy: offy + row * 32,
				fliph: this.fliph,
				flipv: this.flipv,
				camera: this.camera,
				alpha: 1.0
			});
		}
	} // end for(all tiles)
	
	// render HUD stuff if we're not flipping tiles
	if(!isFlipping) {
		// the selection box
		gbox.blitTile(gbox.getBufferContext(), {
			tileset: "selectTile",
			tile: 0,
			dx: offx + selectcol * 32,
			dy: offy + selectrow * 32,
			fliph: false,
			flipv: false,
			camera: this.camera,
			alpha: 1.0
		});
		
		// indicate whose turn it is
		if(maingame.gameturn == GameTurn.Player1) {
			gbox.blitText(gbox.getBufferContext(), {
				font: "small-blue",
				text: "PLAYER 1",
				valign: gbox.ALIGN_BOTTOM,
				halign: gbox.ALIGN_CENTER,
				dx: gbox.getScreenW()/2 - 32, dy: 0,
				dw: 64, dh: 8
			});
		} else if(maingame.gameturn == GameTurn.Player2) {
			gbox.blitText(gbox.getBufferContext(), {
				font: "small-red",
				text: "PLAYER 2",
				valign: gbox.ALIGN_BOTTOM,
				halign: gbox.ALIGN_CENTER,
				dx: gbox.getScreenW()/2 - 32, dy: gbox.getScreenH()-8,
				dw: 64, dh: 8
			});
		}
	}
}


// initialise the game board
function addBoard() {
	// create the array of tiles
	gameboard = new Array(BOARDSZ);
	for(row=0; row < BOARDSZ; ++row) {
		gameboard[row] = new Array(BOARDSZ);
	}
	
	// initialise the game state
	ResetGame();
	
	// create the board as an Akihabara object
	gbox.addObject( {
		id: "boardid",
		group: "board",
		tileset: "diceTiles",
		initialize: function() {  // "constructor"
			offx = gbox.getScreenW() / 2 - BOARDSZ * 16;
			offy = gbox.getScreenH() / 2 - BOARDSZ * 16;
			toys.topview.initialize(this, {});
		},
		first: BoardUpdate,	// links to Update() and Render() functions
		blit: BoardRender
	}); // end addObject
}


// ---------------------------- MAIN HELPERS ---------------------------------
// ----------------------------------------------------------------------------

function DifficultyMenu(reset) {
	if(reset) {
		toys.resetToy(this,"mode");
		this.gamemode=null;
		
	} else {
		// populate the list of game modes
		var mainmenu={labels:[]};
		mainmenu.labels.push("2-PLAYER");
		mainmenu.labels.push("CPU EASY");
		mainmenu.labels.push("CPU MEDIUM");
		mainmenu.labels.push("CPU HARD");
		
		// fade the screen a bit
		gbox.blitFade(gbox.getBufferContext(), {alpha:0.5});
		
		if(this.gamemode==null) {
			// show the difficulty menu
			if(toys.ui.menu(this, "mode", {audiooption: "default-menu-option",
										   audioconfirm: "linedone1",
										   font: "small",
										   keys: {up:"up", down:"down", ok:"a", cancel:"b"},
										   selector: ">",
										   selected: 0,
										   items: mainmenu.labels,
										   x:10,y:10}))
			{
				if(-1 == toys.getToyValue(this, "difficulty", "ok")) {
					return -1;
				} else {
					switch(toys.getToyValue(this, "mode", "selected")) {
					case 0:	// 2-player
						maingame.gamemode = GameMode.TwoPlayer;
						break;
					case 1:	// EASY CPU
						maingame.gamemode = GameMode.Easy;
						break;
					case 2:	// MEDIUM CPU
						maingame.gamemode = GameMode.Medium;
						break;
					case 3:	// HARD CPU
						maingame.gamemode = GameMode.Hard;
						break;
					}
					return true;
				}
			}
		}
		
		return false;
	}
}


// ---------------------------- MAIN HELPERS ---------------------------------
// ----------------------------------------------------------------------------
function main() {
	gbox.setGroups(["board", "game"]);
	maingame = gamecycle.createMaingame("game", "game");

	// disable difficulty-choice menu
	//maingame.gameMenu = function() {return true;};
	maingame.gameMenu = DifficultyMenu;
      
	// disable "get ready" screen
	maingame.gameIntroAnimation = function() {return true;};

	// slide in our title logo
	maingame.gameTitleIntroAnimation=function(reset) {
		if(reset) {
			toys.resetToy(this, "rising");
		}

		gbox.blitFade(gbox.getBufferContext(), {alpha:1});
    
		toys.logos.linear(this, "rising", {
			image: "logo",
			sx:gbox.getScreenW()/2-gbox.getImage("logo").width/2,
			sy:gbox.getScreenH(),
			x:gbox.getScreenW()/2-gbox.getImage("logo").width/2,
			y:20,
			speed:4
		});
	};

	// create the game board
	maingame.initializeGame = function() {
		addBoard();
	};

	// play the game
	gbox.go();
}

