var CAPETIDES = {
	utils : {},
	location : {}
};

/******************************************************************************
 **********                         Utils                            **********
 ******************************************************************************/

CAPETIDES.utils.getObject = function(id)
{	
	var object = document.getElementById(id);
	
	if (object == null)
	{
		throw("Failed to find object with id " + id + ".");
	}
	
	return object;
}

/* Takes a select list object and an array objects. The objects must contain
 * text and value properties. Returns true if the selected value was found.
 * Returns false otherwise. */

CAPETIDES.utils.populateSelectList = function(selectList, options,
		selectedValue)
{
	var foundSelectedValue = false;
	
	CAPETIDES.utils.clearSelectList(selectList);
	
	for (var i = 0; i < options.length; i++)
	{
		var option = options[i];
		
		var selected = false;
		
		if (selectedValue)
		{
			if (option.value == selectedValue)
			{
				selected = true;
				
				foundSelectedValue = true;
			}
		}
		
		CAPETIDES.utils.addSelectListOption(selectList, option.text,
				option.value, selected)
	}
	
	return foundSelectedValue;
};

CAPETIDES.utils.addSelectListOption = function(selectList, text, value,
		selected)
{
	var option = new Option(text, value);
	
	option.selected = selected
	
	try
	{
		selectList.add(option, null);
	}
	catch(ex)
	{
		selectList.add(option); // IE
	}
	
	// NOTE: In addition to setting the selected property of the option, we're
	// going to set the selected index. This appears to be necessary in at least
	// Firefox. Otherwise, the first option in the select list will be selected.
	
	if (selected)
	{
		selectList.selectedIndex = selectList.options.length - 1;
	}
};

CAPETIDES.utils.clearSelectList = function(selectList)
{
	for (var i = selectList.length - 1; i >= 0; i--)
	{
		selectList.options[i] = null;
	}
};

CAPETIDES.utils.selectOption = function(selectList, optionValue)
{
	for (var i = 0; i < selectList.length; i++)
	{
		var option = selectList.options[i];
		
		if (option.value == optionValue)
		{
			option.selected = true;
			
			return true;
		}
	}
	
	return false;
};

CAPETIDES.utils.getSelectedValue = function(selectList)
{
	if (selectList.selectedIndex == -1)
	{
		return;
	}
	
	return selectList.options[selectList.selectedIndex].value;
};

/******************************************************************************
 **********                       Search Form                        **********
 ******************************************************************************/

CAPETIDES.location.LocationCollection = function()
{
	this._locations = [];
	
	/* Registers a location. The location will be added to the map when the map
	 * is shown. */
	
	this.add = function(id, name, latitude, longitude)
	{
		this._locations.push({
			"id" : id,
			"name" : name,
			"latitude" : latitude,
			"longitude" : longitude
		});
	};
	
	/* Returns an array containing all of the locations. */
	
	this.get = function()
	{
		return this._locations;
	};
	
	this.exists = function(locationID)
	{
		for (var i = 0; i < this._locations.length; i++)
		{
			var location = this._locations[i];
			
			if (location.id == locationID)
			{
				return true;
			}
		}
		
		return false;
	};
};

/* A logical map object. This is a wrapper for the Google map, the YUI panel
 * which contains it, and the locations that are shown on the map. */

