/*
 * (c) 2008 David Harvey. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the 
 *      following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
 *      the following disclaimer in the documentation and/or other materials provided with the distribution.
 *   3. The name of the author may not be used to endorse or promote products derived from this software without 
 *      specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * www.davethehat.com
*/

var game = null;
var gameSize = 8;
var numAtoms = 4;

var title         = "Black Box";
var welcome       = "Welcome to Black Box, the classic puzzle game of logic and deduction!";
var newGameText   = "OK, one more game...";
var resetText     = "Against the laws of physics, but here goes...";

var SURPRESS_TEXT = {};
var GAME_MODE = {
	ACTIVE: {},
	SCORED: {}
}
var gameMode = GAME_MODE.ACTIVE;

var Sounds = {};
var SoundData = [
	{	id: "click",
		multiShot: true,
		url: "media/click-low.mp3"
	},
	{	id: "laser",
		multiShot: true,
		url: "media/laser.mp3"
	},
	{	id: "rebound",
		multiShot: true,
		url: "media/plinger03.mp3"
	},
	{	id: "absorb",
		multiShot: true,
		url: "media/robonoise_20.mp3"
	},
];

var soundEnabled = true;

soundManager.debugMode   = false;
soundManager.useConsole  = true;
soundManager.consoleOnly = true;
soundManager.url         = 'lib/soundManager/soundmanager2.swf';

soundManager.onload = function(){
	SoundData.forEach(function(soundDef) {
		Sounds[soundDef.id] = soundManager.createSound(soundDef);
	})	
	animateText(welcome, $("#game-text"));
}

$(document).ready(function () {
	$("#title").hide().text(title);
	$("#title").fadeIn(1000);
	
	["top","left","right","bottom"].forEach(function (port) {
		var ports = $('<table id="' + port + 'ports" class="port-panel"></table>');
		ports.appendTo($('#' + port));
		if (port == "top" || port == "bottom") {
			var row = $("<tr></tr>");
			for (var portx = 1; portx < gameSize+1; portx++) {
				var porty = port == "top" ? 0 : gameSize + 1;
				row.append($('<td class="port" x="' + portx +'" y="' + porty + '">?</td>'));
			}
			ports.append(row);
		} else {
			for (var porty = 1; porty < gameSize+1; porty++) {
				var portx = port == "left" ? 0 : gameSize + 1;
				ports.append($('<tr><td class="port"  x="' + portx +'" y="' + porty + '">&nbsp;?&nbsp;</td></tr>'));
			}
		}
	});
	
	$("#board").append($('<table id="cells"></table>'));
	for (var y = 1; y <= gameSize; y++) {
		var row = $("<tr></tr>");
		for (var x = 1; x <= gameSize; x++) {
			var cell = $('<td class="cell" x="'+ x + '" y="' + + y + '"></td>');
			row.append(cell);
		}
		$("#cells").append(row);
	}
	
	$(".cell").click(function () {
		if (gameMode != GAME_MODE.ACTIVE) return;
		var cell = getGameObj($(this));
		if (cell.isGuess()) {
			cell.setGuess(false);
		} else {
			cell.setGuess(true);
		}
	});

	newGame(SURPRESS_TEXT);	
});

function firedPortEnter() {
	$(this).addClass("port-fired-hover");
	var port = getGameObj($(this));	
	var dest = port.getDestination();
	if (!port.isAbsorbed() && !port.isReflected()) {
		getPortView(dest.getX(), dest.getY()).addClass("port-fired-hover");
	}
	if (gameMode == GAME_MODE.SCORED) {
		var path = port.getPath();
		setRayGraphics(path,false);
		playGameSound(getSoundForPort(port));
	}
}

function firedPortLeave() {
	$(this).removeClass("port-fired-hover");
	var port = getGameObj($(this));	
	var dest = port.getDestination();
	if (!port.isAbsorbed() && !port.isReflected()) {
		getPortView(dest.getX(), dest.getY()).removeClass("port-fired-hover");
	}
	if (gameMode == GAME_MODE.SCORED) {
		var path = port.getPath();
		setRayGraphics(path,true);
	}
}

function portEnter() {
	$(this).text("");
	$(this).addClass("port-hover");
}

function portLeave() {
	$(this).text("?");
	$(this).removeClass("port-hover");
}

function portClick() {
	if (gameMode != GAME_MODE.ACTIVE) return;
	var port = getGameObj($(this));
	port.fire();
}

function getPortView(x,y) {
	return $(".port[@x="+x + "][@y="+y+"]");
}

function getCellView(x,y) {
	return $(".cell[@x="+x + "][@y="+y+"]");
}

function getGameObj(element) {
	return game.cellAt(element.attr("x"),element.attr("y"));	
}

function newGame(surpressText) {
	game = new BlackBox(gameSize,numAtoms);
	game.addObserver(BlackBox.events.GUESS_CHANGED, guessChanged);
	game.addObserver(BlackBox.events.PORT_FIRED, portFired);
	if (!surpressText) {
		animateText(newGameText, $("#game-text"))
	}
	reset(SURPRESS_TEXT);
}

function reset(surpressText) {
	game.reset();
	$(".cell").removeClass("guess")
		.removeClass("show")
		.removeClass("bad-guess");
	$(".cell").text("");

	$(".port").removeClass("port-fired");
	$(".port").addClass("port-free")
	$(".port").text(" ? ");
	
	$(".port-free").unbind();
	$(".port-free").hover(portEnter, portLeave);
	$(".port-free").click(portClick);
	
	updateTries();
	updateGuesses();
	gameMode = GAME_MODE.ACTIVE;
	if (!surpressText) {
		animateText(resetText, $("#game-text"))
	}
}

