/*
Chris Fox
8/23/08
Javascript for my Minesweeper knock-off, "Mines Ahoy."
*/

var tileLength;  // length of each square tile
var safeTiles = 0;  // number of tiles that are shrouded and mine-free
var cm;  // for CueManager object
var timer;  // Timer object

// determine if user is on an iphone
var agent = navigator.userAgent.toLowerCase();
var is_iphone = (agent.indexOf('iphone') != -1);
if (is_iphone) { 
	tileLength = 3;
} else {
	tileLength = 1.75;
}

$(document).ready(function(){
	// set up CueManager for toggling visual cues on buttons
	cm = new CueManager($("#controls").children().children(), [
		"flag_small.png",
		"arrow_up.png", 
		"arrow_up.png" 
	]);
	// UI event handlers
	$("#mode, #settings, #bestTimes").click(function(){ 
		cm.toggle($(this).attr("id")); 
	});
	// disable custom controls if custom is not checked when page loads
	$(".custom").attr("disabled", !$("#custom").attr("checked"));
	// difficulty radio buttons will disable/enable custom controls appropriately
	$(".difficulty").click(function() {
		$(".custom").attr("disabled", this.id != "custom");
	});
	$("#newGame").click(newGame);
	$("#settings, #bestTimes").click(function(){
		var id = $(this).attr("id");
		var other = $("#settings, #bestTimes").not("#" + id).attr("id");
		if ($("#" + id + "Panel:hidden").get(0)) {
			cm.reset(other);
			if ($("#" + other + "Panel:hidden").get(0)) {
				$("#" + id + "Panel").slideDown("fast");
			} else {
				$("#" + other + "Panel").slideUp("fast", function(){
					$("#" + id + "Panel").slideDown("fast");
				});
			}
		} else {
			cm.reset(id);
			$("#" + id + "Panel").slideUp("fast");
		}		
	});
	$("#submit").click(submitClick);
	// set up timer
	timer = new Timer($("#time"));
	newGame();
	$("#tables").load("bestTimes.php");
});

// SETTING UP A NEW GAME

function newGame() {
	if ($("#newTime:visible").get(0)) {
		$("#newTime").slideUp("fast");
	}
	$("#game").hide();
	timer.stop();
	var gameType = $(".difficulty[checked]").attr("id");
	if (gameType == "easy") {
		setupGame(9, 9, 10);
	} else if (gameType == "medium") {
		setupGame(16, 16, 40);
	} else if (gameType == "hard") {
		setupGame(30, 16, 99);
	} else {
		var c = $(".custom");
		setupGame(
			$(c[0]).attr("value"), 
			$(c[1]).attr("value"), 
			$(c[2]).attr("value")
		);
	}
}

// places tiles and mines
function setupGame(numColumns, numRows, numMines) {
	safeTiles = numRows * numColumns - numMines;
	$("#game div").remove();
	$("#game").width(tileLength * numColumns + "em");
	$("#game").height(tileLength * numRows + "em");
	for (var row=0; row < numRows; row++) {
		var y = row*tileLength + 0.5 + "em";
		for (var column=0; column < numColumns; column++) {
			$(document.createElement("div"))
				.append(document.createElement("span"))
				.css({ 	width: tileLength + "em", 
						height: tileLength + "em",
						top: y, 
						left: column*tileLength + 0.5 + "em" })
				.attr("id", "_" + row + "_" + column)
				.addClass("fog")
				.click(tileClick)
				.appendTo($("#game"))
				.children().css("vertical-align", -12 * tileLength +"%");
		}
	}
	$("#game").slideDown("slow");
	$(randomSample($("#game div").get(), numMines)).addClass("mine");
	timer.reset();
}

// TILE BEHAVIOR

// calls appropriate action based on the context of the click
function tileClick(event) {
	var tile = $(this);
	if (!timer.isStarted()) {
		timer.start();
	}
	if (!tile.hasClass("reveal")) {
		if (!cm.mode.img1On || event.shiftKey || event.ctrlKey) {
			tile.toggleClass("flag");
		} else if (!tile.hasClass("flag")) {
			if (tile.hasClass("mine")) {
				endGame();
				tile.removeClass("revealmine").addClass("explosion");
			} else {
				safeTileClick(tile);
				if (safeTiles == 0) { 
					endGame();
					checkTime();
				}
			}
		}
	}
}

