// GMap2 - the google map
var map;

// Array - an array containing previous loaded data
var memory = new Array();

// Array - an array of markers
var markers = new Array();

// GIcon - the icon of all markers at intersections
var icon_intersection = makeIcon("blue");

// GIcon - the icon of a marker visible when highlighting an intersection
var icon_highlight = makeIcon("green");

// GIcon - the icon of a marker visible when creating a new intersection
var icon_arrow = makeArrow("arrow");

// GMarker - a marker that overlays the currently selected marker
var highlight = new GMarker(new GLatLng(0,0), {icon: icon_highlight});

// GMarker - a marker that shows where a new marker will be placed
var arrow = new GMarker(new GLatLng(0,0), {icon: icon_arrow, draggable: true, dragCrossMove: false});

// int - the ID of the current marker
var highlight_id = 0;

// boolean - whether the arrow is visible
var arrow_visible = false;

// boolean - is the user in "insert" mode or "browse" mode?
var insert_mode = false;

// string - the piece of HTML that creates a form for a new intersection
var intersection_form = "";
intersection_form += '<div>Add an intersection:</div>'+"\n";
intersection_form += '<form name="form1" id="form1" method="post" action="javascript:void(0);" >'+"\n";
intersection_form += '<div>Name of Intersection:</div>'+"\n";
intersection_form += '<input type="text" name="title" maxlength="45" />'+"\n";
intersection_form += '<form name="form1" id="form1" method="post" action="javascript:void(0);" >'+"\n";
intersection_form += '<div>Your Name:</div>'+"\n";
intersection_form += '<input type="text" name="name" maxlength="30" />'+"\n";
intersection_form += '<div>Age:</div>'+"\n";
intersection_form += '<input type="text" name="age" maxlength="2" size="5" />'+"\n";
intersection_form += '<div>City of Residence:</div>'+"\n";
intersection_form += '<input type="text" name="residence" maxlength="30" />'+"\n";
intersection_form += '<div>Comments:</div>'+"\n";
intersection_form += '<textarea name="comments" rows="5" cols="20"></textarea>'+"\n";
intersection_form += '<input type="button" value="Add Intersection" onClick="user_sendIntersection()" />'+"\n";
intersection_form += '</form>'+"\n";

// string - the piece of HTML that creates a form for a new comment
var comment_form = "";
comment_form += '<hr />'+"\n";
comment_form += '<div>Add a comment:</div>'+"\n";
comment_form += '<form name="form1" id="form1" method="post" action="javascript:void(0);" >'+"\n";
comment_form += '<div>Name:</div>'+"\n";
comment_form += '<input type="text" name="name" maxlength="30" />'+"\n";
comment_form += '<div>Age:</div>'+"\n";
comment_form += '<input type="text" name="age" maxlength="2" size="5" />'+"\n";
comment_form += '<div>City of Residence:</div>'+"\n";
comment_form += '<input type="text" name="residence" maxlength="30" />'+"\n";
comment_form += '<div>Comments:</div>'+"\n";
comment_form += '<textarea name="comments" rows="5" cols="20"></textarea>'+"\n";
comment_form += '<input type="button" value="Add Comment" onClick="user_sendComment()" />'+"\n";
comment_form += '</form>'+"\n";

// int - default latitude/longitude/zoom
var HOME_LAT = 47.24730;
var HOME_LNG = -122.44400;
var HOME_ZOOM = 10;



// This function is triggered by <body onload="">
function initialize() {
	
	// Initialize the map
	map = new GMap2(document.getElementById("map"));
	map.setCenter(new GLatLng(HOME_LAT, HOME_LNG), HOME_ZOOM);
	
	// Add extras to map
	map.addControl(new GLargeMapControl());
	
	var mapControl = new GHierarchicalMapTypeControl();
	map.addControl(mapControl);
	
	// Add click behavior to map
	GEvent.addListener(map, "click", user_clickOnMap);
	GEvent.addListener(map, "dblclick", user_dblclickOnMap);
	
	// Load the markers onto the map
	refreshMarkers();
}



// Ask the server to send information about all the markers on the map
function refreshMarkers() {
	memory = new Array();
	markers = new Array();
	removeArrow();
	removeHighlight();
	map.clearOverlays()
	GDownloadUrl("markers.php", outcome_refreshMarkers);
}



