// Created by Joel Duggan (joelgduggan@gmail.com), May 2013

//
// CONSTANTS
//

var TOP_MARGIN  = 75,
    BOTTOM_MARGIN = 20,
	SIDE_MARGIN = 40,
	LINE_LENGTH = 64,
	LINE_WIDTH  = 5,
	DOT_RADIUS  = 5,
	TOUCH_CLOSE = 20;		// how close they need to be to activate line when mouse over


var PLAYER_LETTER = 'A';
var AI_LETTER     = 'B';

var BLACK            = 'rgb(0,0,0)';
var DOT_COLOR        = 'rgb(100, 100, 100)';
var TOUCH_COLOR      = 'rgba(255, 0, 0, 0.5)';
var LAST_LINE_COLOR  = 'rgb(0, 140, 0)';
var PLAYER_COLOR     = 'rgb(40, 40, 255)';
var AI_COLOR         = 'rgb(255, 100, 0)';
var TURN_ARROW_COLOR = 'rgb(255, 0, 255)';

// ai difficulty settings
var VERY_EASY = 0, EASY = 1, MEDIUM = 2, HARD = 3, VERY_HARD = 4;
var TIME_LIMIT  = [1000, 1000, 2000, 3000, 5000];		// in milliseconds
var DEPTH_LIMIT = [1, 2, 2, 3, 99];
var DUMB_EVAL   = [true, true, false, false, false];


//
// GLOBAL VARIABLES
//

var canvas;						// canvas html element
var ctx;						// 2d context to the canvas

var numDots;					// number of dots in in each row and column
var numLines;					// number of lines in each row and column
var gameStarted;				// true if their is a game being played
var gameBoard;					// holds current state of the game
var undoGameBoard;				// holds the state that will be returned to if undo is hit
var playerTurn;					// true if waiting for player to make a move
var aiDifficulty;				// predefined difficulty level
var touchLine;					// if < 0 then not touching anything
var lastAILine;					// the last line that the computer placed


oneTimeInitialize();

function oneTimeInitialize() {

	// attempt to get canvas context
	canvas = document.getElementById("canvas");

	if (canvas.getContext)
		ctx = canvas.getContext("2d");
	else
		alert("HTML5 Canvas not supported. Sorry.");

	// listeners
	document.onmousemove = onMouseMoved;
	document.onmousedown = onMouseDown;
	document.ontouchstart= onMouseMoved;
	document.ontouchend  = onMouseDown;

	gameStarted = false;
}

// called when the start game button is clicked on the page
function newGameInitialize() {

	if (gameStarted && undoGameBoard != null) {

		if (!confirm("Are you sure you want to end your current game?"))
			return;
	}

	// change grid to size selected on page
	changeGridSize(document.getElementById("gridSizeList").value);
	ctx.textBaseline = "top";

	// check difficulty setting
	if      (document.getElementById("diffVeryEasy").checked) aiDifficulty = VERY_EASY;
	else if (document.getElementById("diffEasy").checked)     aiDifficulty = EASY;
	else if (document.getElementById("diffMedium").checked)   aiDifficulty = MEDIUM;
	else if (document.getElementById("diffHard").checked)     aiDifficulty = HARD;
	else												      aiDifficulty = VERY_HARD;

	// create new empty board
	gameBoard = new Board();
	gameBoard.create(numDots, null, null);
	undoGameBoard = null;
	document.getElementById('undoButton').disabled = true;

	gameStarted   = true;
	touchingHoriz = false;
	touchingVert  = false;
	lastAILine    = null;

	// check to see who should go first
	if (document.getElementById("playerFirstButton").checked)
		playerTurn = true;
	else {

		playerTurn = false;
		requestAIMove();
	}

	drawGame();
}

function changeGridSize(newSize) {

	numDots = newSize;
	numLines = numDots - 1;
	canvas.width  = SIDE_MARGIN * 2 + LINE_LENGTH * numLines;
	canvas.height = TOP_MARGIN + BOTTOM_MARGIN + LINE_LENGTH * numLines;
}

