var map;
var gMapserver = null;
var gUrlPhp = g_ROOT_URL + "/seamap2/main/seamap2_gm.php";

// Google Maps initial center location and zoom level.
// Can be changed for each page.
var gInitLoc = {latitude: 10, longitude: 0, zoom: 1}; 	

//if (window.location.href.indexOf("seamap-dev") > 0 || window.location.href.indexOf("seamap2") > 0) {
if (g_DEV) {
	var gSEAMAP2 = true;
	var gPlone = BASE_PLONE_URL;		// For seamap-dev.env
	if (window.location.href == "http://seamap-dev.env.duke.edu/front_page_test.html") {
		var gIconBase = "seamap2/icons/";
	} else {
		var gIconBase = "../seamap2/icons/";
	}
} else {
	/*
	var gSEAMAP2 = false;
	var gPlone = BASE_PLONE_URL + "datasets/";	// for seamap.evn.duke.edu:8888
	var gIconBase = "../icons/";
	*/
	var gSEAMAP2 = true;
	var gPlone = BASE_PLONE_URL;		// For seamap-dev.env
	if (window.location.href == "http://seamap-dev.env.duke.edu/front_page_test.html") {
		var gIconBase = "seamap2/icons/";
	} else {
		var gIconBase = "../seamap2/icons/";
	}
}

var gWinW;
var gWinH;

var myTileOverlay;
var gSeamapLayerOpacity = 1.0;
var gClickedPoint;
var gROITransparency = 0.7;
var gCell = null;
var gZoom = {"01": 5, "001": 9};
var gMaxDim = {maxWidth:320, maxHeight: 160};
var gPolygonInEdit = false;

// The following global variables are used in dataset_detail and species_profile
var gActiveLayer = "dist"	// [dist|point]
var gLayerName = "";		// Set when the point layer is shown. This is the name appeared in mapfile
var gRelatedLayerName = "";

var gYearMin;
var gYearMax;
var gYearStart;
var gYearEnd;
var gYearSlider1;
var gYearSlider2;

var gPrevTaxa = "";
var gRangeCriteria = false;
var gSpeciesList = [];
var gTemporalScale = 2;	// Century:0, Decade:1, Year:2, Month:3, Day:4; Season:-1; seasonal month:-2


gQuery = {
	sp_tsn: "",	// List of TSNs comma-separated. Get value from species search textbox.
	sp_tsns: "",	// set from other methods. e.g. protected species checkbox in species page.
	datasets: "",	// List of dataset IDs comma-separated.
	dataset: "", 	// Single dataset choosen
	provider_datasets: "",	// Set when a provider is chosen in datasets.html
	spatial: "",
	temporal: "",
	criteria: "", 	// Additional search criteria mainly used for species search
	temporalZ: "",
	taxaColumn: "all_taxa",
	temporalScale: 2,		// Century:0, Decade:1, Year:2, Month:3, Day:4; Season:-1; seasonal month:-2
	publish: "publish",
	layer_type: "dataset",
	additionalWhere: function(mode) {return {};},		// Replace this function with specialized one (e.g. for ESAS)
	
	toQueryString: function(mode) {
		var tsns = "";
		if (this.sp_tsns != "") {
			tsns = this.sp_tsns;
		}
		if (this.sp_tsn != "") {	// If a single species is chosen, devoid this.sp_tsns
			tsns = this.sp_tsn;
		}
		
		var datasets = "";
		if (this.datasets != "") {
			datasets = this.datasets;
		}
		if (this.provider_datasets != "") {
			datasets = this.provider_datasets;
		}
		if (this.dataset != "") {
			datasets = this.dataset;
		}
		
		var params = $H({toQueryString: mode, sp_tsn: tsns, datasets: datasets, spatial: this.spatial, temporal_scale: this.temporalScale, temporal: this.temporal, publish: this.publish});
		
		switch (mode) {
			case "chart":
			case "dist_layer":
				params = params.merge({criteria: this.criteria});
				break;
			case "chart_z_union":
				var spatial = this.spatialZ();
				params.update({spatial: spatial});
				break;
			case "z_union":
				var spatial = this.spatialZ();
				var spatial_effort = this.spatial;
				var temporal = this.temporalZ;
				if (typeof(gSeriesSelected) != "undefined" && gSeriesSelected != "") {
					var series = gSeriesSelected;
				} else {
					var series = "";
				}
				params.update({spatial: spatial, spatial_effort: spatial_effort, temporal: temporal, series: series});
				break;
			default:		// facts
				break;
		}
		
		params = params.merge(this.additionalWhere(mode));
		params = params.toQueryString();
		return params;
	},
	
	spatialZ: function() {
		return this.spatial.replace(/geom(?=[,\s])/g, "_geom");
	},
	
	clearTemporal: function() {
		this.temporal = "";
		this.temporalZ = "";
	}
}

if (is_ie) {
	var gTableRow = 'block';
	var gTableCell = 'block';
} else {
	var gTableRow = 'table-row';
	var gTableCell = 'table-cell';
}


function load() {
	if (GBrowserIsCompatible()) {
		map = new GMap2($("map"), {draggableCursor:"default", backgroundColor: '#1D1C56'});
		map.addControl(new GLargeMapControl());
		//map.addControl(new GHierarchicalMapTypeControl());
		map.addControl(new GMapTypeControl());

		map.setCenter(new GLatLng(gInitLoc["latitude"], gInitLoc["longitude"]), gInitLoc["zoom"], G_SATELLITE_MAP);

		var copyright = new GCopyright(1,
		   new GLatLngBounds(new GLatLng(23,122),new GLatLng(46,151) ),
		    1, "OBIS-SEAMAP");

		var copyrightCollection = new GCopyrightCollection('SEAMAP Distribution');
		copyrightCollection.addCopyright(copyright);

		var myTileLayer = new GTileLayer(copyrightCollection, 1, 16);
		myTileLayer.getTileUrl = myGetTileUrl;
		myTileLayer.getOpacity = function() { return gSeamapLayerOpacity; }
		myTileLayer.isPng = function() { return false; }	// Somehow IE6 doesn't show overlays in PNG if there is a polygon.

		myTileOverlay = new GTileLayerOverlay(myTileLayer);

		map.addOverlay(myTileOverlay);

		GEvent.addListener(map, "click", on_click_identify);
		GEvent.addListener(map, "infowindowclose", erase_cell);
		if ($('current_position')) {
			GEvent.addListener(map, 'mousemove', on_mousemove_coordinate);
		}
		
		if (typeof(map_loaded) == 'function') {
			map_loaded();
		}
	}
}