// reveals this tile and surrounding safe tiles, 
// ending the game if all safe tiles have been revealed
function safeTileClick(tile) {
	safeTiles--;
	tile.removeClass("fog flag").addClass("reveal");
	var neighbors = adjacentTiles(tile[0]);
	var mines = neighbors.filter(".mine").length;
	if (mines == 0) {
		neighbors.each(function() {
			if (!$(this).hasClass("reveal")) {
				safeTileClick($(this));
			}
		});
	} else {
		$("span", tile[0]).text("" + mines);	
	}
}

// returns a jQuery object containing the tiles adjacent to the given one
function adjacentTiles(tileElement) {
	var position = tileElement.id.split("_");
	var row = parseInt(position[1]);
	var column = parseInt(position[2]);
	return $("#_" + (row-1) + "_" + (column-1))  // top left
		.add($("#_" + (row-1) + "_" + column))  // top center
		.add($("#_" + (row-1) + "_" + (column+1)))  // top right
		.add($("#_" + row + "_" + (column-1)))  // center left
		.add($("#_" + row + "_" + (column+1)))  // center right
		.add($("#_" + (row+1) + "_" + (column-1)))  // bottom left
		.add($("#_" + (row+1) + "_" + column))  // bottom center
		.add($("#_" + (row+1) + "_" + (column+1)));  // bottom right
}

// reveal the board and stop timer
function endGame() {
	timer.stop();
	$("#game div").removeClass("flag fog").addClass("reveal");
	$(".mine").addClass("revealmine");
}

// BEST TIMES

function checkTime() {
	$.ajax({
		url: "checkTime.php",
		data: {time: timer.time, settings: getSettings()},
		success: checkTimeHelper,
		dataType: "text"
	});
}

function checkTimeHelper(data) {
	if (data) {
		$("#newTime span").text(timer.time + "");
		if ($("#bestTimesPanel:hidden").get(0)) {
			$("#newTime").show();
			$("#bestTimes").click();
		} else {
			$("#newTime").slideDown("fast");	
		}
	}
}

function submitClick() {
	$("#tables").load("bestTimes.php", {
		time: timer.time, 
		name: $("#name").attr("value"),
		motto: $("#motto").attr("value"),
		settings: getSettings()
	});
	$("#newTime").slideUp("fast");
}

function getSettings() {
	var settings = $(".difficulty[checked]").attr("id");
	if (settings == "custom") {
		$(".custom").each(function(){
			settings += "-" + $(this).attr("value");
		});
	}
	return settings;
}

// CUE MANAGER

// post: Toggles visual cues for buttons and holds current state of cues.
// pre:  FirstImages should be a jQuery or array containing the image elements,
//       while altImages should be an array of image urls for toggling.
function CueManager(imageElements, altImages) {
	for (var i=0; i < imageElements.length; i++) {
		this[$(imageElements[i]).parent().attr("id")] = {
			element: imageElements[i],
			img1: $(imageElements[i]).attr("src"),
			img2: altImages[i],  // clearly, the arrays need to be of equal length
			img1On: true
		}
	}
}

// toggles button appearence and cue state
CueManager.prototype.toggle = function(buttonID) {
	var data = this[buttonID];
	if (data.img1On) {
		$(data.element).attr("src", data.img2);
	} else {
		$(data.element).attr("src", data.img1);
	}
	data.img1On = !data.img1On;
}

CueManager.prototype.reset = function(buttonID) {
	var data = this[buttonID];
	$(data.element).attr("src", data.img1);
	data.img1On = true;
}

// RANDOM SAMPLE

// Pre:  sampleSize <= population.length
// Post: Returns an array of elements chosen randomly from given array
function randomSample(population, sampleSize) {
	var sample = [];
	for (var i=0; i < sampleSize; i++) {
		var randomIndex = Math.floor(Math.random() * population.length);
		sample.push(population[randomIndex]);
		population.splice(randomIndex, 1);
	}
	return sample;
}

// TIMER

// Creates a Timer, using given DOM element to display time
function Timer(element) {
	this.time = 0;
	this.interval = 0;
	this.timerElement = element;
	this.status = false;
}

Timer.prototype.start = function() {
	this.interval = setInterval(function(t) {
			t.time++;
			t.timerElement.text(t.time);
	}, 1000, this);
	this.status = true;
}

Timer.prototype.stop = function() {
	clearInterval(this.interval);
	this.status = false;
}

Timer.prototype.isStarted = function() {
	return this.status;
}

Timer.prototype.reset = function() {
	this.time = 0;
	this.timerElement.text("0");
}