// BEGIN CONTROL TAB CODE

function hideControlPanels() {
	var controlPanels = $("panels");
	for(var i = 0; i < controlPanels.childNodes.length ; i++) {
		var f = controlPanels.childNodes[i];
		if(f.nodeType == 1)
			f.style.display = 'none';
	}
}

function clearControlTabs() {
	var tabs = $("panel-tabs");
	for(var i = 0 ; i < tabs.childNodes.length ; i++) {
		var f = tabs.childNodes[i];
		if(f.className == "active")
			f.className = "";
	}
}

function showControlPanel(controlName) {
	$(controlName+"-panel").style.display = 'block';
}

function activateControlTab(controlName) {
	var tab = $(controlName+"-tab");
	tab.className = "active";
}

function showControl(controlName) {
	hideControlPanels();
	clearControlTabs();
	showControlPanel(controlName);
	activateControlTab(controlName);
	$('ajax-result').update();
}

// END CONTROL TAB CODE

// BEGIN MAP NAV CODE

function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name) {
	createCookie(name,"",-1);
}

var mapProperties = {
	x: readCookie("recentx")*1  || 0,
	y: readCookie("recenty")*1 || 0,
	zoom: readCookie("recentzoom")*1 || 1,
	adjtypeid: readCookie("adjtypeid")*1 || 1,
	scale : function() {
		return Math.pow(2, 4 - this.zoom);
	},
	w: 512,
	h: 512
};

function saveMapCookies() {
	createCookie("recentzoom",mapProperties.zoom,100);
	createCookie("recentx",mapProperties.x,100);
	createCookie("recenty",mapProperties.y,100);
	createCookie("adjtypeid",mapProperties.adjtypeid,100);
}

function changeZoom(zoomChange) {
	if(mapProperties.zoom * zoomChange == -1 || mapProperties.zoom * zoomChange == 5)
		return;
	
	var s = mapProperties.scale();
	if(zoomChange > 0) {
		s = s/4;
		mapProperties.x += mapProperties.w * s;
		mapProperties.y += mapProperties.h * s;
	} else {
		s = s/2;
		mapProperties.x -= mapProperties.w * s;
		mapProperties.y -= mapProperties.h * s;
	}
	
	mapProperties.zoom = zoomChange * 1 + mapProperties.zoom;
	saveMapCookies();
	reloadMap();
}

function changeLocation(xChange, yChange) {
	var d = 128 * (mapProperties.scale());
	mapProperties.x = xChange * d + mapProperties.x;
	mapProperties.y = yChange * d + mapProperties.y;
	saveMapCookies();
	reloadMap();
}

function changeAdjTypeId() {
	if($F('adjtypeid') == '')
		return;
	mapProperties.adjtypeid = $F('adjtypeid');
	saveMapCookies();
	reloadMap();
}

function checkReload() {
	var map = $('map');
	if(!$('map').complete)
		window.setTimeout('checkReload()',500);
	else {
		window.setTimeout("$('map_reload_notifier').hide();$('map_reload_link').show();",500);
	}
}

function reloadMap() {
	$('map_reload_link').hide();
	$('map_reload_notifier').show();
	$('map').src = "map/indicator_arrows_black.gif";
	$("map").src = "../map/draw.php5?plot&x="+ mapProperties.x + "&y=" + mapProperties.y + "&zoom=" + mapProperties.zoom + "&w=" + mapProperties.w +"&h=" + mapProperties.h + "&adjtypeid="+mapProperties.adjtypeid+"&random="+(new Date().getTime());
	window.setTimeout('checkReload()',500);
	$("map_params").update("x="+mapProperties.x+" ,y="+mapProperties.y+" ,zoom="+mapProperties.zoom);
	adjtypes = $('adjtypeid');
	for(var i = 0; i < adjtypes.options.length; i++) {
		adjtypes.options[i].selected = (mapProperties.adjtypeid == adjtypes.options[i].value);
	}
}

function pageLoad() {
	reloadMap();
}

// END MAP NAV CODE

// BEGIN MAP ACTION CODE

function getMouseCoordinates(event) {
	// returns an object with the map coordinates as x and y properties
	var x = mapProperties.x + (event.offsetX || event.pageX - event.target.x) * Math.pow(2,4-mapProperties.zoom);
	var y = mapProperties.y + (event.offsetY || event.pageY - event.target.y) * Math.pow(2,4-mapProperties.zoom);
	return {"x": x, "y": y};
}

function handleClick(event) {
	// this function is what gets called when someone clicks on the map
	// the doClick function of the currently selected action gets called
	if(action)
		action.doClick(event);
}