function initialize_mapfile() {
	var layer_name = choose_layer(1, false);
	
	var action = "initialize_mapfile";
	var where = gQuery.toQueryString("dist_layer");
	gMapserver.request(action, mapfile_initialized,
		{
			layer_name: layer_name,
			layer_type: "dist",
			publish: gQuery.publish,
			taxa_column: "all_taxa"
		}, where, {});
}

function mapfile_initialized(oj) {
	var return_value = oj.responseText.evalJSON();
	gMapserver.parameters.sid = return_value.sid;
	load();
}

function update_layout () {
	window_resize();
}

function adjust_portal_wrapper(window_width) {	// Plone 3 site
	if ($("visual-portal-wrapper")) {	// Plone 3 site
		if (window_width > 1050) {
			var wrapper_margin = window_width - 1050;
		} else {
			var wrapper_margin = 0;
		}
		$("visual-portal-wrapper").setStyle({"marginRight": wrapper_margin + "px"}); // Fixed width, centered.
	}
}

function update_map() {
	gMapserver.refresh();
	myTileOverlay.refresh();
}

/***** Mapserver layer definition *****/
// Return URL to get a tile image from Mapserver.
function myGetTileUrl(tile, zoom) {
    // max zoom plus 1
   	var layer_name = choose_layer(zoom, true);
    var projection = new GMercatorProjection(18);

    // Four vertices location in pixcel in GPoint coordinates
    var p1 = new GPoint(tile.x*256,tile.y*256);
    var p2 = new GPoint(p1.x+256,p1.y+256);

    // latitude/longitude of four vertices location in decimal degree
    var latlng1 = projection.fromPixelToLatLng(p1,zoom);
    var latlng2 = projection.fromPixelToLatLng(p2,zoom);

    var lat1 = latlng1.lat();
    var lon1 = latlng1.lng();
    var lat2 = latlng2.lat();
    var lon2 = latlng2.lng();

    // binding box for mapserver
    var minlat = Math.min(lat1,lat2);
    var minlon = Math.min(lon1,lon2);
    var maxlat = Math.max(lat1,lat2);
    var maxlon = Math.max(lon1,lon2);

	var mapext = minlon + "," + minlat + "," + maxlon + "," + maxlat;
	var parameters = {
		mode: "draw_map",
		layer_name: layer_name,
		layer_type: gActiveLayer,
		mapsize: "256+256",
		mapext: mapext,
		outputformat: gMapserver.parameters.outputformat,
		zoom: zoom
	}

    var url = gMapserver.mapserverRequest(parameters);		
    return url;
}

function update_dist_layers() {
	var zoom = map.getZoom();
	var layer_name = choose_layer(zoom, false);
	var where = gQuery.toQueryString("dist_layer");
	
	gMapserver.request("update_dist_layers", update_map,
		{
			layer_name: layer_name,
			layer_type: gActiveLayer,
			taxa_column: gQuery.taxaColumn
		}, where, {});
}

function on_mousemove_coordinate(point) {
	$("current_position").update("X:" + point.lng().toFixed(2) + " Y:" + point.lat().toFixed(2));
}

function choose_legend(layer_name, cellsize) {
	var legend_name = gIconBase + "legend_" + layer_name + ".png";
	if (legend_name != $('img_legend').src) {
		$('img_legend').src = legend_name;
	}

	var legend_cellsize = gIconBase + "legend_cellsize_v2_" + cellsize + ".png";
	if (legend_cellsize != $('legend_cellsize').src) {
		$('legend_cellsize').src = legend_cellsize;
	}
}


function update_species_list_div() {
	// Species list is in gSpeciesList set by speces_search
	$('species_count').update(gSpeciesList.length);
	if (gSpeciesList.length > 0) {
		var s= "<table class='listing' cellspacing='0' cellpadding='3'>";
		var prev_taxa = "";
		for (var i = 0; i < gSpeciesList.length; i++) {
			var species = gSpeciesList[i];
			var taxa = species.obis_taxa;
			var sp_tsn = species.sp_tsn;
			if (prev_taxa != taxa) {
				s += "<tr class='provider_row'><td colspan='5'>" + taxa + "</td></tr>";
				s += "<tr _class='provider_row'><th>Scientific</th><th>Common</th><th>Rank</th><th>#obs.</th><th>#datasets</th></tr>";
			}
			s += "<tr onmouseover='change_bgcolor(this, \"#A2BBC6\");  this.style.cursor = \"pointer\";' onmouseout='change_bgcolor(this, \"transparent\")' onclick='choose_species(\"" + species.scientific_name + "\");'>";
			s += "<td><a class='species_link' title='take you to the species profile page' href='species_profile.php?id=" + sp_tsn + "'>" + species.scientific_name + "</a></td>";
			s += "<td>" + species.common_name + "</td><td>" + species.sp_rank + "</td>";
			s += "<td align='right'>" + int_format(species.records, false) + "</td>";
			s += "<td align='right'>" + int_format(species.datasets, false) + "</td>";
			s += "</tr>";
			prev_taxa = taxa;
		}
		s += "</table>";
	} else {
		var s = "Please sppecify species or draw your region of interest first.<BR>";
	}
	$('species_list').update(s);
}


