var map, layer; // Map and base layer
var src; // Standard map coordinate space
var dst; // Destination map coordinate space
var cores = new Array(); // The data to be displayed
var points = new Array(); // Markers that were added to the map
var pointCount = 0; // The number of points added to the map
var currentListItem = null; // The currently selected list item
var listItemPreviousColor = null; // The selected list item's previous color.
var previousClickIndex = -1; // The index into points that was previously
// selected.
var hoverInColor; // The color that the list item was before it was hovered
// over by the mouse.
var currentPopup = null; // The marker that is currently showing a popup
var iAmnt = 11; // How much to increase the size of a marker on the map when
// hovered over by the mouse.

/**
 * A rounded bubble with one side not rounded.
 */
var autoSizeAnchored = OpenLayers.Class(OpenLayers.Popup.AnchoredBubble, {
	"autoSize" :true
});

/**
 * Registers events that will be called when loading map tiles to give some
 * feedback to users.
 * 
 * @param layer
 *            OpenLayers Map
 * @return
 */
function registerEvents(layer) {

	var pulseFunction = function(message, speed) {
		message.fadeTo(speed, 1.0);
		message.fadeTo(speed, 0.6, function() {
			pulseFunction(message, speed);
		});
	}

	layer.logEvent = function(message) {
		document.getElementById("eventMessage").innerHTML = message;
	};

	layer.events.register("loadstart", layer, function() {
		this.logEvent("Loading map ...");
		$("#eventMessage").stop(true);
		pulseFunction($("#eventMessage"), "slow");
	});

	layer.events.register("tileloaded", layer, function() {

		if (this.numLoadingTiles > 1) {
			this.logEvent("Loading map: " + this.numLoadingTiles
					+ " tiles left.");
		} else {
			this.logEvent("Loading map: " + this.numLoadingTiles
					+ " tile left.");
		}
		$("#eventMessage").stop(true);
		pulseFunction($("#eventMessage"), "slow");
	});

	layer.events.register("loadend", layer, function() {
		this.logEvent("Loading complete.");
		// Grid:" + this.grid.length + "x"
			// + this.grid[0].length);
			$("#eventMessage").stop(true);
			$("#eventMessage").fadeTo(250, 1.0);
			$("#eventMessage").fadeTo(5000, 0.0);
		});

	map.addLayer(layer);
}

/**
 * Creates a marker from the standard coordinates and data and translates them
 * accordingly so that it can be added to the map.
 * 
 * @param layer
 *            The layer to add the marker
 * @param name
 *            The name of the marker
 * @param longitude
 * @param latitude
 * @param link
 *            The data info link
 * @param image
 *            The icon to use for the marker on the map
 * @return
 */
function createMarker(layer, name, longitude, latitude, link, image) {

	var data = {};
	var location;
	var iconSize, iconOffset;

	// transforming point coordinates
	location = new OpenLayers.LonLat(parseFloat(longitude),
			parseFloat(latitude));
	location.transform(src, dst); // transformation done inplace

	iconSize = new OpenLayers.Size(9, 14);

	iconOffset = new OpenLayers.Pixel(-(iconSize.w / 2), -iconSize.h);

	var url = OpenLayers.Util.getImagesLocation() + image;
	var size = new OpenLayers.Size(9, 14);
	var calculateOffset = function(size) {
		return new OpenLayers.Pixel(-(size.w / 2), -size.h);
	};

	data.icon = new OpenLayers.Icon(url, size, null, calculateOffset);

	data.popupContentHTML = "<h2 style=\"margin-left: 5px; margin-bottom: 0px;\">"
			+ name
			+ "</h2>"
			+ "<table><tr><td>"
			+ "lon: </td><td align=\"right\">"
			+ longitude
			+ "</td></tr>"
			+ "<tr><td>lat: </td><td align=\"right\">"
			+ latitude
			+ "</td></tr></table>"
			+ "<span style=\"text-align:center; margin-left: 5px;\">"
			+ link
			+ "</span>";

	data.overflow = "auto";

	var markerFeature = new OpenLayers.Feature(layer, location, data);

	markerFeature.closeBox = true;
	markerFeature.popupClass = autoSizeAnchored;
	markerFeature.data.overflow = "auto";

	layer.features.push(markerFeature);

	var marker = markerFeature.createMarker();

	points[pointCount] = {};
	points[pointCount].layer = layer;
	points[pointCount].markerFeature = markerFeature;
	points[pointCount].marker = marker;
	points[pointCount].name = name;
	points[pointCount].icon = data.icon;
	points[pointCount].pointsIndex = pointCount;
	pointCount++;

	var markerClick = function(evt) {
		if (this.popup == null) {
			this.popup = this.createPopup(this.closeBox);
			map.addPopup(this.popup);
			this.popup.show();
		}

		if (this.popup !== currentPopup) {
			if (currentPopup !== null) {
				currentPopup.hide();
			}
			currentPopup = this.popup;
			this.popup.show();
		} else {
			this.popup.toggle();
		}

		currentPopup = this.popup;
		OpenLayers.Event.stop(evt);

	};
	marker.events.register("click", markerFeature, markerClick);

	marker.events.register("mouseover", markerFeature, function(e) {
		var markerIcon = data.icon;
		if (!markerFeature.expanded) {
			var newSize = new OpenLayers.Size(markerIcon.size.w + iAmnt,
					markerIcon.size.h + iAmnt);
			markerIcon.setSize(newSize);
			markerFeature.expanded = true;
		}
	});

	marker.events.register("mouseout", markerFeature, function(e) {

		var markerIcon = data.icon;

		if (markerFeature.expanded && !markerFeature.selected) {
			var newSize = new OpenLayers.Size(markerIcon.size.w - iAmnt,
					markerIcon.size.h - iAmnt);
			markerIcon.setSize(newSize);
			markerFeature.expanded = false;
		}
	});

	return marker;
}