function mouseMove(event) {
	// this function is what gets called when the mouse is moved over the map
	// it updates the field displaying the current mouse coordinates
	var coords = getMouseCoordinates(event);
	var x = coords.x;
	var y = coords.y;
	$('coords').update("x="+x+", y="+y);
}

function mouseOut() {
	// this is what gets called when the mouse leaves the map
	$('coords').update();
}

function doRequest(params) {
	// this is a helper function for performing ajax requests that are a result of a command being executed by the user
	$('ajax-result').update('<div style="margin:0px auto">Connecting to server, please wait<br/><img alt="progress bar" src="map/progressbar_green.gif"/></div>');
	new Ajax.Updater($('ajax-result'),'map_ajax_worker.php5',{parameters:params,
					   									onFailure: function() {
															$('ajax-result').update('<span style="color:red">A problem occurred. Try again.</span>');
															reloadMap();
														},
														onSuccess: function() {reloadMap();}});
}

// The following code uses JavaScript objects to represent different kinds of commands (single-click vs two-click)
// It provides default implementations for methods that the actual action classes can use or can override
// If you have no idea about objects in JavaScript it is advised you read some documentation (for example at http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Working_with_Objects)


function AbstractAction(command,name,perform) {
	// this is an abstract base class for all commands
	//  command is a string identifier of a command
	//  name is a human-readable short description of what the command does
	//  perform is an optional method that will be used to perform the command. otherwise the default implementation will be used
	this.command = command || "unknown";
	this.name = name || "unknown";
	this.parameters = {};  // parameters is a mapping of current ajax request parameters being assembed as the command is being invoked
	if(perform) this.perform = perform;
}
AbstractAction.prototype = {
	perform: function() {
		// if the perform function is not overridden in the constructor, the defaultPerform() function is called
		this.defaultPerform();
	},
	defaultPerform: function() {
		// the default perform action is the invoke an ajax request with the current set of parameters
		// the reason there are 2 methods is so that subclasses have the option of overriding the perform() function and still have the funcionality of this function available to them
		doRequest(this.parameters);
	},
	cleanParams: function() {
		// flushses all the parameters that have been assembled to prepare the command to be used again
		for(param in this.parameters)
			delete this.parameters[param];
		delete this.parameters;
	},
	finish: function() {
		// called when the command is finished being executed
		this.cleanParams();
	},
	unselect: function() {
		// called when a different command is selected
		this.finish();
	}
}

function SingleClickAction(command,name,perform) {
	// this class represents all actions that are invoked with a single click (usually done on points)
	this.base = AbstractAction;
	this.base(command,name,perform);
}
SingleClickAction.prototype = new AbstractAction; // inherit default parent behavior
SingleClickAction.prototype.doClick = function(event) {
	// this method is the handler for when the user clicks on the map with this action being selected as the current action
	// gets coordinates, assembles the parameters, and executes an ajax request
	var coords = getMouseCoordinates(event);
	this.parameters.x = coords.x;
	this.parameters.y = coords.y;
	this.parameters.action = this.command;
	this.perform();
	this.finish();
}
	
function DoubleClickAction(command,name,perform) {
	// this class represents all actions that are invoked requiring 2 clicks (usually done for adjacencies)
	this.base = AbstractAction;
	this.base(command,name,perform);
}
DoubleClickAction.prototype = new AbstractAction;
DoubleClickAction.prototype.secondClick = false;  // property identifying if this is the first or second click for this action
DoubleClickAction.prototype.doClick = function(event) {
	var coords = getMouseCoordinates(event);
	if(!this.secondClick) {
		this.parameters.x1 = coords.x;
		this.parameters.y1 = coords.y;
		this.secondClick = true;
	} else {
		// issue the ajax request only after the second click
		this.parameters.x2 = coords.x;
		this.parameters.y2 = coords.y;
		this.parameters.action = this.command;
		this.perform();
		this.finish();
	}
}
DoubleClickAction.prototype.finish = function() {
	this.cleanParams();
	this.secondClick = false; // if action is done, indicated that the next time it will be a first click
}