function species_search(species) {
	var parameters = $H({criteria: species, start_at:0, num_species:-1}).toQueryString();
	var url = gPlone + "getSpecies?" + parameters;
	new Ajax.Request(url,
		{
			method: 'GET',
			onSuccess: species_search_results,
			asynchronous: false
		});

	return gQuery.sp_tsn;	// gQuery.sp_tsn is set in species_search_results
}


function build_range_select(chart_data) {
	gYearMax = chart_data.year_max;
	gYearMin = chart_data.year_min;
	gYearStart = gYearMin;
	gYearEnd = gYearMax;
	gYearSlider1 = gYearStart;
	gYearSlider2 = gYearEnd;

	/* for lined up years */
	var years = chart_data.years;
	if (years.length > 20) {
		var year_class = "year_narrow";
		var cell_width = 20;	// 18 + 2 (border width)
		var diff = 0;			// 23 - 23
	} else {
		var year_class = "year_wide";
		var cell_width = 23;	// 21 + 2 (border width)
		var diff = 3;			// 23 - 20
	}

	if (gYearMax == 104) {		// Season
		var year_class = "season";
		var cell_width = 44;	// 42 + 2 for season
		$("charts_div").removeClassName("chart_year");
		$("charts_div").addClassName("chart_season");
	} else {	// Year, month, day and seasonal month
		$("charts_div").removeClassName("chart_season");
		$("charts_div").addClassName("chart_year");
	}

	var data_max = chart_data.data_max;
	if (data_max == 14 || data_max == 15) {
		var padding_left = 19 - diff;
	} else if (data_max < 4) {
		var padding_left = 13 - diff;
	} else if (data_max < 10) {
		var padding_left = 5 - diff;
	} else if (data_max < 100) {
		var padding_left = 11 - diff;
	} else if (data_max < 1000) {
		var padding_left = 17 - diff;
	} else if (data_max < 10000) {
		var padding_left = 24 - diff;
	} else if (data_max < 100000) {
		var padding_left = 30 - diff;
	} else {
		var padding_left = 36 - diff;
	}
	$("div_table").style.paddingLeft = padding_left + "px";
	$("div_slider").style.paddingLeft = padding_left + "px";


	var table = "<table cellpadding=0 style='' id='year_table'>";
	var cell_id = "year_" + (gYearMin - 1);
	var row1 = "<td><div id='" + cell_id + "' class='" + year_class + " chart_cell'></div></td>";
	var row2 = "<td>NA</td>";
	for (var i = 0; i < years.length; i++) {
		var year = parseInt(years[i]);
		if (gQuery.temporalScale == -1) {
			var season_labels = {101:'Wi', 102:'Sp', 103:'Su', 104:'Fa'};
			var year_str = season_labels[year];
		} else {
			var year_str = year.toString().substring(2);
		}
		var cell_id = "year_" + year;
		var div = "<div id='" + cell_id + "' class='" + year_class + " chart_cell year_selected'></div>";
		row1 += "<td>" + div + "</td>";
		row2 += "<td>" + year_str + "</td>";
		
		if (i > 0 && (i + 1) % 45 == 0) {	// chart break if there are more than 45 data.
			var cell_id = "year_" + (gYearMax + 1);
			row1 += "<td><div id='" + cell_id + "' class='" + year_class + " chart_cell'></div></td>";
			row1 += "<td><div id='" + cell_id + "' class='" + year_class + " chart_cell'></div></td>";
			row2 += "<td>NA</td>";
			row2 += "<td>NA</td>";
		} 
	}
	var cell_id = "year_" + (gYearMax + 1);
	row1 += "<td><div id='" + cell_id + "' class='" + year_class + " chart_cell'></div></td>";
	row2 += "<td>NA</td>";
	table += "<tr>" + row1 + "</tr>";
	table += "</table>";

	$('div_table').update(table);

	// Build slider
	var year_start_div = "<img id='slider1' class='slider' src='" + gIconBase + "seamap2_slidebar_v01.png' style='z-index:60;' title='slide the bar to set time range' width='" + cell_width + "' height='20'>";
	var year_end_div = "<img id='slider2' class='slider' src='" + gIconBase + "seamap2_slidebar_v01.png' style='z-index:60;' title='slide the bar to set time range' width='" + cell_width + "' height='20'>";
	$('div_slider').update(year_start_div + year_end_div);

	// Adjust the slider table width, so the second bar shows up.
	var table_width = $('year_table').getWidth();
	$('div_slider').style.width = (table_width) + 'px';
	$('slider1').style.left = cell_width + "px";	// skip the first 'NA' cell
	$('slider2').style.left = (table_width - cell_width * 3) + "px";	// 71 = 27 (image width) * 3; Make the slidebar width the same as table cell width

	//new Draggable('slider1',{zindex: 10, constraint:'horizontal', snap:[27,0], onEnd:identify_target_year});
	if (is_ie7) {
		// IE7 doesn't like snap. It tends to loose focus on the slidebar image
		new Draggable('slider1',{zindex: 10, constraint:'horizontal', onEnd:identify_target_year, endeffect:null, scrollSensitivity:5, scrollSpeed:50});
		new Draggable('slider2',{zindex: 10, constraint:'horizontal', onEnd:identify_target_year, endeffect:null, scrollSensitivity:5, scrollSpeed:50});
	} else {
		new Draggable('slider1',{zindex: 10, constraint:'horizontal', snap:[cell_width,0], onEnd:identify_target_year});
		new Draggable('slider2',{zindex: 10, constraint:'horizontal', snap:[cell_width,0], onEnd:identify_target_year});
	}

	if (is_ie) {
		// IE doesn't adjust the height.
		$('div_chart').style.height = 200;
	}
}