CAPETIDES.location.Map = function(id, formElementID, locations, callback)
{
	/* Properties. */
	
	this._panelID = id;
	this._mapID = id + "-map";
	this._locationID = id + "-location";
	this._cancelID = id + "-cancel";
	this._callback = callback;
	
	this._formElement = CAPETIDES.utils.getObject(formElementID);
	this._locations = locations;
	this._locationsMapped = false;
	
	/* Creates a Google Map using the specified HTML element ID. */
	
	this._createMap = function(id)
	{
		var element = CAPETIDES.utils.getObject(id);
		
		var map = new GMap2(element);
		
		map.addControl(new GSmallMapControl());
		
		this._centerMap(map);
		
		return map;
	};
	
	/* Sets the center coordinate for the specified map. */
	
	this._centerMap = function(map)
	{
		var point = new GLatLng(41.71393, -70.337219);
		
		map.setCenter(point, 9);
	};
	
	/* Creates a YUI panel using the specified element ID. */

	this._createPanel = function(panelID, mapID, locationID, cancelID)
	{
		var options = this._createPanelOptions();
		
		var panel = new YAHOO.widget.Panel(panelID, options);
		
		panel.setHeader("Pick a Location");
		
		panel.setBody('\
			<div id="' + mapID + '" class="mapContainer"></div>\
			<div id="' + locationID + '" style="padding: 10px;">\
				Move your cursor over the map.\
			</div>\
			<div align="center">\
				<input type="button" id="' + cancelID + '" class="button"\
						value="Cancel">\
			</div>\
		');
		
		panel.render(document.body);
		
		// Add a click event to the cancel button.
		
		var map = this;
		
		var cancel = CAPETIDES.utils.getObject(cancelID);
		
		YAHOO.util.Event.addListener(cancel, "click",
				function() { map.hide(); } );
		
		return panel;
	};
	
	/* Creates a YUI panel options object which is used to initialize the panel
	 * object. */
	
	this._createPanelOptions = function()
	{
		return {
			width : "508px",
			fixedcenter: true,
			constraintoviewport : true,
			close : true,
			visible : false,
			modal : true
		};
	}
	
	/* Displays the map. */
	
	this.show = function()
	{
		if (!this._initialized)
		{
			this._initialize();
		}
		
		this._panel.show();
	};
	
	/* Hides the map. */
	
	this.hide = function()
	{
		this._panel.hide();
	};
	
	/* Deslects the currently selected location and closes the map. */
	
	this.deselect = function()
	{
		this._formElement.selectedIndex = 0;
		
		this.hide();
	};
	
	/* Adds all the locations to the Google map. */
	
	this._mapLocations = function()
	{
		// Toggle our flag first to minimize the chance of race conditions.
		
		this._locationsMapped = true;
		
		var locations = this._locations.get();
		
		for (var i in locations)
		{
			var location = locations[i];
			
			this._mapLocation(location);
		}
	};
	
	/* Adds the specified location to the Google map. */
	
	this._mapLocation = function(location)
	{
		var coordinates = new GLatLng(location.latitude, location.longitude);
		
		var marker  = new GMarker(coordinates);
		
		var map = this;
		
		GEvent.addListener(marker, "mouseover", function() { map.setTitle(location.name); });
		GEvent.addListener(marker, "click", function() { map.select(location) });
		
		this._map.addOverlay(marker);
	};
	
	/* Updates the title of the map. */
	
	this.setTitle = function(title)
	{
		var element = CAPETIDES.utils.getObject(this._locationID);
		
		element.innerHTML = title;
	};
	
	/* Selects the specified location and closes the map. */
	
	this.select = function(location)
	{
		CAPETIDES.utils.selectOption(this._formElement, location.id);
		
		this.hide();
		
		if (typeof this._callback == "function")
		{
			this._callback();
		}
	};
	
	this._initialize = function()
	{
		this._panel = this._createPanel(this._panelID, this._mapID,
			this._locationID, this._cancelID);
		
		this._map = this._createMap(this._mapID);
		
		this._mapLocations();
	};
};

/* Represents the form which utilizes a location map. */