function MixedClickAction(command,name,perform) {
	// this class represents a mixed click action used for paths, where the first time you need 2 clicks, but subsequently a single click is enough
	this.base = AbstractAction;
	this.base(command,name,perform);
}
MixedClickAction.prototype = new AbstractAction;
MixedClickAction.prototype.beginningAction = true; // indicates its the first phase of the action
MixedClickAction.prototype.secondClick = false; // indicates we're on the first click
MixedClickAction.prototype.prev = null; // stores result of previous click to be used in second phase
MixedClickAction.prototype.doClick = function(event) {
	var coords = getMouseCoordinates(event);
	if(this.beginningAction) {
		// in the first phase we only issue the request after two clicks
		if(!this.secondClick) {
			this.parameters.x1 = coords.x;
			this.parameters.y1 = coords.y;
			this.secondClick = true;
		} else {
			this.parameters.x2 = coords.x;
			this.parameters.y2 = coords.y;
			this.parameters.action = this.command;
			this.prev = coords;
			this.perform();
			this.beginningAction = false; // indicate now we will be in second phase
			this.finish();
		}
	} else {
		// in second phase issue request after each click
		this.parameters.x2 = coords.x;
		this.parameters.y2 = coords.y;
		this.parameters.x1 = this.prev.x;
		this.parameters.y1= this.prev.y;
		this.prev = coords;
		this.parameters.action = this.command;
		this.parameters.continuation = 1;
		this.perform();
		this.finish();
	}
}
MixedClickAction.prototype.unselect = function() {
	// when unselected, reset state
	this.finish();
	this.beginningAction = true;
	this.secondClick = false;
}

// this object stores a collection of all the actions and provides some utility methods and properties
var actions = {
	addAction:function(type,command,name,perform) {
		// adds an action to this collection dynamically calling the appropriate constructor based on the type of action
		this[command] = new this.actionTypes[type](command,name,perform);
	},
	actionTypes : {
		// an enumeration of the different types of actions
		single: SingleClickAction,
		double: DoubleClickAction,
		mixed: MixedClickAction
	}
};


function processFlags(prefix, flags, params) {
	// this is a utility function for processing flags
	for(var i = 0; i < flags.length; i++) {
		var flag = prefix + flags[i];
		if($(flag).checked)
			params[flag] = 1;
	}
}


// the following code is what actually adds all the actions
actions.addAction('single','makewp','Create new waypoint',
				  function() {
					  // this is an example of a customized perform method that prompts for a name of the waypoint in certain conditions
					  var promptText = null;
					  if($F('wp_name')) // prompt if name forcing is selected
							promptText = 'Enter a name for this point:';
					  else if($F('wpflags_busstop') || $F('wpflags_archway')) // prompt if this is a bus stop or archway
					  		promptText = 'This kind of point should have a name:';
					  
					  if(promptText) {
					  		var name = prompt(promptText,'');
							if(name == null) // when cancelled, abort
								return;
							this.parameters.wp_name = name;
					  }
					  
					  processFlags('wpflags_',['accessible','busstop','archway'],this.parameters);
					  
					  if($F('destinationid') != 0) // potentially add destination id
					  		this.parameters.destinationid = $F('destinationid');
					  this.defaultPerform(); // issue request
				});
actions.addAction('double','movewp','Move waypoint');
actions.addAction('single','deletewp','Delete waypoint',
				  function() {
					  if(confirm('Are you sure you want to delete this point?'))
					  	this.defaultPerform();
				  });
actions.addAction('double','deletegroup','Delete group of waypoints',
				  function() {
					  var tx1 = (this.parameters.x1 - mapProperties.x)/Math.pow(2,4-mapProperties.zoom);
					  var tx2 = (this.parameters.x2 - mapProperties.x)/Math.pow(2,4-mapProperties.zoom);
					  var ty1 = (this.parameters.y1 - mapProperties.y)/Math.pow(2,4-mapProperties.zoom);
					  var ty2 = (this.parameters.y2 - mapProperties.y)/Math.pow(2,4-mapProperties.zoom);
					  var x1 = Math.min(tx1,tx2);
					  var x2 = Math.max(tx1,tx2);
					  var y1 = Math.min(ty1,ty2);
					  var y2 = Math.max(ty1,ty2);
					  var foo = $('map_overlay');
					  $('map_overlay').style.left = x1+"px";
					  $('map_overlay').style.top = y1+"px";
					  $('map_overlay').style.width = Math.abs(x2 - x1)+"px";
					  $('map_overlay').style.height = Math.abs(y2 - y1)+"px";
					  
					  //$('map_overlay').show();
					  if(confirm('Are you sure you want to proceed? All points and their adjacencies within the specified region will be deleted.'))
					    this.defaultPerform();
					  
					  $('map_overlay').hide();
				  });