function identify_target_year(slider) {
	var slider_offset = slider.element.cumulativeOffset()[0];

	$$("div.chart_cell").each(function (item) {
		var year_offset = $(item).cumulativeOffset()[0];
		if (slider_offset < year_offset + 17 && slider_offset > year_offset - 17) {
			slider_dropped(slider.element, $(item));
			return true;
		}
	});
	
}

function slider_dropped(slider, dropped_year) {
	if (slider.id == 'slider1') {
		var tempYear = parseInt(dropped_year.id.split("_")[1]);
		if (tempYear == gYearSlider1) {
			return true;
		}
		if (tempYear == gYearMin - 1 || tempYear == gYearMax + 1) {		// NA cell
			gYearSlider1 = -1;
		} else {
			gYearSlider1 = tempYear;
		}
	}

	if (slider.id == 'slider2') {
		var tempYear = parseInt(dropped_year.id.split("_")[1]);
		if (tempYear == gYearSlider2) {
			return true;
		}
		if (tempYear == gYearMin - 1 || tempYear == gYearMax + 1) {		// NA cell
			gYearSlider2 = -1;
		} else {
			gYearSlider2 = tempYear;
		}
	}

	gYearStart = Math.min(gYearSlider1, gYearSlider2);
	gYearEnd = Math.max(gYearSlider1, gYearSlider2);
	if (gYearStart == -1) {
		gYearStart = gYearEnd;
	}

	$$("div.chart_cell").each(function (item) {
		var year = $(item).id.replace("year_", "");
		if (year >= gYearStart && year <= gYearEnd) {
			$(item).addClassName('year_selected');
		} else {
			$(item).removeClassName('year_selected');
		}
	});

	build_time_range_query();
	
	if (typeof(time_range_changed) != 'undefined') {
		time_range_changed();
	}
}

function build_time_range_query() {
	if ((gYearMin != gYearStart || gYearMax != gYearEnd)) {
		var date_part = "obs_year";										// Default for 0|1|2
		var date_part_z = "to_char(_obs_datetimez, 'YYYY')::int";		// Default for 0|1|2
		switch (gQuery.temporalScale) {
			case -1:	// Season
			case -2:	// Seasonal month
				var months = "";
				var delimiter = "";
				if (gQuery.temporalScale == -1) {
					var seasons = {101: "12, 1, 2", 102: "3, 4, 5", 103: "6, 7, 8", 104: "9, 10, 11"};
				} else {
					var seasons = {101: "1", 102: "2", 103: "3", 104: "4", 105: "5", 106: "6", 107: "7", 108: "8", 109: "9", 110: "10", 111: "11", 112: "12"};
				}
				for (var j = gYearStart; j <= gYearEnd; j++) {
					months += delimiter + seasons[j];
					delimiter = ", ";
				}
				var where = "date_part in (" + months + ")";
				var date_part = "obs_month";
				var date_part_z = "date_part('month', _obs_datetimez)";
				break;
			case 0:	// Century
				// As Mapserver seems to regard a plus as a spetial character,
				// you con't do this: ((obs_year - 1) / 100)::int + 1 >= " + gYearStart
				var where = "((date_part - 1) / 100)::int >= " + gYearStart + " - 1 and ((date_part - 1) / 100)::int <= " + gYearEnd + " - 1";
				break;
			case 1:	// Decade
				var where = "((date_part / 10)::int) * 10 >= " + gYearStart + " and ((date_part / 10)::int) * 10 <= " + gYearEnd;
				break;
			case 2:	// Year
				var where = "date_part >= " + gYearStart + " and date_part <= " + gYearEnd;
				break;
			case 3:	// Month
				var where = "date_part >= to_date('" + gYearStart + "01', 'YYYYMMDD') and date_part < to_date('" + (gYearEnd + 1) + "01', 'YYYYMMDD')";
				var date_part = "date_min";
				var date_part_z = "_obs_datetimez";
				break;
			case 4:	// Day
				var where = "date_part >= to_date('" + gYearStart + "', 'YYYYMMDD') and date_part < to_date('" + (gYearEnd + 1) + "', 'YYYYMMDD')";
				var date_part = "date_min";
				var date_part_z = "_obs_datetimez";
				break;
		}
	} else {
		var where = "";
	}
	gQuery.temporal = where.replace(/date_part/g, date_part);
	gQuery.temporalZ = where.replace(/date_part/g, date_part_z);
}


/***** Year or Season *****/
function year_or_season(button, which) {
	gQuery.temporalScale = which;	
	gQuery.clearTemporal();
	
	$$("img.time_button").each(function (item) {
		$(item).removeClassName('button_selected');
		$(item).src = gIconBase + $(item).id + '.png';
	});
	$(button).src = gIconBase + $(button).id + "_selected.png";
	$(button).addClassName('button_selected');
	
	if (typeof(adjust_scale_bar) == "function") {
		adjust_scale_bar(Math.abs(which));
	}

	if (typeof(year_or_season_changed) == "function") {
		year_or_season_changed();
	}
}

/***** Google Maps custom controls *****/


/***** Identeify start *****/
function on_click_identify(overlay, point) {
	if (NavigationTools.activeTool() != "default") {
		return false;
	}

	map.closeInfoWindow();	// This causes the existing highlighted cell to be removed.

	// GM's default polygon click passes point as undefined, so ignore the event.
	// Custom polygon click event listener passes both overlay and point.
	if (overlay && typeof(point) == 'undefined') {
		return false;
	}

	//$('please_wait').show();
	gClickedPoint = point;
	var lat = point.lat();
	var lon = point.lng();
	
	var bounds = map.getBounds();
	var sw = bounds.getSouthWest();
	var ne = bounds.getNorthEast();
	var mapext = [sw.lng(), sw.lat(), ne.lng(), ne.lat()].join(",");
	// gActiveLayer is always 'dist' for dist_map.html and datasets.html
	// It is used in dataset_detail.php when it shows observation points. [dist|point]
	if (gActiveLayer == 'dist') {
		highlight_cell(lon, lat);
	}
	if (gActiveLayer == 'dist') {
		var layer_name = choose_layer(map.getZoom(), false);
	} else {
		var layer_name = "";
	}
	gMapserver.request("identify", show_identify,
		{
			latitude: lat,
			longitude: lon,
			mapext: mapext,
			layer_name: layer_name
		}, "", {});
}

