/*
//*******
//
//	filename: google_maps.js
//	author: Zack Brown
//	date: 3rd March 2010
//
//*******
*/

//observe window onload
Event.observe(window, 'load', function()
{
	//create a new google maps object
	google_maps = new GoogleMaps();
	
	//create a new google maps helper object
	google_maps_helper = new GoogleMapsHelper();
});

//declare GoogleMaps class
var GoogleMaps = Class.create();

//declare GoogleMaps prototype
GoogleMaps.prototype = {

	//wrapper function for initialisation
	initialize: function()
	{
		//create a new hash for all maps
		this.maps = new Hash();
		
		//gather array of google map elements
		var map_elements = $$(".google_maps_wrapper");
		
		//check map elements
		if(map_elements.length > 0)
		{
			//loop through array of maps
			map_elements.each(function(map_element)
			{
				//create a new map
				this.create_map(map_element.id);
			}.bind(this));
		}
	},
	
	//wrapper function for creating a new map
	create_map: function(element_id)
	{
		//check element is valid
		if($(element_id) != undefined)
		{
			//set default latitude and longitude
			var lat_long = new google.maps.LatLng(55.0, 0.0);
			
			//set default map options
			var options = {zoom: 5, center: lat_long, mapTypeId: google.maps.MapTypeId.HYBRID, mapTypeControlOptions: {mapTypeIds: [google.maps.MapTypeId.HYBRID, google.maps.MapTypeId.ROADMAP], style: google.maps.MapTypeControlStyle.DROPDOWN_MENU}};
			
			//create a new hash for this map
			var map = new Hash();
			
			//create a new hash for markers on this map
			var markers = new Hash();
			
			//create a new map element with options
			map.set("map", new google.maps.Map($(element_id), options));
			
			//save markers hash to map
			map.set("markers", markers);
			
			//save map and markers to maps hash
			this.maps.set(element_id, map);
		}
	},
	
	//wrapper function for creating a new marker on a map
	create_marker: function(map_id, latitude, longitude, title, marker_id)
	{
		//create a new latitude and longitude
		var lat_long = new google.maps.LatLng(latitude, longitude);
		
		//create a new marker
		var marker = new google.maps.Marker({position: lat_long, title: "Click to toggle", clickable: true, map: this.maps.get(map_id).get("map")});
		
		//create a new info window
		var info_window = new google.maps.InfoWindow({content: title});
		
		//save marker and info window to a new hash
		var data = new Hash();
		data.set("marker", marker);
		data.set("info_window", info_window);
		
		//determine marker id
		var new_marker_id = (marker_id == undefined ? (this.maps.get(map_id).get("markers").size() + 1) : marker_id);
		
		//save marker and info window to map hash
		this.maps.get(map_id).get("markers").set(new_marker_id, data);
		
		//observe marker click events
		google.maps.event.addListener(marker, "click", function() { this.marker_click_event(map_id, this.maps.get(map_id).get("markers").get(new_marker_id)); }.bind(this));
	},
	
	//wrapper function for handling marker click events
	marker_click_event: function(map_id, marker)
	{
		//loop through all markers on this map
		this.maps.get(map_id).get("markers").each(function(hash)
		{
			//close info window
			hash.value.get("info_window").close();
		});
		
		//display marker
		marker.get("info_window").open(this.maps.get(map_id).get("map"), marker.get("marker"));
	},
	
	//wrapper function for centering the map around the given latitude and longitude
	map_pan_to: function(map_id, latitude, longitude)
	{
		//pan map to given latitude and longitude
		this.maps.get(map_id).get("map").panTo(new google.maps.LatLng(latitude, longitude));
	},
	
	//wrapper function for setting zoom level of the map
	set_zoom: function(map_id, level)
	{
		//set map zoom level
		this.maps.get(map_id).get("map").setZoom(level);
	}
};

//declare GoogleMaps class
var GoogleMapsHelper = Class.create();

