var start = [-1,-1];
var goal = [-1,-1];
var walls = new Array();
var field = new Array();
var ROWS = 0, COLS = 0;
var goal_distance = new Array();
Event.observe(window, "load", function(){	
	ROWS = $("field").getAttribute("alt");
	COLS = $("field").getAttribute("rel");
	Sortable.create("field", {tag: "li", onChange: function(n){
		var cy = n.getAttribute("alt");
		var cx = n.getAttribute("rel");
		n.setAttribute("alt", parseInt(n.nextSibling.getAttribute("alt")));
		if(parseInt(n.nextSibling.getAttribute("rel"))-1 > 0)
			n.setAttribute("rel", parseInt(n.nextSibling.getAttribute("rel"))-1);
		else n.setAttribute("rel", "0");
		n.setAttribute("id", "_"+n.getAttribute("alt")+n.getAttribute("rel"));
		if(cx == start[1] && cy == start[0]){
			echo("Dragging start!", true);
			start[1] = parseInt(n.nextSibling.getAttribute("rel"))-1;
			start[0] = parseInt(n.nextSibling.getAttribute("alt"));
			$("_"+start[1]+start[0]).setAttribute("alt", start[0]);
			$("_"+start[1]+start[0]).setAttribute("rel", start[1]);
		}
		if(cx == goal[1] && cy == goal[0]){
			echo("Dragging goal!", true);
			goal[1] = parseInt(n.nextSibling.getAttribute("rel"))-1;
			goal[0] = parseInt(n.nextSibling.getAttribute("alt"));
			$("_"+goal[1]+goal[0]).setAttribute("alt", goal[1]);
			$("_"+goal[1]+goal[0]).setAttribute("rel", goal[0]);
		}
		//astar();
	}});
	function toggle_about(){ var tog = new Effect.toggle("about_astar", "appear", {duration: 0.5});}
	function toggle_explenation() {var tog = new Effect.toggle("algorithm_explenation", "appear", {duration: 0.5});}
	Event.observe("about", "click", toggle_about);
	Event.observe("explenation", "click", toggle_explenation);
	Event.observe("close_explenation", "click", toggle_explenation);
	Event.observe("close_about", "click", toggle_about);
	function randomize() {}
	Event.observe($("randomize"), "click", function() {
		for(var row=0; row<ROWS; row++){
			for(var col=0; col<COLS; col++){
				var rand = Math.round(Math.random()*3);
				if((row != goal[0] && row != start[0]) || (col != goal[1] && col != start[1])){
					if(rand == 1){
						walls[row][col] = true;
						$("_"+row+col).style.backgroundColor = "#eee";
					}
					else {
						walls[row][col] = false;
						$("_"+row+col).style.backgroundColor = "#fff";
						$("_"+row+col).innerHTML = "<p>"+goal_distance[row][col]+"</p>";
					}
				}
			}
		}
		astar();
	});
	Event.observe($("run"), "click", function() {astar();});
	function clear_walls() {}
	Event.observe($("clear_walls"), "click", function(){
		$$("#field .node").each(function(n){
			var col = n.getAttribute("rel");
			var row = n.getAttribute("alt");
			if(walls[row][col] == true){
				echo(row+","+col+" is no longer a wall", true);
				walls[row][col] = false;
				n.style.backgroundColor = "#fff";
			}
		});
		astar();
	});
	for(var i = 0; i<ROWS; i++){
		walls[i] = new Array();
		field[i] = new Array();
		for(var a = 0; a<COLS; a++){
			field[i][a] = a;
			walls[i][a] = false;
		}
	}
	echo("Hi, I'm the feedback output, I'll be giving you advice every now and again and feedback on different things you do, why dont you start by clicking a box to set the starting position", true);
	$$("#field .node").each(function(n, index){ 
		Event.observe(n, "mouseover", function(){$("nodeinfo").innerHTML = "<h2>X:"+n.getAttribute("rel")+" Y: "+n.getAttribute("alt")+"</h2>";});
		Event.observe(n, "mouseout", function(){$("nodeinfo").innerHTML = "";});
		Event.observe(n, "click", function(){
			var row = n.getAttribute("alt");
			var col = n.getAttribute("rel");
			
			//first click is start position, second click is goal position
			//all clicks after that toggle wall on and off
			if(start[0] == -1){
				start = [row, col];
				echo("START is set to "+start+"\n> Click a box to set GOAL", true);
				n.style.backgroundColor = "#0417a5";
				n.style.fontcolor = "#fff";
				n.style.cursor = "move";
			}
			else if(start[0] != -1 && goal[0] == -1){
				goal = [row,col];
				echo("GOAL is set to "+goal, true);
				n.style.backgroundColor = "#912a2a";
				n.style.fontcolor = "#fff";
				n.style.cursor = "move";
				echo("You can now place walls by clicking different boxes, or run the algorithm", true);
				astar();
			}
			//toggle walls off
			else if(walls[row][col]){
				walls[row][col] = false;
				n.style.backgroundColor = "#fff";
				n.style.fontcolor = "#aaa";
				echo(row+","+col+" is no longer a wall", true)
				//astar();
			}
			//toggle walls on if not start pos or goal pos
			else if((row != goal[0] && row != start[0]) || (col != goal[1] && col != start[1])) {
				walls[row][col] = true;
				n.style.backgroundColor = "#eee";
				n.style.fontcolor = "#eee";
				echo(row+","+col+" is now a wall", true)
				//astar();
			}
			else echo("You can't build a wall on START or GOAL", true);
		});
	});
});