function highlight_cell(lon, lat) {
	var zoom = map.getZoom();
	if (zoom < gZoom["01"]) {
		var times = 1;
		var shift = 0.5;
	} else if (zoom < gZoom["001"]) {
		var times = 10;
		var shift = 0.05;
	} else {
		var times = 100;
		var shift = 0.005;
	}
	lon = Math.round(lon * times) / times;
	lat = Math.round(lat * times) / times;
	var point1 = new GLatLng(lat + shift, lon + shift);
	var point2 = new GLatLng(lat - shift, lon - shift);

	var options = {color: "#DDDDDD", weight: 3, opacity: 1};
	gCell = getRectangle(point1, point2, options);
	map.addOverlay(gCell);
}

function erase_cell() {
	if (gCell != null) {
		map.removeOverlay(gCell);
		gCell = null;
	}
}

function show_identify(oj) {
	var msg = oj.responseText;
	map.openInfoWindowHtml(gClickedPoint, msg, gMaxDim);
}
/***** Identeify end *****/

/***** Navigation Tools *****/
// Tool management
var gClickHandler;
var gMousemoveHandler;
var gFirstPoint;
var gSecondPoint;
var gExtent;
var gROIs = [];

var gStep = 1;

var NavigationTools = {
	tools: [],
	toolStack: [{tool_id: 'default', activate: function(){}, deactivate: function(){}}],

	add: function (imgID) {
		var element = $(imgID);
		var action_name = element.id;

		this.tools.push(action_name);

		element.observe('mouseover',
			function (event) {
				if(NavigationTools.activeTool() != action_name) {
					element.src = gIconBase + "icon_" + action_name + '_hover.png'
				}
			});

		element.observe('mouseout',
			function (event) {
				if(NavigationTools.activeTool() != action_name) {
					element.src = gIconBase + "icon_" + action_name + '.png'
				}
			});
	},

	activate: function (tool_id, activate_func, deactivate_func) {
		theTool = $(tool_id);

		if (this.activeTool() != tool_id) {
			this.deactivateCurrentTool();
			theTool.src = gIconBase + "icon_" + tool_id + '_selected.png';
			var active_tool = {tool_id: tool_id, activate: activate_func, deactivate: deactivate_func};
			this.toolStack.push(active_tool);
			activate_func();
		} else {
			this.deactivateCurrentTool();
		}
	},

	activeTool: function() {
		return this.toolStack[this.toolStack.length - 1].tool_id;
	},

	deactivateCurrentTool: function() {
		var active_tool = this.toolStack[this.toolStack.length - 1];
		if (active_tool.tool_id != 'default') {
			$(active_tool.tool_id).src = gIconBase + "icon_" + active_tool.tool_id + '.png';
			active_tool.deactivate();
			this.toolStack.pop();
		}
	}
}


function activate_zoomin() {
	gClickHandler = GEvent.addListener(map, "click", placePoint);
	onFinishPlacePoint = zoomin;
}

function deactivate_zoomin() {
	// Deactivate
	GEvent.removeListener(gClickHandler);
}

function placePoint(marker, point) {
	// Ignore event issued by polyline click
	if (marker && (typeof(point) == 'undefined')) {
		return false;
	}

	if (gFirstPoint == null) {
		if (gExtent) {
			map.removeOverlay(gExtent);
		}
		gFirstPoint = point;
		gMousemoveHandler = GEvent.addListener(map, "mousemove", drawExtent);
	} else {
		GEvent.removeListener(gMousemoveHandler);
		onFinishPlacePoint();
		gFirstPoint = null;
	}
}

function drawExtent(latlong) {
	if (gStep % 3 != 0) {
		gStep++;
		if (gStep > 3) {
			gStep = 1;
		}
		return false;
	}

	gSecondPoint = latlong;

	if (gExtent) {
		map.removeOverlay(gExtent);
	}

	//fillLocationBoxes(latlong);
	gExtent = getRectangle(gFirstPoint, gSecondPoint, {onclick: placePoint});
	map.addOverlay(gExtent);
}

function getRectangle(point1, point2, options) {
	var options_default = {color: "#FFFFFF", weight: 3, opacity: 1.0};
	options = $H(options_default).merge(options).toObject();
	var linePoints = cornerPoints(point1, point2);

	var extent = new GPolyline(linePoints, options.color, options.weight, options.opacity);
	if (options.onclick) {
		GEvent.addListener(extent,"click",function(point){
			//placePoint(extent, point);
			options.onclick(extent, point);
		});
	}
	return extent;
}

function cornerPoints(point1, point2) {
	var lats = Array();
	var lngs = Array();
	lats[0] = point1.lat();
	lngs[0] = point1.lng();
	lats[1] = point2.lat();
	lngs[1] = point2.lng();
	var point3 = new GLatLng(lats[1], lngs[0]);
	var point4 = new GLatLng(lats[0], lngs[1]);

	var linePoints = Array();
	linePoints[0] = point1;
	linePoints[1] = point3 ;
	linePoints[2] = point2;
	linePoints[3] = point4;
	linePoints[4] = point1;

	return linePoints;
}

function getPolygon(points) {
	var index = gROIs.length;
	poly_opacity = 1.0 - gROITransparency;
	poly_colors = new Array("#FFFF00", "#66FF00", "#FF33CC", "#FF0066", "#00FFFF");
	var color_index = index % 5;
	poly_color = poly_colors[color_index];
	
	var polygon = new GPolygon(points, poly_color, 2, 0.8, poly_color, poly_opacity);
	GEvent.addListener(polygon,"click",function(point){
		gClickedPolygon = polygon;
		switch (NavigationTools.activeTool()) {
			case "tool_draw_polygon":
				polygon.enableEditing();
				break;
			case "tool_remove_polygon":
				remove_polygon(polygon);
				break;
			default:
        		on_click_identify(polygon, point);
		}
      });
	map.addOverlay(polygon);
	gROIs.push(polygon);
	return polygon;
}