actions.addAction('single','displaywp','Display waypoint details');
function pathPerform() {
	// common perform method for path actions
	var promptText = null;
	if($F('path_adj_name'))
		promptText = 'Enter a name for this adjacency';
	else
		promptText = 'Every adjacency should have a name. Please enter one:';
		
	if(promptText) {
		var name = prompt(promptText, '');
		if(name == null) // when cancelled, abort
			return;
		this.parameters.adj_name = name;
	}
	promptText = null;
	
	if($F('path_wp_name'))
		promptText = 'Enter a name for this point:';
	else if($F('pathflags_wp_archway'))
		promptText = 'This kind of point should have a name:';
	
	if(promptText) {
		var name = prompt(promptText,'');
		if(name == null) // when cancelled, abort
			return;
		this.parameters.wp_name = name;
	}
	
	this.parameters.adjtypeid = mapProperties.adjtypeid;
	
	this.parameters.safety = $F('path_safety');
	
	processFlags('pathflags_wp_', ['accessible','archway'], this.parameters);
	processFlags('pathflags_adj_', ['accessible','stairway','crossing'], this.parameters);
	this.defaultPerform();
}
actions.addAction('mixed','makepath','Create 2-way path',pathPerform);
actions.addAction('mixed','makepath1','Create 1-way path',pathPerform);

function adjacencyPerform() {
	// common perform method for adjacency actions
	var promptText = null;
	if($F('adj_name'))
		promptText = 'Enter a name for this adjacency';
	else
		promptText = 'Every adjacency should have a name. Please enter one:';
		
	if(promptText) {
		var name = prompt(promptText, '');
		if(name == null) //  when cancelled, abort
			return;
		this.parameters.adj_name = name;
	}
	
	this.parameters.adjtypeid = mapProperties.adjtypeid;
	this.parameters.safety = $F('adj_safety');
	
	processFlags('adjflags_',['accessible','stairway','crossing'],this.parameters);
	this.defaultPerform();
}

actions.addAction('double','makeadj','Create 2-way adjacency', adjacencyPerform);
actions.addAction('double','makeadj1','Create 1-way adjacency', adjacencyPerform);

function adjacencyPerformOther() {
	this.parameters.adjtypeid = mapProperties.adjtypeid;
	this.defaultPerform();
}

actions.addAction('double','groupsafety','Modify safety of a group of adjacencies',
				  function() {
					  this.parameters.safety = $F('adj_group_safety');
					  this.parameters.adjtypeid = mapProperties.adjtypeid;
					  this.defaultPerform();
				  });
actions.addAction('double','splitadj','Split adjacency',adjacencyPerformOther);
actions.addAction('double','deleteadj','Delete adjacency',
				  function() {
					  if(confirm('Are you sure you want to delete this adjacency?')) {
						  	this.parameters.adjtypeid = mapProperties.adjtypeid;
					  		this.defaultPerform();
					  }
				});
actions.addAction('double','displayadj','Display adjacency',adjacencyPerformOther);

var action = null; // this variable stores the current action

function selectionChange(input) {
	// this function is what gets called when the action selection is changed
	// it enables/disables the appropriate flag fields, based on the selected action
	if(action) action.unselect(); // unselect the current action
	action = actions[input.value]; // get current action
	$("current_action").update(action.name);
	
	var wpflags = ["accessible","busstop","archway"];
	var adjflags = ["accessible","stairway","crossing"];
	var pathflags = ["wp_accessible","wp_archway","adj_accessible","adj_stairway","adj_crossing"];
	
	var i = 0;
	for(i = 0; i < wpflags.length ; i++) {
		$("wpflags_"+wpflags[i]).disabled = (action.command != "makewp");
	}
	$("destinationid").disabled = (action.command != "makewp");
	$('wp_name').disabled = (action.command != 'makewp');
	
	
	for(i = 0; i < adjflags.length ; i++) {
		$("adjflags_"+adjflags[i]).disabled = (action.command != "makeadj" && action.command != "makeadj1");
	}
	$('adj_name').disabled = (action.command != 'makeadj' && action.command != 'makeadj1');
	$('adj_safety').disabled = (action.command != 'makeadj' && action.command != 'makeadj1');
	
	$('adj_group_safety').disabled = (action.command != 'groupsafety');
	
	
	for(i = 0; i < pathflags.length; i++) {
		$("pathflags_"+pathflags[i]).disabled = (action.command != "makepath" && action.command != "makepath1");
	}
	$('path_adj_name').disabled = (action.command != 'makepath' && action.command != 'makepath1');
	$('path_wp_name').disabled = (action.command != 'makepath' && action.command != 'makepath1');
	$('path_safety').disabled = (action.command != 'makepath' && action.command != 'makepath1');
}

// END MAP ACTION CODE