// This function is triggered when the server responses to the client's request for markers
function outcome_refreshMarkers(data, responseCode) {
	
	// To ensure against HTTP errors that result in null or bad data,
	// always check status code is equal to 200 before processing the data
	if (responseCode == 200) {
		
		// Split up the data
		var rows = data.split("\n");
		
		// Read each row
		for (var i=0; i<rows.length-1; i++) {
			var record = rows[i];
			var data = record.split("\t");
			
			// If the correct number of variables are found
			if (data.length == 5) {
				
				// Create a marker
				var point = new GLatLng(data[2],data[3]);
				var marker = new GMarker(point, {icon: icon_intersection})
				marker.id = data[0];
				marker.name = data[1];
				marker.isCommented = (data[4] > 0);
				
				// Add the marker to the map
				map.addOverlay(marker);
				
				// Add the marker to the array
				markers[data[0]] = marker;
			}
			
		} // End of "for (var i=0; i<rows.length-1; i++)"
	} else { 
		alert("Cannot access list of markers.");
	}
}


// If the user double clicks while in insert mode, reload the marker onto that spot
function user_dblclickOnMap(marker, point) {
	if (insert_mode) {
		removeArrow();
		addArrowAt(point);
	}
}


// This function is triggered when the user clicks on the map
function user_clickOnMap(marker, point) {
	if (insert_mode) {
		
		// If the user clicked on a marker
		if (marker && marker != arrow) {
			var question = 'You have selected the intersection: "'+marker.name+'".' +"\n"+ 'Do you want to comment on this intersection?';
			if (confirm(question)) {
				changeToViewMode();
				user_clickOnMap(marker, marker.getPoint());
			}
		} else {
			// User clicked on the map itself
			if (!arrow_visible) {
				addArrowAt(point)
			}
		}
		
	} else {
		// If the user clicked on a marker, and that marker is not the highlight...
		if (marker && marker != highlight) {
			removeHighlight();
			addHighlightOnto(marker);
		}
	}
}


// Display text on the browser
function display(data, add_comment) {
	// Display the information about the marker
	document.getElementById("ajax_comments").innerHTML = data + ((add_comment) ? comment_form : "");
}


// Ask the server for information about a specific marker
function getCommentsFor(marker) {
	var id = marker.id;
	var name = marker.name;
	var isCommented = marker.isCommented;
	
	// If this marker has comments...
	if (isCommented) {
		
		// Ask the server for the comments of this marker
		GDownloadUrl("data.php?id="+id, outcome_getCommentsFor);
		
	} else {
		
		// Don't talk to the server, just tell the user there are no comments
		var result = 'There are no comments about this intersection.'+"\n";
		result = generateIntersectionTitle(name) + result;
		memory[id] = result;
		display(memory[highlight_id], true);
		
	}
}

// This function triggers when the server responds to the client's request about a specific marker
function outcome_getCommentsFor(data, responseCode) {
	
	// To ensure against HTTP errors that result in null or bad data,
	// always check status code is equal to 200 before processing the data
	if (responseCode == 200) {
		
		// Split up the data
		var rows = data.split("\n");
		
		// The formatted data to be saved
		var result = "";
		
		// The first line should contains the ID of the marker, which is a whole number
		var id = ((rows[0].match(/^\d+$/)) ? rows[0] : 0);
		
		// If the ID is valid...
		if (id > 0) {
			
			var marker = markers[id];
			
			// Read each of the other rows
			for (var i=1; i<rows.length-1; i++) {
				var record = rows[i];
				var data = record.split("\t");
				
				// If the correct number of variables are found
				if (data.length == 5) {
					
					// If this is not the first comment, add a break
					if (result != "") { result += generateCommentBreak(); }
					
					// Add the comment
					result += generateSingleComment(data);
				}
			}
			
			// Add a title to the top of the result
			result = generateIntersectionTitle(marker.name) + result;
			
			// Save the results to the cache
			memory[id] = result;
			
			// If this marker is still being highlighted, display the data
			if (id == highlight_id) {
				display(memory[id], true);
			}
			
		} // End of "if (id > 0)"
	} else { 
		alert("Cannot access comments.");
	}
	
}



///////////////////////////////////////////////////
////  JavaScript functions that generate HTML  ////
function generateSingleComment(data) {
	var result = "";
	result += '<div class="head">'+data[0]+' ('+data[1]+') of '+data[2]+' writes:</div>'+"\n";
	result += '<div class="body">'+data[3]+'</div>'+"\n";
	result += '<div class="date">'+data[4]+'</div>'+"\n";
	return result;
}

function generateCommentBreak() {
	return '<hr />'+"\n";
}