function zoomin () {
	zoomTo();

	// Clear the extent polyline
	map.removeOverlay(gExtent);
	NavigationTools.deactivateCurrentTool();
}

function zoomTo() {
	if (gFirstPoint.lng() < gSecondPoint.lng()) {
		var sw = gFirstPoint;
		var ne = gSecondPoint;
	} else {
		var sw = gSecondPoint;
		var ne = gFirstPoint;
	}
	
	zoomToSwNe(sw, ne, -1);
}

function zoomToSwNe(sw, ne, max_zoom) {
	if (ne != null) {
		var aBound = new GLatLngBounds(sw, ne);
		var aCenter = aBound.getCenter();
		var aZoomLevel = map.getBoundsZoomLevel(aBound);
	} else {
		var aCenter = sw;
		var aZoomLevel = max_zoom;
	}
	map.panTo(aCenter);
	map.setCenter(aCenter);

	if (aZoomLevel - map.getZoom() > 2) {
		aZoomLevel -= 1;
	} 
	
	if (max_zoom > 0 && aZoomLevel > max_zoom) {
		aZoomLevel = max_zoom;
	}
	map.setZoom(aZoomLevel);
}

function zoomToBOX3D(extent) {
	extent = extent.replace("BOX3D(", "");
	extent = extent.replace(")", "");
	extent = extent.split(",");
	var sw = extent[0].split(" ");
	var ne = extent[1].split(" ");
	
	// In rare cases where just one observation is found, you can't define extent.
	// In that case, zoom in to the one observation.
	if (sw[1] != ne[1] && sw[0] != ne[0]) {
		var sw = new GLatLng(sw[1], sw[0]);
		var ne = new GLatLng(ne[1], ne[0]);
		var zoom = 8;
	} else {
		var sw = new GLatLng(sw[1], sw[0]);
		var ne = null;
		var zoom = 4;		// arbitrary
	}
	zoomToSwNe(sw, ne, zoom);
}

function zoomToPoint(lon, lat, zoom) {
	if (zoom == -1) {
		var current_zoom = map.getZoom();
		if (current_zoom < 7) {
			zoom = 7;
		} else {
			zoom = current_zoom;
		}
	}
	var aCenter = new GLatLng(lat, lon);
	map.panTo(aCenter);
	map.setCenter(aCenter);
	map.setZoom(zoom);
}

function activate_draw_rect() {
	gClickHandler = GEvent.addListener(map, "click", placePoint);

	for (var i = 0; i < gROIs.length; i++) {
		// Shapefiles may be a part of gROIs (e.g. exercise_areas). Then, you need to skip them.
		if (typeof(gROIs[i].type) != "string" || gROIs[i].type != "shapefile") {
			gROIs[i].enableEditing();
		}
	}
	
	onFinishPlacePoint = draw_rect;
}

function draw_rect() {
	zoomTo();
	map.removeOverlay(gExtent);
	
	//var roi = getPolygon(gFirstPoint, gSecondPoint);
	var points = cornerPoints(gFirstPoint, gSecondPoint);
	var polygon = getPolygon(points);

	NavigationTools.deactivateCurrentTool();
}

function deactivate_draw_rect() {
	GEvent.removeListener(gClickHandler);
	deactivate_draw_polygon();
}

function activate_draw_polygon() {
	for (var i = 0; i < gROIs.length; i++) {
		// Shapefiles may be a part of gROIs (e.g. exercise_areas). Then, you need to skip them.
		if (typeof(gROIs[i].type) != "string" || gROIs[i].type != "shapefile") {
			gROIs[i].enableEditing();
		}
	}
	
	gPolygonInEdit = true;		// Turned to false in finish_drawing.
	var polygon = getPolygon([]);
	GEvent.addListener(polygon, "endline", finish_drawing);
	polygon.enableDrawing();

}

function finish_drawing() {
	gPolygonInEdit = false;
	var extent = this.getBounds();
	var sw = extent.getSouthWest();
	var ne = extent.getNorthEast();
	zoomToSwNe(sw, ne, -1);
	NavigationTools.deactivateCurrentTool();
}

function deactivate_draw_polygon() {
	if (gPolygonInEdit) {	// This means a new polygon is left unfinished.
		gROIs[gROIs.length - 1].disableEditing();
		map.removeOverlay(gROIs[gROIs.length - 1]);
		gROIs.pop();
	}
	for (var i = 0; i < gROIs.length; i++) {
		// Shapefiles may be a part of gROIs (e.g. exercise_areas). Then, you need to skip them.
		if (typeof(gROIs[i].type) != "string" || gROIs[i].type != "shapefile") {
			gROIs[i].disableEditing();
		}
	}
	gPolygonInEdit = false; 
	
	gQuery.spatial = build_spatial_query();

	if (typeof(roi_updated) != "undefined") {
		roi_updated();
	}
}

function point_icon () {
	var icon = new GIcon();
	icon.image = gIconBase + "marker_red.png";
	icon.iconSize = new GSize(16, 16);
	icon.iconAnchor = new GPoint(8, 8);
	icon.infoWindowAnchor = new GPoint(8, 8);
	return icon;
}