/**
 * Create the map and add all of the data points to it.
 * 
 * @return
 */
function init() {

	src = new OpenLayers.Projection("EPSG:4326"); // Standard longitude,
	// latitude
	dst = new OpenLayers.Projection("EPSG:3031"); // Map coordinate meters

	OpenLayers.ProxyHost = "/proxy/?url=";
	map = new OpenLayers.Map("map", {
		projection :dst,
		displayProjection :dst,
		maxResolution :"auto",
		maxExtent :new OpenLayers.Bounds(-4051820, -3051820, 4051820, 3051820),
		units :"m"
	});

	layer = new OpenLayers.Layer.WMS("A-CAP",
			"http://nsidc.org/cgi-bin/acap.pl?", {
				layers :new Array("sea_ice_extent_01", "moa", "coastlines",
						"south_pole_geographic"),
				maxExtent :new OpenLayers.Bounds(-12400000, -12400000,
						12400000, 12400000)
			});

	map.addLayer(layer);
	registerEvents(layer);

	var l3 = new OpenLayers.Layer.WMS("A-CAP Cryospheric",
			"http://nsidc.org/cgi-bin/acap.pl?", {
				layers :new Array("sea_ice_concentration_02",
						"glas_dem,coastlines", "geographic_features_ice",
						"geographic_features_sea", "south_pole_geographic"),
				maxExtent :new OpenLayers.Bounds(-12400000, -12400000,
						12400000, 12400000)
			});

	map.addLayer(l3);
	registerEvents(l3);

	var l2 = new OpenLayers.Layer.MapServer(
			"GMRT Topography",
			"http://www.marine-geo.org/exe/mapserv?",
			{
				map :"/system/link/server/apache/htdocs/marine-geo/map/wms_SP.map",
				layers :"South_Polar_Bathymetry"
			});

	map.addLayer(l2);
	registerEvents(l2);

	addMapIcons();

	map.addControl(new OpenLayers.Control.LayerSwitcher());
	map.addControl(new OpenLayers.Control.MousePosition( {
		displayProjection :src
	}));

	map
			.zoomToExtent(new OpenLayers.Bounds(-2051820, -2051820, 2051820,
					2051820));

	var pointList = document.getElementById("pointList");

	var sortedList = new Array();

	var i;
	for (i = 0; i < points.length; i++) {
		sortedList[i] = points[i];
	}

	/*
	 * Sort the data points by name so that we can place them on the right side
	 * of the map. This could be done by a database call, but that would require
	 * another call to the server.
	 */
	sortedList.sort( function(a, b) {
		if (a.name < b.name)
			return -1;
		else if (a.name > b.name)
			return 1;
		else
			return 0;
	});

	/*
	 * Add the data points to a list along the right side of the map.
	 */
	for (i = 0; i < sortedList.length; i++) {
		var li = document.createElement("tr");
		var text = document.createTextNode(sortedList[i].name);
		var td = document.createElement("td");
		td.appendChild(text);
		li.appendChild(td);
		pointList.appendChild(li);

		/*
		 * When the mouse hovers over a list item move the item to the front of
		 * the drawing stack and increase its size on the map.
		 */
		var mouseOverFunction = function(index, listItem) {
			return function(e) {

				if (currentListItem !== listItem) {

					hoverInColor = listItem.style.backgroundColor;
					listItem.style.backgroundColor = "silver";

					var layer = points[index].layer;

					layer.removeMarker(points[index].marker);
					layer.addMarker(points[index].marker);

					map.raiseLayer(layer, 10);

					if (!points[index].markerFeature.expanded) {
						var icon = points[index].icon;
						var size = new OpenLayers.Size(icon.size.w + iAmnt,
								icon.size.h + iAmnt);
						icon.setSize(size);
						points[index].markerFeature.expanded = true;
					}
				}

			}
		}(sortedList[i].pointsIndex, li);

		if (li.addEventListener) {
			li.addEventListener("mouseover", mouseOverFunction, false);
		} else {
			li.attachEvent("onmouseover", mouseOverFunction);
		}

		/*
		 * Set the current list item as selected. This will cause a popup to
		 * occur over the associated marker on the map, and it will make the map
		 * pan to be centered on the item. The item will remained selected until
		 * it is clicked on again, or another list item is selected.
		 */
		var mouseClickFunction = function(index, listItem) {
			return function(e) {

				if (currentListItem !== listItem) {
					if (currentListItem !== null) {
						var icon = points[previousClickIndex].icon;
						var size = new OpenLayers.Size(icon.size.w - iAmnt,
								icon.size.h - iAmnt);
						icon.setSize(size);
						currentListItem.style.backgroundColor = hoverInColor;
						points[previousClickIndex].markerFeature.expanded = false;
					}

					listItemPreviousColor = listItem.style.backgroundColor;

					listItem.style.backgroundColor = "gray";
					var layer = points[index].layer;

					points[index].markerFeature.selected = true;

					layer.removeMarker(points[index].marker);
					layer.addMarker(points[index].marker);

					map.raiseLayer(layer, 10);
					map.panTo(points[index].markerFeature.lonlat);

					currentListItem = listItem;
					previousClickIndex = index;
				} else if (currentListItem === listItem) {

					currentListItem.style.backgroundColor = listItemPreviousColor;
					currentListItem = null;
					listItemPreviousColor = null;
					previousClickIndex = -1;
					points[index].markerFeature.selected = false;

				}

				points[index].marker.events.triggerEvent("click", e);
			};
		}(sortedList[i].pointsIndex, li);

		if (li.addEventListener) {
			li.addEventListener("click", mouseClickFunction, false);
		} else {
			li.attachEvent("onclick", mouseClickFunction);
		}

		/*
		 * Cause the associated marker on the map to decrease in size.
		 */
		var mouseOutFunction = function(index, listItem) {
			return function(e) {

				if (currentListItem !== listItem) {
					listItem.style.backgroundColor = hoverInColor;

					if (points[index].markerFeature.expanded) {
						var icon = points[index].icon;
						var size = new OpenLayers.Size(icon.size.w - iAmnt,
								icon.size.h - iAmnt);
						icon.setSize(size);
						points[index].markerFeature.expanded = false;
					}
				}
			}
		}(sortedList[i].pointsIndex, li);

		if (li.addEventListener) {
			li.addEventListener("mouseout", mouseOutFunction, false);
		} else {
			li.attachEvent("onmouseout", mouseOutFunction);
		}

	}

	$("#mapCorners").corner("bevel 8px").parent().css('padding', '5px').corner(
			"bevel 10px");

}

function addMapIcons() {

	var curDataType, prevDataType;
	var typeLayer;

	var markers = [ "marker.png", "marker-blue.png", "marker-gold.png",
			"marker-green.png" ];
	var markerCount = 0;

	for ( var i = 0; i < cores.length; i++) {
		var c = cores[i];

		curDataType = prevDataType = c.dataType;
		typeLayer = new OpenLayers.Layer.Text(curDataType);

		while (curDataType === prevDataType) {
			typeLayer.addMarker(createMarker(typeLayer, c.name, c.longitude,
					c.latitude, c.link, markers[markerCount]));
			i++;
			prevDataType = curDataType;
			if (i < cores.length) {
				c = cores[i];
				curDataType = c.dataType;
			} else {
				curDataType = prevDataType + "1"; // just make it different
			}

		}
		i--;
		map.addLayer(typeLayer);
		markerCount = (markerCount + 1) % markers.length;
	}
}