function generateIntersectionTitle(name) {
	return '<div class="title">'+name+'</div>'+"\n";
}
////  JavaScript functions that generate HTML  ////
///////////////////////////////////////////////////





// This function triggers when the user wants to add an intersection
function user_sendIntersection() {
	
	// Get the information from the form
	var id        = highlight_id;
	var title     = trim(document.form1.title.value);
	var latitude  = arrow.getPoint().lat();
	var longitude = arrow.getPoint().lng();
	var name      = trim(document.form1.name.value);
	var age       = trim(document.form1.age.value);
	var residence = trim(document.form1.residence.value);
	var comments  = trim(document.form1.comments.value);
	
	// Validate the information
	var errors = "";
	if (title     == "") { errors += "\n- Include a title"; }
	if (!arrow_visible ) { errors += "\n- Mark the intersection on the map."; }
	if (name      == "") { errors += "\n- Include a name"; }
	if (age       == "") { errors += "\n- Include an age"; }
	if (residence == "") { errors += "\n- Include a residence"; }
	if (comments  == "") { errors += "\n- Include your comments"; }
	
	// Stop if the data is incomplete
	if (errors) {
		errors = "Please make sure all the information is filled out:"+errors;
		alert(errors);
		return;
	}
	
	// Send the information
	var post = "";
	post += "title="     + URLEncode(title)     + "&";
	post += "latitude="  + URLEncode(latitude)  + "&";
	post += "longitude=" + URLEncode(longitude) + "&";
	post += "name="      + URLEncode(name)      + "&";
	post += "age="       + URLEncode(age)       + "&";
	post += "residence=" + URLEncode(residence) + "&";
	post += "comments="  + URLEncode(comments)  + "&";
	post += "id=" + "0";
	
	GDownloadUrl("add_intersection.php", outcome_sendItem, post, "application/x-www-form-urlencoded");
	// Set a process to update the form
}

// This function triggers when the user wants to add a comment
function user_sendComment() {
	
	// Get the information from the form
	var id        = highlight_id;
	var name      = trim(document.form1.name.value);
	var age       = trim(document.form1.age.value);
	var residence = trim(document.form1.residence.value);
	var comments  = trim(document.form1.comments.value);
	
	// Validate the information
	var errors = "";
	if (name      == "") { errors += "\n- Include a name"; }
	if (age       == "") { errors += "\n- Include an age"; }
	if (residence == "") { errors += "\n- Include a residence"; }
	if (comments  == "") { errors += "\n- Include your comments"; }
	
	// Stop if the data is incomplete
	if (errors) {
		errors = "Please make sure all the information is filled out:"+errors;
		alert(errors);
		return;
	}
	
	// Send the information
	var post = "";
	post += "name="      + URLEncode(name)      + "&";
	post += "age="       + URLEncode(age)       + "&";
	post += "residence=" + URLEncode(residence) + "&";
	post += "comments="  + URLEncode(comments)  + "&";
	post += "id=" + id;
	
	GDownloadUrl("add_comment.php", outcome_sendItem, post, "application/x-www-form-urlencoded");
	// Set a process to update the form
}


function outcome_sendItem(data, responseCode) {
	// To ensure against HTTP errors that result in null or bad data,
	// always check status code is equal to 200 before processing the data
	if (responseCode == 200) {
		
		// Split up the data
		var rows = data.split("\n");
		
		// The first line should contains the ID of the marker, which is a whole number
		var id = ((rows[0].match(/^\d+$/)) ? rows[0] : 0);
		var outcome = (rows[1] && (rows[1].match(/^\w+$/)) ? rows[1] : "");
		
		switch(outcome) {
			case "i-incomplete":
				alert("The intersection has not been added because the title or the location is missing.");
				break;
			case "c-database":
				alert("The intersection has not been added because of a problem with the database.");
				break;
			case "c-incomplete":
				alert("The comment has not been added because one or more fields were incomplete.");
				break;
			case "c-invalid":
				alert("The intersection you are commenting on is not in the database.");
				break;
			case "c-database":
				alert("The comment has not been added because of a problem with the database.");
				break;
			case "approval":
				alert("The information has been received, and is awaiting administrator approval.");
				// Continue onto success
			case "success":
				changeToViewMode();
				
				// Remove comment list from memory
				memory[id] = "";
				
				if (markers[id]) {
					// The marker is on the map
					var marker = markers[id];
					
					// Note there is now a new comment on this marker
					marker.isCommented = true;
					
					user_clickOnMap(marker, marker.getPoint());
				} else {
					// The marker is not on the map, so must be new
					refreshMarkers();
				}
				break;
			default:
				alert("Message could not be understood:" + outcome);
				break;
		}
		
	} else { 
		alert("A problem occurd while trying to add the comment.");
	}
}