function build_spatial_query() {
	var coords = "";
	var delimiter = "";

	if (gROIs.length == 0) {
		return "";
	}

	// Obtain all points from all polygons
	for (var k = 0; k < gROIs.length; k++) {
		var aPolygon = gROIs[k];
		delimiter = "";	
		if (typeof(aPolygon.type) == "string" && aPolygon.type == "shapefile") {	// shapefile
			if (aPolygon.coords != "") {
				coords += "((";
				coords += aPolygon.coords;
				coords += "))";
			}
		} else {		// GPolygon
			coords += delimiter + "((";
			for (var l = 0; l < aPolygon.getVertexCount(); l++) {
				var aVertex = aPolygon.getVertex(l);
				coords += delimiter + aVertex.lng().toFixed(8) + " " + aVertex.lat().toFixed(8);
				delimiter = ",";
			}
			coords += "))";
		}
	}

	if (coords != "") {
		var query = "(geom && geometryfromtext('MULTIPOLYGON(" + coords + ")', -1)";
		query += " and Within(geom, geometryfromtext('MULTIPOLYGON(" + coords + ")', -1)))";
	} else {
		var query = "";
	}
	return query;
}

function activate_remove_roi() {

}

function deactivate_remove_roi() {
	if (typeof(roi_updated) != "undefined") {
		roi_updated();
	}
}

function remove_polygon(polygon) {
	gROIs = gROIs.without(polygon);
	map.removeOverlay(polygon);
	map.closeInfoWindow();
	gQuery.spatial = build_spatial_query();
	NavigationTools.deactivateCurrentTool();
}

function polygon_transparency(element, e) {
	var pos = parseInt($(element.element).style.left);

	if (pos < 50) {
		var buffer = 10;
	} else {
		var buffer = 16;
	}
	pos = pos + buffer;
	if (pos < 0) {
		pos = 0;
	}
	if (pos > 100) {
		pos = 100;
	}
	var bar_width = 100;
	//$('debug').update(pos);
	gROITransparency = pos / bar_width;
	change_transparency(gROITransparency);
}

function change_transparency(transparency) {
	for (var i = 0; i < gROIs.length; i++) {
		if (typeof(gROIs[i].type) != "string") {
			gROIs[i].opacity = 1 - transparency;
			gROIs[i].redraw(true);
		}
	}
}

function adjust_bar_pos(element) {
	var buffer_left = -10;
	var buffer_right = -16;
	var pos = parseInt($(element.element).style.left);
	//$('debug').update(pos);
	if (pos < buffer_left || pos > 100 + buffer_right) {
		if (pos < buffer_left) {
			var adjusted_pos = buffer_left;
		} else if (pos > 100 + buffer_right) {
			var adjusted_pos = 100 + buffer_right;
		}

		$(element.element).setStyle({left: adjusted_pos + "px"});
	}
}

/***** Change color of rows upon mouseover in species_list and dataset_list divs. *****/
function change_bgcolor(item, color) {
	$(item).style.backgroundColor = color;
}

/***** When the user clicks on the species in the popup... *****/
function species_observed(tsns) {
	if (tsns != "") {
		var parameters = $H({sp_tsns: tsns, start_at:0, num_species:-1}).toQueryString();
		var url = gPlone + "getSpecies?" + parameters;
		new Ajax.Request(url,
			{
				method: 'GET',
				onSuccess: update_taxa_infowindow
			});
	} else {
		var s = alert("No species observed in this cell.");
	}
}

function update_taxa_infowindow(oj) {
	var species_info = eval("(" + oj.responseText + ")");
	var species = species_info.species_list;
	s = "";
	for (var i = 0; i < species.length; i++) {
		if (species[i].common_name != "") {
			var common_name = " (" + species[i].common_name + ")";
		} else {
			var common_name = "";
		}
		s += "<a href='javascript:void(0);' class='species_link' onclick='choose_species(\"" + species[i].scientific_name + "\")';>" + species[i].scientific_name + "</a>" + common_name + "<BR>";
	}
	s = "<div style='height:180px; overflow:auto;'>" + s + "</div>";
	var infowindow = map.getInfoWindow();
	map.openInfoWindowHtml(infowindow.getPoint(), s, {maxHeight: 200, maxWidth:400});
}

function set_temporal_scale(scale) {
	gTemporalScale = scale;
}

function open_mapper(id, layer_type, color_by) {
	var url = "http://seamap.env.duke.edu/prod/mapservice/googlemaps/seamap_gm.phtml";

	var parameters = $H({'add': id, 'coloredBy': color_by, 'layer_type': layer_type}).toQueryString();
	window.open(url + "?" + parameters);
}

function open_google_earth(key, value) {
	// key = [datasetid|tsn]
	//var url = "http://seamap.env.duke.edu/prod/mapservice/googleearth/kml_generator.php?" + key + "=" + value;
	// key = [dataset|sp_tsn]
	var modes = {'dataset': 'dataset', 'sp_tsn': 'species'};
	var url = g_ROOT_URL + "/seamap2/mapper/kml.php?" + key + "=" + value + "&mode=" + modes[key];
	window.open(url);
}

function show_species_pic(url, parameters) {
	var map_pos = $('map').cumulativeOffset();
	$(parameters.div).setStyle({top: (map_pos.top + parameters.offset_top) + "px", left: (map_pos.left + parameters.offset_left) + "px"});
	$(parameters.pic).src = url;	
	//new Effect.Grow(parameters.div, {duration:2.0, delay:1.0, beforeStart: before_show_pic.bind(parameters), afterFinish: function(){gLock = false;}});
	// Image position unpredictable with Grow.
	// It seems Opacity does not change display attribute. So, first change opacity to 0.1 while the element is still hidden.
	// then show the element (display:block) and get it back to opacity 1.
	new Effect.Opacity(parameters.div, {duration:0.1, from:1.0, to:0.1, afterFinish: function(){$(parameters.div).show(); new Effect.Opacity(parameters.div, {duration:2.0, from:0.1, to:1});}});	
}


function hide_species_pic() {
	$('species_pic_div').hide();
}

function show_hide_map() {
	toggle_row('map_row');
	if ($('map_row').visible()) {
		$('img_show_hide').src = $('img_show_hide').src.replace('expand_', 'hide_');
	} else {
		$('img_show_hide').src = $('img_show_hide').src.replace('hide_', 'expand_');
	}
}