function guessChanged(src, eventType) {
	var view = getCellView(src.getX(), src.getY());
	if (src.isGuess()) {
		view.addClass("guess");
	} else {
		view.removeClass("guess");
	}
	updateGuesses();
}

function portFired(src, eventType) {
	var port = src;
	var text = "" + port.getTrial();
	var dest = port.getDestination();
	if (port.isAbsorbed()) {
		text += "(A)";
	} else if (port.isReflected()) {
		text += "(R)";
	}
	
	var view = getPortView(port.getX(), port.getY());
	view.unbind();
	view.removeClass("port-hover");
	view.removeClass("port-free");
	view.text(text);
	view.addClass("port-fired");
	view.addClass("port-fired-hover");
	view.hover(firedPortEnter,firedPortLeave);
	playGameSound(getSoundForPort(port));
	
	updateTries();
}

function getSoundForPort(port) {
	var dest = port.getDestination();
	return port.isAbsorbed() ? "absorb" 
		 : port.isReflected()  ? "rebound"
		 : "laser";
}

function updateTries() {
	var tries = game.getTries();
	$("#num-trials").text(tries ? tries : "No");
	$("#txt-trials").text(tries == 1 ? "trial" : "trials");	
}

function updateGuesses() {
	var guesses = game.countGuesses();
	$("#num-guesses").text(guesses ? guesses : "No");
	$("#txt-guesses").text(guesses == 1 ? "posit" : "posits");	
}


function reveal() {
	if (gameMode != GAME_MODE.ACTIVE) return;
	
	gameMode = GAME_MODE.SCORED;
	var score = game.getScore();
	var scoretext = "You scored " + score.score + " (for "
					+ score.correct + " correct deductions, "
					+ score.wrongGuesses + " incorrect deductions and "
					+ score.undiscovered + " undiscovered atoms in "
					+ score.tries + (score.tries == 1 ? " trial)" : " trials)");
					
	animateText(scoretext, $("#game-text"));
	
	$(".cell").each(function () {
		var cell = getGameObj($(this));
		if (cell.isGuess()) {
			if (cell.isAtom()) {
				$(this).addClass("show");
				$(this).text("OK");
			} else {
				$(this).addClass("bad-guess");
				$(this).text("X");
			}
		} else if(cell.isAtom()) {
			$(this).addClass("show");
		}
	});
}

function TextAnimator(text, element) {
	this.init(text, element);
}

TextAnimator.prototype = {
	init: function(text, element) {
		this.stop = false;
		var self = this;
		var count = 0;
		setTimeout(function() {
			if (self.stop) return;
			element.text(text.substr(0,count));
			playGameSound("click");	
			if (count++ < text.length) {
				setTimeout(arguments.callee, self._getInterval());
			}		
		}, self._getInterval());
	},

	kill: function() {
		this.stop = true;
	},

	_getInterval: function() { return Math.ceil(Math.random() * 3) * 75; },
}

TextAnimator.animators = {};

function animateText(text, element) {
	if (TextAnimator.animators[element]) {
		TextAnimator.animators[element].kill();
	}
	TextAnimator.animators[element] = new TextAnimator(text, element);
}

function playGameSound(id) {
	if (soundEnabled) {
		Sounds[id].play();
	}
}

function toggleSound() {
	soundEnabled = !soundEnabled;
	$("#toggle-sound").text(soundEnabled ? "Sound off" : "Sound on");
}

function setRayGraphics(path,reset) {
	path.getElements().forEach(function(pathElement) {
		if (!pathElement.cell.isCell()) return;
		var cellClass = getRayCellClass(pathElement);
		var cellView = getCellView(pathElement.cell.getX(), pathElement.cell.getY());
		if (reset) {
			cellView.removeClass(cellClass);
		} else {
			cellView.addClass(cellClass);
		}
	});
}

var RayCellClassMap = {
	_1_0  : {
		_0_0  : "ray-absorb-w",
		_1_0  : "ray-straight-e-w",
		_0_1  : "ray-bend-w-s",
		_m1_0 : "ray-reflect-w",
		_0_m1 : "ray-bend-n-w",
	},
	_0_1  : {
		_0_0  : "ray-absorb-n",
		_1_0  : "ray-bend-e-n",
		_0_1  : "ray-straight-n-s",
		_m1_0 : "ray-bend-n-w",
		_0_m1 : "ray-reflect-n",		
	},
	_m1_0 :	{
		_0_0  : "ray-absorb-e",
		_1_0  : "ray-reflect-e",
		_0_1  : "ray-bend-s-e",
		_m1_0 : "ray-straight-e-w",
		_0_m1 : "ray-bend-e-n",		
	},
	_0_m1 : {
		_0_0  : "ray-absorb-s",
		_1_0  : "ray-bend-s-e",
		_0_1  : "ray-reflect-s",
		_m1_0 : "ray-bend-w-s",
		_0_m1 : "ray-straight-n-s",		
	},
}

function getRayCellClass(pathElement) {
	var key1 = makeVelocityKey(pathElement.entryV);
	var key2 = makeVelocityKey(pathElement.exitV);
	return RayCellClassMap[key1][key2];
}

function makeVelocityKey(v) {
	var e1 = v[0][0] < 0 ? "m" + -v[0][0] : v[0][0];
	var e2 = v[1][0] < 0 ? "m" + -v[1][0] : v[1][0];
	return "_" + e1 + "_" + e2;
}

function showHelp() {
	var w = window.open("BlackBoxHelp.html",
		"How to play Black Box",
		"width=400,height=600,status=no,scrollbars=yes");
}