// User has pressed the "add marker" or "view marker" button
function user_viewAddToggle() {
	if (insert_mode) {
		changeToViewMode()
	} else {
		changeToInsertMode();
	}
}

// If not in insert mode, change to insert mode
function changeToInsertMode() {
	if (!insert_mode) {
		insert_mode = true;
		map.disableDoubleClickZoom();
		removeHighlight()
		document.getElementById("toggleViewInsert").value = "Cancel Adding Intersection";
	}
}

// If not in view mode, change to view mode
function changeToViewMode() {
	if (insert_mode) {
		insert_mode = false;
		map.enableDoubleClickZoom();
		removeArrow()
		document.getElementById("toggleViewInsert").value = "Add an Intersection";
	}
}


// Put the highlight on the map atop the marker the user has clicked
function addHighlightOnto(marker) {
	
	highlight.setPoint(marker.getPoint());
	map.addOverlay(highlight);
	highlight_id = marker.id;
	
	// If this marker has been cached...
	if (memory[highlight_id]) {
		// Pull the information from the cache
		display(memory[highlight_id], true);
	} else {
		// Ask the server for data about the marker
		getCommentsFor(marker);
	}
}

// If the highlight is on the map, remove the highlight
function removeHighlight() {
	if (highlight_id != 0) {
		map.removeOverlay(highlight);
		highlight_id = 0;
	}
	
	// Clear the display as well
	display("", false);
}

// Put the arrow on the map where the user has clicked
function addArrowAt(point) {
	arrow.setPoint(point);
	map.addOverlay(arrow);
	arrow_visible = true;
	
	display(intersection_form, false);
}

// If the arrow is on the map, remove the arrow
function removeArrow() {
	if (arrow_visible) {
		map.removeOverlay(arrow);
		arrow_visible = false;
	}
	
	// Clear the display as well
	display("", false);
}







/* Make an icon based on the folder the icon information is in. */
function makeIcon(color) {
	var xMarker = new GIcon();
	xMarker = new GIcon();
	xMarker.image = "markers/"+color+"/marker.png";
	xMarker.iconSize = new GSize(11, 11);
	xMarker.printImage = "markers/"+color+"/markerie.gif";
	xMarker.mozPrintImage = "markers/"+color+"/markerff.gif";
	xMarker.iconAnchor = new GPoint(6, 6);
	return xMarker;
}

/* Make an icon based on the folder the icon information is in. */
function makeArrow(color) {
	var xMarker = new GIcon();
	xMarker = new GIcon();
	xMarker.image = "markers/"+color+"/marker.png";
	xMarker.iconSize = new GSize(20, 34);
	xMarker.shadow = "markers/"+color+"/shadow.png";
	xMarker.printImage = "markers/"+color+"/markerie.gif";
	xMarker.printShadow = "markers/"+color+"/shadowie.gif";
	xMarker.mozPrintImage = "markers/"+color+"/markerff.gif";
	xMarker.mozPrintShadow = "markers/"+color+"/shadowff.gif";
	xMarker.shadowSize = new GSize(37, 34);
	xMarker.iconAnchor = new GPoint(9, 34);
	return xMarker;
}



/* Trim whitespace before and after text. */
/*        Version December 3, 2006        */
function trim(sString) {
   
   if (sString == null)
      return "";
   
   return sString.replace(/^\s*|\s*$/g,"");
   
}

// Code borrowed from (2008-05-14):
//   http://cass-hacks.com/articles/code/js_url_encode_decode/
function URLEncode (clearString) {
  var output = '';
  var x = 0;
  clearString = clearString.toString();
  var regex = /(^[a-zA-Z0-9_.]*)/;
  while (x < clearString.length) {
    var match = regex.exec(clearString.substr(x));
    if (match != null && match.length > 1 && match[1] != '') {
    	output += match[1];
      x += match[1].length;
    } else {
      if (clearString[x] == ' ')
        output += '+';
      else {
        var charCode = clearString.charCodeAt(x);
        var hexVal = charCode.toString(16);
        output += '%' + ( hexVal.length < 2 ? '0' : '' ) + hexVal.toUpperCase();
      }
      x++;
    }
  }
  return output;
}