function toggle_row(element) {
	if ($(element).visible()) {
		$(element).hide();
	} else {
		$(element).style.display = gTableRow;
	}

	window_resize();
}

function button_onoff(element, class_name) {
	if ($(element).hasClassName('button_selected')) {
		return true;
	}
	$$(class_name).each(function(item) {
		$(item).removeClassName('button_selected');
		$(item).src = $(item).src.replace('_selected.png', '.png');
	});
	
	$(element).addClassName('button_selected');
	$(element).src = $(element).src.replace('_hover.png', '_selected.png');
}


function expand() {
	var graph_button = $('tool_graph');
	
	if (!$("div_chart").visible() && gQuery.sp_tsn == "" & gQuery.dataset == "" && gQuery.provider_datasets == "" && gQuery.spatial == "" && gQuery.criteria == "") {
		alert("Graph is available when at least one criterion (species, dataset, provider, region of interest) is specified.");
		return true;
	}
	$$('div.chart_area').invoke('toggle');
	if ($('div_chart').visible() && gQuery.temporal == "") {
		graph_button.src = gIconBase + "icon_" + graph_button.id + "_selected.png";
		graph_button.addClassName("button_selected");
		update_chart();
	} else {
		graph_button.src = gIconBase + "icon_" + graph_button.id + ".png";
		graph_button.removeClassName("button_selected");
	}
	window_resize();
}

function mapsize(size) {
	var center = map.getCenter();
	if ((typeof(gDataset) != "undefined" && parseInt(gDataset.num_records) > 100000) || (typeof(gSpecies) != "undefined" && parseInt(gSpecies.records) > 100000)) {
		var buffer_large_dataset = 30;
	} else {
		var buffer_large_dataset = 0;
	}
	switch (size) {
		case "l":
			if (is_ie) {
				var buffer_w = 55;
			} else {
				var buffer_w = 45;
			}
			buffer_w += 150;	// 150: legend pane
			var buffer_h = 15;
			var map_pos = $('map').cumulativeOffset();
			var outer_frame_pos = $('outer_frame').cumulativeOffset();
			var width = gWinW - map_pos[0] - buffer_w - buffer_large_dataset;
			var height = gWinH - (map_pos[1] - outer_frame_pos[1]) - buffer_h;
			var outer_frame_width = gWinW - 45;
			break;
		case "s":
			var width = 550 - buffer_large_dataset;
			var height = 300;
			var outer_frame_width = 955; // Default; fixed
			break;
	}
	$('map').setStyle({width: width + "px", height: height + "px"});
	map.checkResize();
	map.setCenter(center);
	$('outer_frame').setStyle({width:outer_frame_width + "px"});
	$('inner_frame').setStyle({width:(outer_frame_width - 1) + "px"});
	$$("div.left_pane").each(function(item) {$(item).style.height = (height + 20) + "px";});	// 30: toolbar
	$$("div.right_pane").each(function(item) {$(item).style.height = (height + 30) + "px";});	// 30: toolbar
}

function map_cursor(cursor) {
	var dragObj = map.getDragObject();
	dragObj.setDraggableCursor(cursor); 	
}

function show_options() {
	if ($('option_form').visible()) {
		new Effect.SlideUp('option_form');
	} else {
		// Determine option_form div position
		var offset = $('options_button').cumulativeOffset();
		var height_adjust = $('options_button').getHeight() + 4;	// 4 is an arbitrary buffer
		//var left_adjust = $('option_form').getWidth() - $('options_button').getWidth();
		var left_adjust = 50;
		$('option_form').setStyle({left: (offset[0] - left_adjust) + "px", top: (offset[1] + height_adjust) + "px"});
		new Effect.SlideDown('option_form');
	}
}

function cancel_bubble(e) {
	if (!e) var e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();	
}


/***** Environmental layers *****/
function onoff_env_layer(env_type) {
	if (gActiveLayer != "point") {
		alert("Please switch to points layer.");
		return true;
	}
	var action = "onoff_env_layer";
	gDontUpdateChart = true;
	gMapserver.request(action, custom_query_updated,
		{
			layer_name: "env_layer",
			temporal_scale: gQuery.temporalScale,
			date_start: gYearStart,
			env_type: env_type,
			status: env_type != "N"
		}, "", {asynchronous: false});
}

function custom_query_updated(oj) {
	if ($('option_form').visible()) {
		$('option_form').hide();	
	}
	var msg = oj.responseText.evalJSON();
	update_map();
	/*
	if (typeof(msg.env_layer_data) != "undefined") {
		var legend_url = gIconBase + "blank.png";
		$('env_legend').src = legend_url;
		if (msg.env_layer_data.indexOf("not found") == -1) {
			var legend_url = "/cgi-bin/mapserv?map=/var/www" + BASE_URL + "cache/maps/" + gMapserver.parameters.sid + ".map&mode=legend&layer=env_layer";
		}
		var env_layer = msg.env_layer_desc;
		$('current_env_layer').update(env_layer);
		$('env_legend').src = legend_url;
	}
	*/
	/*
	Env legends are pre-generated ones stored in /var/www/seamap2/icons.
	The file name is given by seamap2_gm.php (two locations: custom_query and onoff_env_layer
	To udate the legend, call mapserver:
	http://seamap.env.duke.edu/cgi-bin/mapserv?map=/var/www/cache/maps/48f8a3f959b73.map&mode=legend&layer=env_layer
	*/
	if (msg.env_type != "N") {
		var legend_url = msg.env_legend;
		$('env_legend').src = legend_url;
		$('current_env_layer').update(msg.env_layer_desc);
	}
}

function load_env_desc() {
	var container = "env_desc";
	if (gSEAMAP2) {
		var url = "/datasets/";
	} else {
		var url = "";
	}
	url += "content/env_desc.html";
	new Ajax.Updater(container, url);
}