function astar()
{
	$("output").value = "";
	if(start[0] == -1){
		echo("click somewhere to place start", true);
		return;
	}
	else if(goal[0] == -1){
		echo("Good Job, now click somewhere else to place goal", true);
		return;
	}
	echo("Calculating shortest route from: "+start[1]+","+start[0]+" to: "+goal[1]+","+goal[0], true);
	$("output").value += "X,Y : GD : M.L\n";
	var locked_nodes = new Array();
	var bFound = false;
	current_node = start;
	//SETUP distance between NODES and GOAL, and locked NODES
	for(var i=0; i<ROWS; i++){
		goal_distance[i] = new Array();
		locked_nodes[i] = new Array();
		for(var a=0;a<COLS;a++){
			goal_distance[i][a] = 0;
			locked_nodes[i][a] = false;
			if(walls[i][a] == false && goal != [i,a]) $("_"+i+a).style.backgroundColor = "#fff";
		}
	}
	$("_"+goal[0]+goal[1]).style.backgroundColor = "#912a2a";
	$("_"+start[0]+start[1]).style.backgroundColor = "#0417a5";
	locked_nodes[start[0]][start[1]] = true;
	//END SETUP distance between NODES and GOAL, and locked NODES
	var rows = field.length;
	var index = 0;
	//calc distance between node N and GOAL
	for(var i=0; i<rows; i++){
		var cols = field[i].length;
		for(var a=0; a<cols; a++){
			//dx and dy stand for DistanceX and DistanceY
			var dx = 0;
			var dy = 0;
			if(a > goal[1]) dx = a-goal[1];
			else if(a == goal[1]) dx = 0;
			else dx = goal[1]-a;
			if(i > goal[0]) dy = i-goal[0];
			else if(i == goal[0]) dy = 0;
			else dy = goal[0] - i;
			
			//allow different methods for calculating aprox. distance to goal
			//$F("elementID") = document.getElementById("elementID").value;
			switch($F("method")){
				case "hyp":
					var distance = Math.round(Math.sqrt(dx*dx+dy*dy)); //hypotenuse
				break;
				case "arg":
					var distance = Math.round((dx+dy)/2); //average
				break;
				case "sum":
					var distance = dx+dy; //sum
				break;
				case "max":
					var distance = [dx, dy].max()+1; //max
				break;
				case "min":
					var distance = [dx, dy].min()+1; //min
				break;
			}
			goal_distance[i][a] = distance;
			$$("#field .node")[index].innerHTML = "<p style=\"opacity: "+(distance / 10+0.1)+";\">"+distance+"</p>";
			index++;
		}
	}
	//END calc distance between node N and GOAL
	//search for A path from START through node N to GOAL
	var bFound = false;
	var cx = start[1];
	var cy = start[0];
	var movables = new Array();
	var path = new Array([start[1],start[0]]);
	var pathID = 0;
	var next_node = 0;
	var last_node = [0,0];
	last_node[0] = start[0], last_node[1] = start[1];
	var step = 1;
	while(!bFound){
	
		//cx and cy stand for CurrentX and CurrentY
		cy = parseInt(cy);
		cx = parseInt(cx);
		//Why i used the try-catch approach:
		//if the first line of the try should fail (the field[cy][cx] line) the try would abort and throw an array out of bounds error,
		//and node wouldnt be pushed on the movables array, and never evaluated
		//DOWN UP RIGHT LEFT
		var check_neighbours = function(){
			if(cy+1 < ROWS && !locked_nodes[cy+1][cx] && !walls[cy+1][cx]){
				movables.push([cy+1,cx,goal_distance[cy+1][cx]]);
			}
			if(cy-1 >= 0 && !locked_nodes[cy-1][cx] && !walls[cy-1][cx]){
				movables.push([cy-1,cx,goal_distance[cy-1][cx]]);
			}
			if(cx+1 < COLS && !locked_nodes[cy][cx+1] && !walls[cy][cx+1]){
				movables.push([cy,cx+1,goal_distance[cy][cx+1]]);
			}
			if(cx-1 >= 0 && !locked_nodes[cy][cx-1] && !walls[cy][cx-1]){
				movables.push([cy,cx-1,goal_distance[cy][cx-1]]);
			}
			/* try {
				field[cy+1][cx];
				if(!locked_nodes[cy+1][cx] && !walls[cy+1][cx])
					movables.push([cy+1,cx,goal_distance[cy+1][cx]]);
			} catch(e){}
			try {
				field[cy-1][cx];
				if(!locked_nodes[cy-1][cx] && !walls[cy-1][cx])
					movables.push([cy-1,cx,goal_distance[cy-1][cx]]);
			} catch(e){}
			try {
				field[cy][cx+1];
				if(!locked_nodes[cy][cx+1] && !walls[cy][cx+1])
					movables.push([cy,cx+1,goal_distance[cy][cx+1]]);
			} catch(e){}
			try {
				field[cy][cx-1];
				if(!locked_nodes[cy][cx-1] && !walls[cy][cx-1])
					movables.push([cy,cx-1,goal_distance[cy][cx-1]]);
			} catch(e){}; */
		}
		
		/* check_neighbours();
		while (movables.length < 1){
			echo("Path Locked, reverting back to safe-node", true);
			$("_"+cy+cx).style.backgroundColor = "#ddd89c";
			cx = last_node[1];
			cy = last_node[0];
			last_node[1] = start[1];
			last_node[0] = start[0];
			echo(cx+","+cy+" : "+goal_distance[cy][cx]+"\n", false);
			$("_"+cy+cx).style.backgroundColor = "#eee9b1";
			$("_"+cy+cx).innerHTML = "<p>"+(goal_distance[cy][cx])+"<br />"+step+"</p>";
		}
		if(movables.length == 0){
			echo("Bailing out", true);
			break;
		} */
		check_neighbours();
		
		//if the node has no neighbours to which it can move it reverts back to the last
		//node with 2 or more possible outputs (since one of the outputs is now locked)
		
		if(movables.length == 0 || cx >= COLS || cy >= ROWS || cx < 0 || cy < 0){
			echo("Path Locked, reverting back to safe-node", true);
				$("_"+cy+cx).style.backgroundColor = "#ddd89c";
				cx = path[(path.length-1)][1];
				cy = path[(path.length-1)][0];
				path.pop(); //the reverted node should be popped off the path array to make sure we dont travel back there, it worked without popping tho..
				echo(cx+","+cy+" : "+goal_distance[cy][cx]+"\n", false);
				$("_"+cy+cx).style.backgroundColor = "#eee9b1";
				$("_"+cy+cx).innerHTML = "<p>"+(goal_distance[cy][cx])+"<br />"+step+"</p>";
			
			//re-evaluate the neighbours of the new node, if none are found, bail out because
			//there was no last node with 2 or more free neighbours and the while !bfound will never end
			check_neighbours();
			if(movables.length == 0) {
				check_neighbours();
				echo("Ye, I can't move anywhere, so I'm bailing to avoid a crash", true);
				break;
			}
		}
		
		
		//create a temporary array that only stores valid nodes that can be travelled to, 
		//iterarte thru them and pick the  one with the lowest value
		var temp = new Array();
		movables.each(function(n){temp.push(n[2]);});
		/* next_node = temp.sort();
		cy = movables[temp[0]][0];
		cx = movables[temp[0]][1];
		locked_nodes[cy][cx] = true;
		temp.reverse();
		temp.pop();
		temp.reverse();
		path.push([cy,cx]); */
		next_node = temp.min();
		for(var i = 0; i<movables.length; i++){
			if(movables[i][2] == next_node){
				/* last_node[0] = cy;
				last_node[1] = cx; */
				if(movables.length > 1){
					pathID++;
					path.push([cy,cx]); //index based on "step" should work?
				}
				cx = movables[i][1];
				cy = movables[i][0];
				locked_nodes[cy][cx] = true;
				break;
			}
		}
		echo(cx+","+cy+" : "+next_node+" : "+movables.length+"\n", false);
		$("_"+cy+cx).style.backgroundColor = "#eee9b1";
		$("_"+cy+cx).innerHTML = "<p>"+(goal_distance[cy][cx])+"<br />"+step+"</p>";
		if(cx == goal[1] && cy == goal[0])
		{
			echo("Found a path in "+(step)+" steps!", true);
			bFound = true;
		}
		step++;
		
		//clear all nodes of their attribute of movable
		movables.clear();
	}
	$("_"+goal[0]+goal[1]).style.backgroundColor = "#912a2a";
	/* var update_stuff = new Ajax.Request("stats.php", {parameters: "found="+bFound, method: "post", onSuccess: function(transport){
		var runs = transport.responseText;
		$("runs").innerText = runs;
	}}) */
	var update_stuff = new Ajax.Updater("runs", "stats.php", {parameters: "found="+bFound, method: "post", onComplete: function(){
		var vars = $("runs").innerHTML.split(" ");
		$("founds").innerHTML = vars[1];
		$("runs").innerHTML = vars[0];
		$("percent").innerHTML = "("+Math.round(vars[1]/vars[0]*100)+"%)";
		var hilite = new Effect.Highlight("footer", {startcolor: "#eee9b1", endcolor: "#ffffff", duration: 2});
	}});
	return;
}

//echo() writes to the output feedback box

function echo(str, format)
{
	if(format) $("output").value += "> "+str+"\n"; 
	else $("output").value += str;
	return;
}