function checkMouseTouch(mx, my) {

	// find mouse position relative to start of dots
	mx -= SIDE_MARGIN;
	my -= TOP_MARGIN;

	touchLine = -1;

	// out of bounds?
	if (mx < -TOUCH_CLOSE                            || my < -TOUCH_CLOSE ||
	    mx >= (LINE_LENGTH * numLines + TOUCH_CLOSE) || my >= (LINE_LENGTH * numLines + TOUCH_CLOSE))
		return;

	// x = distance to closest vert. line, y = dist to closest horiz. line
	x = mx % LINE_LENGTH;
	y = my % LINE_LENGTH;
	x = Math.min(x, LINE_LENGTH - x);
	y = Math.min(y, LINE_LENGTH - y);

	if (x >= TOUCH_CLOSE && y >= TOUCH_CLOSE)
		return;

	if ((x < TOUCH_CLOSE && y >= TOUCH_CLOSE) || (y < TOUCH_CLOSE && x < y)) {	// vert. line

		x = Math.round(mx / LINE_LENGTH);
		y = clamp(Math.floor(my / LINE_LENGTH), 0, numLines - 1);

		touchLine = (numLines * numDots) + x * numLines + y;
	}
	else  {		// horiz. line

		x = clamp(Math.floor(mx / LINE_LENGTH), 0, numLines - 1);
		y = Math.round(my / LINE_LENGTH);

		touchLine = x + y * numLines;
	}

	if (gameBoard.isLineSet(touchLine))		// is line already set?
		touchLine = -1;
}

function onMouseMoved(e) {
	if (!gameStarted) return;

	var mx = e.pageX - canvas.offsetLeft;
	var my = e.pageY - canvas.offsetTop;

	checkMouseTouch(mx, my);
	drawGame();
}

function onMouseDown(e) {
	if (!gameStarted) return;

	if (touchLine >= 0 && playerTurn == true) {

		// do player's move
		playerTurn    = false;
		document.getElementById('undoButton').disabled = false;
		undoGameBoard = gameBoard;
		gameBoard     = gameBoard.applyMove(touchLine, PLAYER_LETTER);
		touchLine     = -1;

		// do ai's move
		if (gameBoard.anotherMove == false)
			requestAIMove();
		else
			playerTurn = true;

		drawGame();
		checkGameOver();
	}

}

// this allows the page to redraw the canvas before ai blocks the thread
function requestAIMove() {
	setTimeout('doAIMove()', 200);
}

function doAIMove() {

	var aiMove = AIFindNextMove(gameBoard, TIME_LIMIT[aiDifficulty], DEPTH_LIMIT[aiDifficulty], DUMB_EVAL[aiDifficulty]);
	gameBoard  = gameBoard.applyMove(aiMove, AI_LETTER);
	lastAILine = aiMove;
	drawGame();

	if (gameBoard.anotherMove == true && gameBoard.isGameOver() == false)
		requestAIMove();
	else
		playerTurn = true;

	drawGame();
	checkGameOver();
}

function checkGameOver() {

	if (gameBoard.isGameOver()) {
		gameStarted = false;
		var players = gameBoard.numBoxesOwnedBy(PLAYER_LETTER);
		var comps   = gameBoard.numBoxesOwnedBy(AI_LETTER);

		if (players > comps)
			alert("You Won!");
		else if (comps > players)
			alert("Sorry. You lost.");
		else
			alert("Tie game.");

		drawGame();
	}
}

function undoLastMove() {
	if (undoGameBoard == null) return;

	gameBoard     = undoGameBoard;
	undoGameBoard = null;
	gameStarted   = true;
	document.getElementById('undoButton').disabled = true;
	drawGame();
}