//declare GoogleMaps prototype
GoogleMapsHelper.prototype = {

	//wrapper function for initialisation
	initialize: function()
	{
		//gather map element
		this.map = $("smcms_cms_map");
		
		//gather map form element
		this.form = $("smcms_map_form");
		
		//gather map table element
		this.table = $("smcms_map_table");
		
		//array of marker ids
		this.marker_ids = new Hash();
		
		//check form element is valid
		if(this.form != undefined)
		{
			//gather image elements used to observe click events
			var image_create = $("smcms_map_icon_create");
			var image_view = $$(".smcms_map_icon_view");
			var image_toggle = $$(".smcms_map_icon_toggle");
			var image_update = $$(".smcms_map_icon_update");
			var image_delete = $$(".smcms_map_icon_delete");
			
			//observe click events for new markers
			Event.observe(image_create, "click", this.marker_create);
			
			//loop through view image elements
			image_view.each(function(element)
			{
				//observe click events for view markers
				Event.observe(element.id, "click", this.marker_view);
			}.bind(this));
			
			//loop through view image elements
			image_toggle.each(function(element)
			{
				//observe click events for toggle markers
				Event.observe(element.id, "click", this.marker_toggle);
			}.bind(this));
			
			//loop through view image elements
			image_update.each(function(element)
			{
				//observe click events for update markers
				Event.observe(element.id, "click", this.marker_update);
			}.bind(this));
			
			//loop through view image elements
			image_delete.each(function(element)
			{
				//observe click events for delete markers
				Event.observe(element.id, "click", this.marker_delete);
			}.bind(this));
		}
		
		//check table element is valid
		if(this.table != undefined)
		{
			//gather all rows in table
			var table_rows = $$("#" + this.table.id + " tr");
			
			//check table rows
			if(table_rows.length > 0)
			{
				//remove table header from table rows
				table_rows = table_rows.without(table_rows.first());
				
				//loop through table row elements
				table_rows.each(function(row)
				{
					//gather cells in table row
					var table_cells = row.childElements();
					
					//check table cells
					if(table_cells.length > 1)
					{
						//save table row id
						var table_row_id = table_cells[1].innerHTML;
						
						//set table row id
						row.id = "row_" + table_row_id;
					}
				});
			}
			
			//gather all hidden form fields
			var hidden_fields = this.form.getInputs("hidden");
			
			//check table fields
			if(hidden_fields.length > 0)
			{
				//loop through hidden fields
				hidden_fields.each(function(field)
				{
					//convert marker data
					var data = this.marker_string_to_hash(field.value);
					
					//save marker id
					this.marker_ids.set(data.id, data.id);
					
					//add map marker
					google_maps.create_marker(this.map.id, data.latitude, data.longitude, "<h1>" + data.title + "</h1><address>" + data.address + "</address>", data.id);
					
					//check enabled flag
					if(data.enabled == 0)
					{
						//disable map marker
						google_maps.maps.get(this.map.id).get("markers").get(data.id).get("marker").setVisible(false);
					}
				}.bind(this));
			}
		}
	},
	
	//wrapper function for creating a new table row
	table_create_row: function(data)
	{
		//check table element is valid
		if(this.table != undefined)
		{
			//gather all rows in table
			var table_rows = $$("#" + this.table.id + " tr");
			
			//check table rows
			if(table_rows.length > 0)
			{
				//remove table header from table rows
				table_rows = table_rows.without(table_rows.first());
				
				//check table rows and columns
				if(table_rows.length == 1 && table_rows.first().childElements().length == 1)
				{
					//remove table row
					table_rows.first().remove();
				}
			}
			
			//create a new table row
			var table_row = new Element("tr", {id: "row_" + data.get("id")});
			
			//gather all rows in table
			var table_rows = $$("#" + this.table.id + " tr");
			
			//check table rows
			if(table_rows.length > 0)
			{
				//remove table header from table rows
				table_rows = table_rows.without(table_rows.first());
				
				//check current rows
				if(table_rows.length % 2)
				{
					//append class name
					table_row.addClassName("smcms_table_row_alt");
				}
			}
			
			//create new table cells
			var table_cell_view = new Element("td");
			var table_cell_id = new Element("td");
			var table_cell_title = new Element("td");
			var table_cell_address = new Element("td");
			var table_cell_lat_long = new Element("td");
			var table_cell_enabled = new Element("td");
			var table_cell_toggle = new Element("td");
			var table_cell_update = new Element("td");
			var table_cell_delete = new Element("td");
			
			//create new image elements
			var image_view = new Element("img", {id: "smcms_map_icon_view_" + data.get("id"), className: "smcms_map_icon_view", width: 16, height: 16, src: "images/smcms/modules/map.png"});
			var image_enabled = new Element("img", {id: "smcms_map_icon_enabled_" + data.get("id"), width: 16, height: 16, src: "images/smcms/modules/module_enabled.png"});
			var image_toggle = new Element("img", {id: "smcms_map_icon_toggle_" + data.get("id"), className: "smcms_map_icon_toggle", width: 16, height: 16, src: "images/smcms/modules/method_toggle.png"});
			var image_update = new Element("img", {id: "smcms_map_icon_update_" + data.get("id"), className: "smcms_map_icon_update", width: 16, height: 16, src: "images/smcms/modules/method_update.png"});
			var image_delete = new Element("img", {id: "smcms_map_icon_delete_" + data.get("id"), className: "smcms_map_icon_delete", width: 16, height: 16, src: "images/smcms/modules/method_delete.png"});
			
			//create span elements
			var span_view = new Element("span", {className: "table_action"});
			var span_enabled = new Element("span", {className: "table_action"});
			var span_toggle = new Element("span", {className: "table_action"});
			var span_update = new Element("span", {className: "table_action"});
			var span_delete = new Element("span", {className: "table_action"});
			
			//observe click events
			Event.observe(image_view, "click", this.marker_view);
			Event.observe(image_toggle, "click", this.marker_toggle);
			Event.observe(image_update, "click", this.marker_update);
			Event.observe(image_delete, "click", this.marker_delete);
			
			//append image elements to span elements
			span_view.insert(image_view);
			span_enabled.insert(image_enabled);
			span_toggle.insert(image_toggle);
			span_update.insert(image_update);
			span_delete.insert(image_delete);
			
			//append cell content
			table_cell_view.insert(span_view);
			table_cell_id.innerHTML = data.get("id");
			table_cell_title.innerHTML = data.get("title");
			table_cell_address.innerHTML = data.get("address");
			table_cell_lat_long.innerHTML = "(" + data.get("longitude") + ", " + data.get("latitude") + ")";
			table_cell_enabled.insert(span_enabled);
			table_cell_toggle.insert(span_toggle);
			table_cell_update.insert(span_update);
			table_cell_delete.insert(span_delete);
			
			//append table cells to table row
			Insertion.Bottom(table_row, table_cell_view);
			Insertion.Bottom(table_row, table_cell_id);
			Insertion.Bottom(table_row, table_cell_title);
			Insertion.Bottom(table_row, table_cell_address);
			Insertion.Bottom(table_row, table_cell_lat_long);
			Insertion.Bottom(table_row, table_cell_enabled);
			Insertion.Bottom(table_row, table_cell_toggle);
			Insertion.Bottom(table_row, table_cell_update);
			Insertion.Bottom(table_row, table_cell_delete);
			
			//append table row to table
			Insertion.Bottom(this.table, table_row);
		}
	},
	
	//wrapper function for updating a table row
	table_update_row: function(element_id, data)
	{
		//gather table row
		var row = $("row_" + element_id);
		
		//check table row is valid
		if(row != undefined)
		{
			//gather table cells
			var cells = row.childElements();
			
			//update table cell contents
			cells[2].innerHTML = data.get("title");
			cells[3].innerHTML = data.get("address");
			cells[4].innerHTML = "(" + data.get("latitude") + ", " + data.get("longitude") + ")";
		}
	},
	
	//wrapper function for deleting a table row
	table_delete_row: function(element_id)
	{
		//gather table row
		var row = $("row_" + element_id);
		
		//check table row is valid
		if(row != undefined)
		{
			//remove row
			row.remove();
		}
	},
	
	//wrapper function for creating a hidden form value
	form_create_field: function(element_id, data)
	{
		//check form element is valid
		if(this.form != undefined)
		{
			//create a new hidden field
			var field = new Element("input", {type: "hidden", id: "smcms_map_marker_" + data.get("id"), name: "smcms_map_marker[" + data.get("id") + "]"});
			
			//convert marker data
			data = this.marker_hash_to_string(data);
			
			//set field value
			field.value = data;
			
			//append field to form
			Insertion.Bottom(this.form, field);
		}
	},
	
	//wrapper function for updating a hidden form value
	form_update_field: function(element_id, data)
	{
		//gather form field
		var field = google_maps_helper.form_gather_field(element_id);
		
		//check field is valid
		if(field != undefined)
		{
			//convert marker data
			data = google_maps_helper.marker_hash_to_string(data);
			
			//set field value
			field.value = data;
		}
	},
	
	//wrapper function for deleting a hidden form value
	form_delete_field: function(element_id)
	{
		//gather form field
		var field = google_maps_helper.form_gather_field(element_id);
		
		//check field is valid
		if(field != undefined)
		{
			//remove field from form
			field.remove();
		}
	},
	
	//wrapper function for gathering a hidden form value
	form_gather_field: function(field_id)
	{
		//gather hidden form field and return
		return this.form.getInputs("hidden", "smcms_map_marker[" + field_id + "]").first();
	},
	
	//wrapper function for creating a new marker
	marker_create: function()
	{
		//create a new modal view
		this.modal_view = new ModalView({modal_title: "Insert Map Marker", modal_form_callback: google_maps_helper.marker_create_callback.bind(this)});
		
		//create a new paragraph form header element
		var form_header = new Element("p");
		form_header.innerHTML = "To create a new marker, enter the text to be displayed in the field below.";
		
		//create label - title
		var input_title_label_text = new Element("p");
		input_title_label_text.innerHTML = "* Title:";
		
		//create a new form input element
		var input_title = new Element("input", {id: "input_title", name: "input_title"});
		
		//create a new form label element
		var input_title_label = new Element("label");
		
		//append form input element to form label
		input_title_label.appendChild(input_title_label_text);
		input_title_label.appendChild(input_title);
		
		//create label - address
		var input_address_label_text = new Element("p");
		input_address_label_text.innerHTML = "* Address:";
		
		//create a new form input element
		var input_address = new Element("input", {id: "input_address", name: "input_address"});
		
		//create a new form label element
		var input_address_label = new Element("label");
		
		//append form input element to form label
		input_address_label.appendChild(input_address_label_text);
		input_address_label.appendChild(input_address);
		
		//append form fields to modal form
		this.modal_view.append_form_element(form_header);
		this.modal_view.append_form_element(input_title_label);
		this.modal_view.append_form_element(input_address_label);
		
		//append modal form to modal view
		this.modal_view.append_form();
		
		//display modal view
		this.modal_view.modal_display();
	},
	
	//wrapper function for viewing a map marker
	marker_view: function(event)
	{
		//gather the element which was clicked
		var element = Event.element(event);
		
		//determine unique element id
		var element_id = element.id.replace("smcms_map_icon_view_", "");
		
		//gather form field
		var field = google_maps_helper.form_gather_field(element_id);
		
		//convert marker data
		var data = google_maps_helper.marker_string_to_hash(field.value);
		
		//check enabled flag
		if(data.enabled == 1)
		{
			//set map zoom level
			google_maps.set_zoom(google_maps_helper.map.id, 7);
			
			//pan map to marker
			google_maps.map_pan_to(google_maps_helper.map.id, data.latitude, data.longitude);
		}
	},
	
	//wrapper function for toggling a map marker
	marker_toggle: function(event)
	{
		//gather the element which was clicked
		var element = Event.element(event);
		
		//determine unique element id
		var element_id = element.id.replace("smcms_map_icon_toggle_", "");
		
		//gather form field
		var field = google_maps_helper.form_gather_field(element_id);
		
		//convert marker data
		var data = google_maps_helper.marker_string_to_hash(field.value);
		
		//toggle enabled vale
		data.enabled = (data.enabled == 1 ? 0 : 1);
		
		//gather and switch toggle image element src
		$("smcms_map_icon_enabled_" + element_id).src = "images/smcms/modules/module_" + (data.enabled == 0 ? "disabled" : "enabled") + ".png";
		
		//toggle map marker and info window
		google_maps.maps.get(google_maps_helper.map.id).get("markers").get(data.id).get("marker").setVisible((data.enabled == 1));
		google_maps.maps.get(google_maps_helper.map.id).get("markers").get(data.id).get("info_window").close();
		
		//convert marker data and set field value
		field.value = google_maps_helper.marker_hash_to_string(data);
	},
	
	//wrapper function for updating a map marker
	marker_update: function(event)
	{
		//gather the element which was clicked
		var element = Event.element(event);
		
		//determine unique element id
		var element_id = element.id.replace("smcms_map_icon_update_", "");
		
		//gather form field
		var field = google_maps_helper.form_gather_field(element_id);
		
		//convert marker data
		var data = google_maps_helper.marker_string_to_hash(field.value);
		
		//create a new modal view
		this.modal_view = new ModalView({modal_title: "Update Map Marker", modal_form_callback: google_maps_helper.marker_update_callback.bind(this)});
		
		//create a new paragraph form header element
		var form_header = new Element("p");
		form_header.innerHTML = "To update this marker, enter the text to be displayed in the field below.";
		
		//create label - title
		var input_title_label_text = new Element("p");
		input_title_label_text.innerHTML = "* Title:";
		
		//create a new form input element
		var input_title = new Element("input", {id: "input_title", name: "input_title"});
		
		//create a new form label element
		var input_title_label = new Element("label");
		
		//append form input element to form label
		input_title_label.appendChild(input_title_label_text);
		input_title_label.appendChild(input_title);
		
		//create label - address
		var input_address_label_text = new Element("p");
		input_address_label_text.innerHTML = "* Address:";
		
		//create a new form input element
		var input_address = new Element("input", {id: "input_address", name: "input_address"});
		
		//create a new form label element
		var input_address_label = new Element("label");
		
		//create a new form input element
		var input_id = new Element("input", {id: "input_id", name: "input_id", type: "hidden"});
		
		//set form values
		input_title.value = data.title.gsub(/\+/, " ");
		input_address.value = data.address.gsub(/\+/, " ");
		input_id.value = data.id;
		
		//append form input element to form label
		input_address_label.appendChild(input_address_label_text);
		input_address_label.appendChild(input_address);
		
		//append form fields to modal form
		this.modal_view.append_form_element(form_header);
		this.modal_view.append_form_element(input_title_label);
		this.modal_view.append_form_element(input_address_label);
		this.modal_view.append_form_element(input_id);
		
		//append modal form to modal view
		this.modal_view.append_form();
		
		//display modal view
		this.modal_view.modal_display();
	},
	
	//wrapper function for deleting a map marker
	marker_delete: function(event)
	{
		//gather the element which was clicked
		var element = Event.element(event);
		
		//determine unique element id
		var element_id = element.id.replace("smcms_map_icon_delete_", "");
		
		//remove form field
		google_maps_helper.form_delete_field(element_id);
		
		//remove table row
		google_maps_helper.table_delete_row(element_id);
		
		//remove map marker
		google_maps.maps.get(google_maps_helper.map.id).get("markers").get(element_id).get("marker").setMap(null);
		google_maps.maps.get(google_maps_helper.map.id).get("markers").unset(element_id);
	},
	
	//wrapper function for handling marker create callbacks
	marker_create_callback: function(event, elements)
	{
		//gather form values
		var input_title = elements[0].getValue();
		var input_address = elements[1].getValue();
		
		//check form value is valid
		if(input_title.length > 0 && input_address.length > 0)
		{
			//create a new geocoder 
			var geocoder = new google.maps.Geocoder();
			
			//geocode address
			geocoder.geocode({"address": input_address}, function(response, status)
			{
				//check status
				if(status == google.maps.GeocoderStatus.OK)
				{
					//create a new hash of data
					var data = new Hash();
					
					//create a new marker id
					var marker_id = 1;
					
					//loop to find a unique marker id
					while(google_maps_helper.marker_ids.get(marker_id) != undefined)
					{
						//increment marker id
						++marker_id;
					}
					
					//set data
					data.set("id", marker_id);
					data.set("title", input_title);
					data.set("address", response[0].formatted_address);
					data.set("latitude", response[0].geometry.location.lat());
					data.set("longitude", response[0].geometry.location.lng());
					data.set("enabled", 1);
					
					//save marker id
					google_maps_helper.marker_ids.set(marker_id, marker_id);
					
					//create a new table row
					google_maps_helper.table_create_row(data);
					
					//create a new form field
					google_maps_helper.form_create_field(marker_id, data);
					
					//add map marker
					google_maps.create_marker(google_maps_helper.map.id, data.get("latitude"), data.get("longitude"), "<h1>" + data.get("title") + "</h1><address>" + data.get("address") + "</address>", data.get("id"));
				}
			});
		}
	},
	
	//wrapper function for handling marker update callbacks
	marker_update_callback: function(event, elements)
	{
		//gather form values
		var input_id = elements[2].getValue();
		var input_title = elements[0].getValue();
		var input_address = elements[1].getValue();
		
		//check form value is valid
		if(input_id.length > 0 && input_title.length > 0 && input_address.length > 0)
		{
			//create a new geocoder 
			var geocoder = new google.maps.Geocoder();
			
			//geocode address
			geocoder.geocode({"address": input_address}, function(response, status)
			{
				//check status
				if(status == google.maps.GeocoderStatus.OK)
				{
					//gather form field
					var field = google_maps_helper.form_gather_field(input_id);
					
					//create a new hash of data
					var hash = new Hash();
					
					//check field is valid
					if(field != undefined)
					{
						//convert marker data
						var data = google_maps_helper.marker_string_to_hash(field.value);
						
						//save marker data
						hash.set("id", data.id);
						hash.set("title", data.title);
						hash.set("address", data.address);
						hash.set("latitude", data.latitude);
						hash.set("longitude", data.longitude);
						hash.set("enabled", data.enabled);
					}
					
					//save updated marker data
					hash.set("title", input_title);
					hash.set("address", response[0].formatted_address);
					hash.set("latitude", response[0].geometry.location.lat());
					hash.set("longitude", response[0].geometry.location.lng());
					
					//update form field
					google_maps_helper.form_update_field(input_id, hash);
					
					//update table row
					google_maps_helper.table_update_row(input_id, hash);
				}
			});
		}
	},
	
	//convert a string to a hash
	marker_string_to_hash: function(string)
	{
		//convert query string to hash and return
		return string.toQueryParams();
	},
	
	//convert a hash to a string
	marker_hash_to_string: function(hash)
	{
		//convert hash to query string and return
		return Object.toQueryString(hash);
	}
};
