////////////////////////////////////////
// WORDSEARCH - RAZZLE
////////////////////////////////////////
var Wordsearch = function() {
	var Req, 
	    Data, 
	    permCanvas, 
	    tempCanvas, 
	    canvasWidth, 
	    canvasHeight, 
	    startX, 
	    startY, 
	    endX, 
	    endY, 
	    posOffset, 
	    rowMax, 
	    colMax, 
	    cellWidth, 
	    cellHeight, 
	    cellCenterOffset, 
	    ReturnObj = {
	    	failCount : 0
	    };
	
	/////////////////////////////////////////////////////////////////////
	var init = function() {
		if (!renderCanvas(1)) return;
		
		$("fl-reset").addEvent("click", function(event) {
			Wordsearch.resetBoard();
			event.stop();
			return false;
		});
		$("fl-newgame").addEvent("click", function(event) {
			Wordsearch.gutBoard();
			Wordsearch.getWordsearch();
			if (!!event) { event.stop(); }
			return false;
		});
		$("fl-print").addEvent("click", function(event) {
			Razzle.renderForPrint();
			event.stop();
			return false;
		});
		
		if (Razzle.puzzleJSON.length > 0) {
			parse(Razzle.puzzleJSON[0], !Browser.Engine.presto);
		} else {
			getWordsearch();
		}
	};
	
	/////////////////////////////////////////////////////////////////////
	var getWordsearch = function() {
		Req = new Request.JSON({ url : "/getWordsearch/" + new Date().getTime(), method : "GET", secure : false });
		Req.addEvent("onComplete", function(wordsearch) {
			if (!!wordsearch && wordsearch.length > 0) {
				Wordsearch.parse(wordsearch[0], !Browser.Engine.presto);
			} else if (Wordsearch.failCount < 11) {
				Wordsearch.failCount++;
				Wordsearch.getWordsearch();
			}
		});
		Req.get();
	};
	
	/////////////////////////////////////////////////////////////////////
	var parse = function(Fields, fadeIn) {
		Data = Fields;
		if (!Data.word_list.length || Data.word_list.length < 1) {
			Wordsearch.failCount++;
			if (Wordsearch.failCount < 11) {
				getWordsearch();
			}
			return;
		}
		
		if (fadeIn) {
			$("puzzleBoard").setStyles({ "opacity" : 0.0, "visibility" : "hidden" });
		}
		
		// Generate the word list table:
		if (!!$("wsWordsTable")) {
			$("wsWordsTable").dispose();
		}
		
		Data.words = Data.word_list.split(",").sort();
		var html = ["<TABLE id='wsWordsTable'><TBODY><TR>"], 
		    htmlL = 1, 
		    rowCount = Math.ceil(Data.words.length / 4), 
		    cell = "<TD><A id='wsWord-#WORD#' title='Click for the definition (popup)' " + 
		    	"href='http://dictionary.reference.com/browse/#WORD#' class='wsWordLink' target='_blank'>#WORD#</A></TD>";
		
		for (var i=0, len=Data.words.length; i<len; i++) {
			if (i % rowCount === 0 && i !== 0) {
				html[htmlL++] = "</TR><TR>";
			}
			html[htmlL++] = cell.replace(/#WORD#/g, Data.words[i]);
		}
		html[htmlL++] = "</TR></TBODY></TABLE>";
		
		$("puzDescExtra").set("html", html.join(""));
		
		// Generate the word search grid:
		Data.grid = Data.puzzle_grid.split("|");
		html = ["<TABLE id='wsTable'><TBODY>"];
		htmlL = 1;
		for (var i=0, len=Data.grid.length; i<len; i++) {
			Data.grid[i] = Data.grid[i].split(",");
			html[htmlL++] = "<TR><TD>" + Data.grid[i].join("</TD><TD>") + "</TD></TR>";
		}
		html[htmlL++] = "</TBODY></TABLE>";
		$("puzzleBoard").set("html", html.join(""));
		
		rowMax = Data.grid.length - 1;
		colMax = (rowMax > 0) ? Data.grid[0].length - 1 : 0;
		renderCanvas(2);
		
		if (fadeIn) {
			$("puzzleBoard").fade(1.0);
		}
		
		$(tempCanvas).addEvent("mousedown", Wordsearch.initOutline);
		Razzle.setStatTimer((new Date()).getTime());
	};
	
	/////////////////////////////////////////////////////////////////////
	var renderCanvas = function(phase) {
		if (phase === 1) {
			try {
				tempCanvas = document.createElement("canvas");
				tempCanvas.id = "wordsearchCanvas";
				if (Browser.Engine.trident) {
					G_vmlCanvasManager.initElement(tempCanvas);
				}
				var ctx = tempCanvas.getContext("2d");
				
				permCanvas = document.createElement("canvas");
				permCanvas.id = "wsPermCanvas";
				if (Browser.Engine.trident) {
					G_vmlCanvasManager.initElement(permCanvas);
				}
				
				return true;
			} catch (e) {
				Razzle.showMessage("Sorry, but this page requires technology that this browser does not have. Please try another browser.", false);
				Razzle.shutdownFunctions(false);
				return false;
			}
		} else if (phase === 2) {
			var coords = $("wsTable").getCoordinates("puzzleBoard");
			if (Browser.Engine.gecko18) { // Firefox 2 does not report $("wsTable").offsetWidth correctly
				var g18FirstCell = $("wsTable").tBodies[0].rows[0].cells[0], 
				     g18Columns = $("wsTable").tBodies[0].rows[0].cells.length, 
				     g18CellWidth = g18FirstCell.offsetWidth, 
				     g18Rows = $("wsTable").tBodies[0].rows.length, 
				     g18CellHeight = g18FirstCell.offsetHeight;
				
				coords.width = g18CellWidth * g18Columns;
				coords.height = g18CellHeight * g18Rows;
				coords.left = Math.floor(($("puzzleBoard").offsetWidth - (g18CellWidth * g18Columns)) / 2.0);
			}
			canvasWidth = coords.width;
			canvasHeight = coords.height;
			
			if (Browser.Engine.trident) {
				tempCanvas.style.width = canvasWidth + "px";
				tempCanvas.style.height = canvasHeight + "px";
				permCanvas.style.width = canvasWidth + "px";
				permCanvas.style.height = canvasHeight + "px";
			} else if (Browser.Engine.presto925) {
				tempCanvas.width = canvasWidth;
				tempCanvas.height = canvasHeight;
				permCanvas.width = canvasWidth;
				permCanvas.height = canvasHeight;
			} else {
				tempCanvas.setAttribute("width", canvasWidth);
				tempCanvas.setAttribute("height", canvasHeight);
				permCanvas.setAttribute("width", canvasWidth);
				permCanvas.setAttribute("height", canvasHeight);
			}
			var selBlocker = new Element("DIV", {
				"id" : "selectionBlocker",
				"styles" : { "width" : canvasWidth, "height" : canvasHeight, "opacity" : 0.01 }
			});
			
			if (!Browser.Engine.trident4 && !Browser.Engine.trident5) {
				tempCanvas.style.marginLeft = coords.left + "px";
				permCanvas.style.marginLeft = coords.left + "px";
				selBlocker.style.marginLeft = coords.left + "px";
			}
			
			$("puzzleBoard").insertBefore(selBlocker, $("wsTable"));
			$("puzzleBoard").insertBefore(permCanvas, $("wsTable"));
			$("puzzleBoard").insertBefore(tempCanvas, $("wsTable"));
			
			var sample = $("wsTable").tBodies[0].rows[0].cells[0];
			cellWidth = sample.offsetWidth + ((Browser.Engine.trident && !window.XDomainRequest) ? 1 : 0);
			cellHeight = sample.offsetHeight + ((Browser.Engine.trident && !window.XDomainRequest) ? 1 : 0);
			cellCenterOffset = Math.round(cellWidth / 2.0);
		}
	};
	
	/////////////////////////////////////////////////////////////////////
	var initOutline = function(event) {
		document.addEvents({
			"mousemove" : Wordsearch.dragOutline,
			"mouseup" : Wordsearch.parseOutline
		});
		
		posOffset = $("wsTable").getPosition();
		if (Browser.Engine.gecko18) {
			posOffset.x += 70; // FUBAR IN FF2 - TODO: FIX
		}
		startX = (Math.floor((event.page.x - posOffset.x) / cellWidth) * cellWidth) + cellCenterOffset;
		startY = (Math.floor((event.page.y - posOffset.y) / cellHeight) * cellHeight) + cellCenterOffset;
		endX = 0;
		endY = 0;
		
		if (Browser.Engine.presto925) { // Stop text selection in Opera 9.25
			event.stop();
			return false;
		}
	};
	
	/////////////////////////////////////////////////////////////////////
	var dragOutline = function(event) {
		var row = Math.floor((event.page.x - posOffset.x) / cellWidth), 
		    col = Math.floor((event.page.y - posOffset.y) / cellHeight);
		
		row = (row < rowMax) ? ((row < 0) ? 0 : row) : rowMax;
		col = (col < colMax) ? ((col < 0) ? 0 : col) : colMax;
		endX = (row * cellWidth) + cellCenterOffset;
		endY = (col * cellHeight) + cellCenterOffset;
		
		renderOutline(false);
		
		if (!!window.getSelection) {
			window.getSelection().removeAllRanges();
		} else if (!!document.selection) {
			document.selection.empty();
		}
	};
	
	/////////////////////////////////////////////////////////////////////
	var parseOutline = function(event) {
		document.removeEvents({
			"mousemove" : Wordsearch.dragOutline,
			"mouseup" : Wordsearch.parseOutline
		});
		
		var ctx = tempCanvas.getContext("2d"), 
		    xCoords = [parseInt((startX - cellCenterOffset) / cellWidth, 10), parseInt((endX - cellCenterOffset) / cellWidth, 10)], 
		    yCoords = [parseInt((startY - cellCenterOffset) / cellHeight, 10), parseInt((endY - cellCenterOffset) / cellHeight, 10)], 
		    temp, 
		    word1 = "", 
		    word2 = "";
		
		ctx.clearRect(0, 0, canvasWidth, canvasHeight);
		
		switch (computeAngle(1)) {
			case 0.0: // Vertical
				if (yCoords[0] > yCoords[1]) {
					temp = yCoords[0]; yCoords[0] = yCoords[1]; yCoords[1] = temp;
				}
				for (var i=yCoords[0]; i<=yCoords[1]; i++) word1 += Data.grid[i][xCoords[0]];
				break;
			case (Math.PI / 4): // Diagonal
				if (xCoords[0] > xCoords[1]) {
					temp = xCoords[0]; xCoords[0] = xCoords[1]; xCoords[1] = temp;
					temp = yCoords[0]; yCoords[0] = yCoords[1]; yCoords[1] = temp;
				}
				if (yCoords[0] < yCoords[1]) {
					for (var i=0, len=yCoords[1]-yCoords[0]; i<=len; i++) word1 += Data.grid[yCoords[0] + i][xCoords[0] + i];
				} else {
					for (var i=0, len=yCoords[0]-yCoords[1]; i<=len; i++) word1 += Data.grid[yCoords[0] - i][xCoords[0] + i];
				}
				break;
			case (Math.PI / 2): // Horizontal
				if (xCoords[0] > xCoords[1]) {
					temp = xCoords[0]; xCoords[0] = xCoords[1]; xCoords[1] = temp;
				}
				for (var i=xCoords[0]; i<=xCoords[1]; i++) word1 += Data.grid[yCoords[0]][i];
				break;
			default:
				return;
		}
		word2 = word1.split("").reverse().join("");
		
		if (Data.words.contains(word1) || Data.words.contains(word2)) {
			var foundWord = (Data.words.contains(word1)) ? word1 : word2; // Assumes never both
			Data.words.erase(foundWord);
			$("wsWord-" + foundWord).setStyle("text-decoration", "line-through");
			renderOutline(true);
			
			if (Data.words.length === 0) {
				Razzle.updateStat("wordsearch");
				Razzle.showMessage("Congratulations, you've solved the puzzle!", true);
			}
		}
	};
	
	/////////////////////////////////////////////////////////////////////
	var renderOutline = function(asPermanent) {
		if (asPermanent) {
			var ctx = permCanvas.getContext("2d");
		} else {
			var ctx = tempCanvas.getContext("2d");
			ctx.clearRect(0, 0, canvasWidth, canvasHeight);
		}
		ctx.strokeStyle = "#224fa2";
		var radius = cellCenterOffset - 2;
		
		if (startY >= endY) {
			if (startX <= endX) {
				// Quandrant 1:
				var angle = computeAngle(1), 
				    xOffset = radius * Math.cos(angle), 
				    yOffset = radius * Math.sin(angle);
				
				ctx.beginPath();
				ctx.arc(startX, startY, radius, angle, angle + Math.PI, false);
				ctx.lineTo(endX - xOffset, endY - yOffset);
				ctx.arc(endX, endY, radius, angle + Math.PI, angle + (2 * Math.PI), false);
				ctx.lineTo(startX + xOffset, startY + yOffset);
				ctx.stroke();
			} else { // startX > endX
				// Quandrant 2:
				var angle = computeAngle(2), 
				    xOffset = radius * Math.sin(angle), 
				    yOffset = radius * Math.cos(angle);
				
				ctx.beginPath();
				ctx.arc(startX, startY, radius, angle - (0.5 * Math.PI), angle + (0.5 * Math.PI), false);
				ctx.lineTo(endX - xOffset, endY + yOffset);
				ctx.arc(endX, endY, radius, angle + (0.5 * Math.PI), angle + (1.5 * Math.PI), false);
				ctx.lineTo(startX + xOffset, startY - yOffset);
				ctx.stroke();
			}
		} else { // startY < endY
			if (startX > endX) {
				// Quandrant 3:
				var angle = computeAngle(3), 
				    xOffset = radius * Math.cos(angle), 
				    yOffset = radius * Math.sin(angle);
				
				ctx.beginPath();
				ctx.arc(startX, startY, radius, angle + Math.PI, angle + (2 * Math.PI), false);
				ctx.lineTo(endX + xOffset, endY + yOffset);
				ctx.arc(endX, endY, radius, angle, angle + Math.PI, false);
				ctx.lineTo(startX - xOffset, startY - yOffset);
				ctx.stroke();
			} else { // startX <= endX
				// Quandrant 4:
				var angle = computeAngle(4), 
				    xOffset = radius * Math.sin(angle), 
				    yOffset = radius * Math.cos(angle);
				
				ctx.beginPath();
				ctx.arc(startX, startY, radius, angle + (0.5 * Math.PI), angle + (1.5 * Math.PI), false);
				ctx.lineTo(endX + xOffset, endY - yOffset);
				ctx.arc(endX, endY, radius, angle - (0.5 * Math.PI), angle + (0.5 * Math.PI), false);
				ctx.lineTo(startX - xOffset, startY + yOffset);
				ctx.stroke();
			}
		}
	};
	
	/////////////////////////////////////////////////////////////////////
	var computeAngle = function(quadrant) {
		// Quadrant:
		//      |
		//  2   |   1
		// -----|-----
		//  3   |   4
		//      |
		
		var a = Math.abs(startY - endY), 
		    b = Math.abs(startX - endX);
		
		return (quadrant === 1 || quadrant === 3) ? Math.atan2(b, a) : Math.atan2(a, b);
	};
	
	/////////////////////////////////////////////////////////////////////
	var resetBoard = function() {
		if (!!tempCanvas) {
			tempCanvas = $(tempCanvas).removeEvent("mousedown", Wordsearch.initOutline).destroy();
			permCanvas = $(permCanvas).destroy();
			$("selectionBlocker").destroy();
			renderCanvas(1);
		}
		parse(Data, false);
		$("messageScreen").setStyle("display", "none");
		$("messageBox").setStyle("display", "none").set("html", "");
		Razzle.setStatTimer(-1);
	};
	
	/////////////////////////////////////////////////////////////////////
	var gutBoard = function() {
		if (!!tempCanvas) {
			tempCanvas = $(tempCanvas).removeEvent("mousedown", Wordsearch.initOutline).destroy();
			permCanvas = $(permCanvas).destroy();
			$("selectionBlocker").destroy();
			renderCanvas(1);
		}
		Wordsearch.failCount = 0;
		Data = {};
		$("messageScreen").setStyle("display", "none");
		$("messageBox").setStyle("display", "none").set("html", "");
	};
	
	ReturnObj.init = init;
	ReturnObj.getWordsearch = getWordsearch;
	ReturnObj.parse = parse;
	ReturnObj.renderCanvas = renderCanvas;
	ReturnObj.initOutline = initOutline;
	ReturnObj.dragOutline = dragOutline;
	ReturnObj.parseOutline = parseOutline;
	ReturnObj.renderOutline = renderOutline;
	ReturnObj.computeAngle = computeAngle;
	ReturnObj.resetBoard = resetBoard;
	ReturnObj.gutBoard = gutBoard;
	
	return ReturnObj;
}();
