// ------------------------------- CONSTANTS ----------------------------------
// ----------------------------------------------------------------------------
var BOARDSZ = 6;
var GameStates = {"Player1": 0, "Player2": 1};


// ------------------------------- VARIABLES ----------------------------------
// ----------------------------------------------------------------------------
var maingame;	// Akihabara game construct
var gameboard;	// array containing tiles
var gamestate;	// whose turn is it, &c.
var gamewinner; // store the winner, when there is one
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: "bluefont",
		image: "font",
		firstletter: " ",
		tileh: 8, tilew: 8, tilerow: 255,
		gapx: 0, gapy: 32
	});
	gbox.addFont({
		id: "redfont",
		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(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
	gamestate = GameStates.Player1;
	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(gamestate == GameStates.Player1 && gameboard[selectrow][selectcol].owner != "p2") {
		updatelist.push(new TileToUpdate(selectrow, selectcol, "p1"));
		gamestate = GameStates.Player2;
		
	} else if(gamestate == GameStates.Player2 && gameboard[selectrow][selectcol].owner != "p1") {
		updatelist.push(new TileToUpdate(selectrow, selectcol, "p2"));
		gamestate = GameStates.Player1;
	}
}


// 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;
			
			gamewinner = CheckWinner();
			if(-1 != gamewinner) {
				isGameOver = true;
			}
		}
		
	} else {	// p1 or p2 are selecting/clicking
		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;
			}
		}
	}
}


// implement the Render() function
function BoardRender() {
	// clear screen
	gbox.blitFade(gbox.getBufferContext(), {});
	
	// if it's game over, just render a message and be done with it
	if(isGameOver) {
		var wintext;
		if(1 == gamewinner) {
			wintext = "PLAYER 1 WINS!";
		} else if(2 == gamewinner) {
			wintext = "PLAYER 2 WINS!";
		}
		
		gbox.blitText(gbox.getBufferContext(), {
			font: "small",
			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

	// 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(gamestate == GameStates.Player1) {
			gbox.blitText(gbox.getBufferContext(), {
				font: "bluefont",
				text: "PLAYER 1",
				valign: gbox.ALIGN_BOTTOM,
				halign: gbox.ALIGN_CENTER,
				dx: gbox.getScreenW()/2 - 32, dy: 0,
				dw: 64, dh: 8
			});
		} else if(gamestate == GameStates.Player2) {
			gbox.blitText(gbox.getBufferContext(), {
				font: "redfont",
				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 FUNCTION ---------------------------------
// ----------------------------------------------------------------------------
function main() {
	if(DOCONSOLE) console.log("MAIN I HOPE");

	gbox.setGroups(["board", "game"]);
	maingame = gamecycle.createMaingame("game", "game");

	// disable difficulty-choice menu
	maingame.gameMenu = function() {return true;};
      
	// 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();
}