CAPETIDES.location.Form = function(form, locationType, locationID, mapLink,
		tideLocations, currentLocations, initialLocationID, callback)
{
	// Form elements
	 
	this._form = form;
	this._locationType = locationType;
	this._locationID = locationID;
	this._mapLink = mapLink;
	
	var Map = CAPETIDES.location.Map;

	this._tideMap = new Map("tides", locationID, tideLocations, callback);
	this._tideLocations = tideLocations;
	this._selectedTideLocationID = 0;
	
	this._currentMap = new Map("currents", locationID, currentLocations);
	this._currentLocations = currentLocations;
	this._selectedCurrentLocationID = 0;
	
	/* Returns the map corresponding to the currently selected type. */

	this._getMap = function()
	{
		var type = this._getSelectedLocationType();
			
		var map = (type == "tides") ? this._tideMap : this._currentMap;
			
		return map;
	};
	
	/* Launches the map corresponding to the selected location type (tides or
	 * currents). */
	
	this.showMap = function()
	{
		var map = this._getMap();
		
		map.show();
	};
	
	/* Hides the map corresponding to the selected location type (tides or
	 * currents). */
	
	this.hideMap = function()
	{
		var map = this._getMap();
		
		map.hide();
	};
	
	/* Adds the event handlers to the form controls. */

	this._addEventHandlers = function()
	{
		var form = this;

		var locationType = this._getLocationType();
		
		YAHOO.util.Event.addListener(locationType, "change",
				function() { form.handleLocationTypeChange(); });
		
		var link = this._getMapLink();
		
		YAHOO.util.Event.addListener(link, "click",
				function() { form.showMap() });
	};
	
	/* Adds the location type options. */
	
	this._setLocationTypeOptions = function()
	{
		var locationType = this._getLocationType();	
		
		CAPETIDES.utils.addSelectListOption(locationType, "Tides", "tides");
		CAPETIDES.utils.addSelectListOption(locationType, "Currents", "currents");
	};

	/* Sets the initial location and it's corresponding type. */

	this._setInitialLocation = function(initialLocationID)
	{
		var locationType = this._getLocationType();
		
		if (this._currentLocations.exists(initialLocationID))
		{
			CAPETIDES.utils.selectOption(locationType, "currents");
		}
		else
		{
			CAPETIDES.utils.selectOption(locationType, "tides");
		}
		
		// Manually invoke the change event on the drop down so that we get the same
		// behavior as if a user selected the option.
		
		this.handleLocationTypeChange();
		
		// At this point, we should have the correct list of locations. Select our
		// location.
		
		var locationID = this._getLocationID();
		
		CAPETIDES.utils.selectOption(locationID, initialLocationID);
		
		// Enable the location type and location ID form elements.
		
		locationType.disabled = false;
		locationID.disabled = false;
	};
	
	/* Handles the locationType change event. */
	
	this.handleLocationTypeChange = function()
	{
		// Disable the location ID field temporarilly.
		
		var locationID = this._getLocationID();
		
		locationID.disabled = true;
		
		// Determine the type and update the locations accordingly.
		
		var type = this._getSelectedLocationType();
		
		this._setLocationsByType(type);
		
		// Re-enable the location ID field.
		
		locationID.disabled = false;
	};
	
	this._setLocationsByType = function(type)
	{
		var locations;
		var selectedLocationID;
		
		if (type == "tides")
		{
			// Switching from currents to tides.
			
			locations = this._tideLocations;
			selectedLocationID = this._selectedTideLocationID;
			
			// Record the selected current. If we don't have one, leave the current
			// value intact. This handles the load state.
			
			this._selectedCurrentLocationID = this._getSelectedLocationID() ||
					this._selectedCurrentLocationID;
		}
		else
		{
			// Switching from tides to currents.
			
			locations = this._currentLocations;
			selectedLocationID = this._selectedCurrentLocationID;
			
			// Record the selected tide. If we don't have one, leave the current
			// value intact. This handles the load state.
			
			this._selectedTideLocationID = this._getSelectedLocationID() ||
					this._selectedTideLocationID;
		}
		
		this._populateLocations(locations, selectedLocationID);
	};
	
	/* Returns a reference to the search form. */
	
	this._getForm = function()
	{
		return document.forms[this._form];
	};
	
	/* Returns a reference to the location type select list. */
	
	this._getLocationType = function()
	{
		var form = this._getForm();
		
		var select = form[this._locationType];
		
		return select;
	};
	
	/* Returns the selected location type value. */
	
	this._getSelectedLocationType = function()
	{
		var select = this._getLocationType();
		
		var value = CAPETIDES.utils.getSelectedValue(select);
		
		return value;
	};
	
	/* Returns a reference to the location ID select list. */
	
	this._getLocationID = function()
	{
		var form = this._getForm();
		
		var select = form[this._locationID];
		
		return select;
	};
	
	/* Returns the selected location ID value. */
	
	this._getSelectedLocationID = function()
	{
		var select = this._getLocationID();
		
		var value = CAPETIDES.utils.getSelectedValue(select);
		
		return value;
	};
	
	this._getMapLink = function()
	{
		var link = CAPETIDES.utils.getObject(this._mapLink);
		
		return link;
	};
	
	/* Populates the location ID drop down with the specified location
	 * collection. Sets the selected location ID. */
	
	this._populateLocations = function(locations, locationID)
	{
		var select = this._getLocationID();
		
		var options = this._locationsToOptions(locations);
		
		CAPETIDES.utils.populateSelectList(select, options, locationID);
	};
	
	/* Converts a LocationCollection object to an array of select list options which
	 * can be appended to a select list. */
	
	this._locationsToOptions = function(locationCollection)
	{
		var options = [];
		
		var locations = locationCollection.get();
		
		for (var i = 0; i < locations.length; i++)
		{
			var location = locations[i];
			
			options.push({
				"text" : location.name,
				"value" : location.id
			});
		}
		
		return options;
	};
	
	/* Initialization. */
	
	this._setLocationTypeOptions();
	
	this._addEventHandlers();
	
	this._setInitialLocation(initialLocationID);
};