function drawLine(lineNum, color) {

	ctx.strokeStyle = color;
	ctx.beginPath();

	if (lineNum < gameBoard.q) {		// horiz. lines

		var r = Math.floor(lineNum / numLines);
		var c = lineNum - r * numLines;

		ctx.moveTo(SIDE_MARGIN + LINE_LENGTH *  c,    TOP_MARGIN + LINE_LENGTH * r);
		ctx.lineTo(SIDE_MARGIN + LINE_LENGTH * (c+1), TOP_MARGIN + LINE_LENGTH * r);
	}
	else {

		lineNum -= gameBoard.q;
		var c = Math.floor(lineNum  / numLines);
		var r = lineNum - c * numLines

		ctx.moveTo(SIDE_MARGIN + LINE_LENGTH * c, TOP_MARGIN + LINE_LENGTH *  r);
		ctx.lineTo(SIDE_MARGIN + LINE_LENGTH * c, TOP_MARGIN + LINE_LENGTH * (r+1));
	}

	ctx.closePath();
	ctx.stroke();
}

function drawGame() {

	ctx.clearRect(0, 0, canvas.width, canvas.height);

	// draw filled boxes
	for (var r = 0; r < numLines; r++) {
		for (var c = 0; c < numLines; c++) {

			if (gameBoard.boxes[r][c].getCount() == 4) {

				if (gameBoard.boxes[r][c].ownedBy == PLAYER_LETTER)
					ctx.fillStyle = PLAYER_COLOR;
				else
					ctx.fillStyle = AI_COLOR;

				ctx.fillRect(SIDE_MARGIN + c * LINE_LENGTH, TOP_MARGIN + r * LINE_LENGTH, LINE_LENGTH, LINE_LENGTH);

			}
		}
	}

	// draw the dots
	ctx.fillStyle  = DOT_COLOR;
	for (var r = 0; r < numDots; r++) {
		for (var c = 0; c < numDots; c++) {
			ctx.beginPath();
			ctx.arc(SIDE_MARGIN + c * LINE_LENGTH, TOP_MARGIN + r * LINE_LENGTH, DOT_RADIUS, 0,2*Math.PI);
			ctx.fill();
		}
	}

	// draw line segments that are set
	ctx.lineWidth  = LINE_WIDTH;
	for (var i = 0; i < gameBoard.numLines; i++) {

		if (gameBoard.isLineSet(i)) {

			if (i != lastAILine)
				drawLine(i, BLACK);
			else
				drawLine(i, LAST_LINE_COLOR);
		}
	}

	// draw line segment that is currently selected
	if (touchLine >= 0)
		drawLine(touchLine, TOUCH_COLOR);

	// draw scores
	ctx.font = "bold 14px Arial";
	ctx.fillStyle = PLAYER_COLOR;
	ctx.fillText("Player", 9, 5);
	ctx.fillText(gameBoard.numBoxesOwnedBy(PLAYER_LETTER), 28, 25);
	ctx.fillStyle = AI_COLOR;
	ctx.fillText("Computer", canvas.width - 77, 5);
	ctx.fillText(gameBoard.numBoxesOwnedBy(AI_LETTER), canvas.width - 45, 25);

	// whose turn arrow
	if (gameStarted) {
		if (playerTurn)
			drawHorizArrow(90,                 12, 30, 10, 7, false, TURN_ARROW_COLOR, 2);
		else
			drawHorizArrow(canvas.width - 114, 12, 30, 10, 7, true,  TURN_ARROW_COLOR, 2);
	}
}

function drawHorizArrow(x, y, length, backX, backY, isRight, color, lineWidth) {

	if (!isRight) {
		length = -length;
		backX  = -backX;
	}

	ctx.lineCap  = "round";
	ctx.lineJoin = "round";

	ctx.beginPath();
	ctx.moveTo(x, y);
	ctx.lineTo(x + length, y);
	ctx.lineTo(x + length - backX, y - backY);
	ctx.moveTo(x + length, y);
	ctx.lineTo(x + length - backX, y + backY);
	ctx.closePath();

	ctx.strokeStyle = color;
	ctx.lineWidth   = lineWidth;
	ctx.stroke();
}

function clamp(num, low, high) {
	if (num < low)       return low;
	else if (num > high) return high;
	else                 return num;
}