/*

License and acknowledgements will go here...

*/

// $Id: AfricaMap.js 82195 2008-06-24 14:12:10Z crschmidt $

var AM = {}; // AfricaMap namespace obj

//var message = "Oops.  We are making some improvements to the AfricaMap system and moving it from development to production.  The system may be down periodically on 2/19/09 and 2/20/09. We are sorry for any inconvenience this causes.";
//alert(message);

//http://cga-5.hmdc.harvard.edu/africamap/
//i have no idea what this is for, but changed from cga-5 --pdurbin 2009-10-28
http://africamap.harvard.edu/
/* ------------------------------------------------------------- */
/* Object for constant config settings. */
/* ------------------------------------------------------------- */
// changing from cga-5 to africamap --pdurbin 2009-10-28
//AM.config = {
//    instance_name: 'newam',
//	base_url: 'http://cga-5.hmdc.harvard.edu/africamap',
//	tilecache_url: 'http://cga-5.hmdc.harvard.edu/africamap/tilecache/tiles.py/',
//	mapserver_base_url: 'http://cga-5.hmdc.harvard.edu/cgi-bin/mapserv',
//	geoserver_base_url: 'http://cga-5.hmdc.harvard.edu:8080/geoserver/wms',
//	feature_count_url: 'http://cga-5.hmdc.harvard.edu/cgi-bin/results.cgi',
//	header_html: '<h1>AfricaMap (alpha)</h1>',
//	map_html: '<div id="map"></div>',
//	about_html: '<iframe src="http://about.africamap.harvard.edu/" width="100%" height="100%"></iframe>'
//};

AM.config = {
    instance_name: 'newam',
	base_url: 'http://africamap.harvard.edu',
	tilecache_url: 'http://africamap.harvard.edu/tilecache/tiles.py/',
	mapserver_base_url: 'http://africamap.harvard.edu/cgi-bin/mapserv',
	geoserver_base_url: 'http://africamap.harvard.edu:8080/geoserver/wms',
	feature_count_url: 'http://africamap.harvard.edu/cgi-bin/results.cgi',
	header_html: '<h1>AfricaMap (alpha)</h1>',
	map_html: '<div id="map"></div>',
	about_html: '<iframe src="http://about.africamap.harvard.edu/" width="100%" height="100%"></iframe>'
};

/* ------------------------------------------------------------- */
/* Object for handling OpenLayers-related objects and functions. */
/* ------------------------------------------------------------- */
AM.OL = {
	map:null,
	map_options : {
        projection: new OpenLayers.Projection("EPSG:900913"),
        displayProjection: new OpenLayers.Projection("EPSG:4326"),
        units: "m",
        maxResolution: 156543.0339,
        numZoomLevels: 20,
        maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34,
                                         20037508.34, 20037508.34),
        controls: [new OpenLayers.Control.Navigation(), new OpenLayers.Control.PanZoomBar()] 
    },
	wmsLayers: {
		gphy: new OpenLayers.Layer.Google("Google Physical",
			{type: G_PHYSICAL_MAP, 'sphericalMercator': true}
        ), 
		gmap: new OpenLayers.Layer.Google("Google Streets",
			{'sphericalMercator': true}
        ),
		gsat: new OpenLayers.Layer.Google("Google Satellite",
	        {type: G_SATELLITE_MAP, 'sphericalMercator': true}
	    ),
		ghyb:new OpenLayers.Layer.Google("Google Hybrid",
	        {type: G_HYBRID_MAP, 'sphericalMercator': true}
		)
	},
	markerLayer: null,
	markerSize: null,
	markerOffset: null,
	markerIcon: null,
	markerIcon2: null,
	init:function() {
	    // avoid pink tiles
	    OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3;
	    OpenLayers.Util.onImageLoadErrorColor = "transparent";
	    OpenLayers.Lang.en.permalink = "Link to this map";
		AM.OL.markerSize = new OpenLayers.Size(32, 32);
		AM.OL.markerOffset = new OpenLayers.Pixel(-17, -32);
		AM.OL.markerIcon = new OpenLayers.Icon('http://maps.google.com/mapfiles/ms/micons/ltblue-dot.png', AM.OL.markerSize, AM.OL.markerOffset);
		AM.OL.markerIcon2 = new OpenLayers.Icon('http://maps.google.com/mapfiles/ms/micons/yellow-dot.png', AM.OL.markerSize, AM.OL.markerOffset);
	},
	activateFromQS: function() {
	    var params = OpenLayers.Util.getParameters();
		if(params.layers) {
			var baseLyr = 0;
			for(var i = 0; i < params.layers.length; i++) {
				if(params.layers[i] === 'B') {
					baseLyr = i;
					break;
				}
			}
			if(baseLyr > 0) {
				AM.OL.map.setBaseLayer(AM.OL.map.layers[baseLyr]);
			}
		}
	},
	addBaseLayers: function() {
		AM.OL.map.addLayers([
			AM.OL.wmsLayers.gphy,
			AM.OL.wmsLayers.gmap,
			AM.OL.wmsLayers.ghyb,
			AM.OL.wmsLayers.gsat
		]);
		
		AM.OL.markerLayer = new OpenLayers.Layer.Markers("Markers");
        AM.OL.markerLayer.displayInLayerSwitcher = false;
		AM.OL.map.addLayer(AM.OL.markerLayer);
		AM.OL.activateFromQS();
	},
	onMapClick:function(e) {
		AM.Ext.Search.searchByXY(e.xy.x, e.xy.y);
	}
};

/* ------------------------------- */
/* Object for OpenLayers controls. */
/* ------------------------------- */
AM.OL.Control = {
	layerSwitcherControl: null,
	/* Reference to the Permalink control that is external to map.controls for quick access. */
	permalinkControl: null,
	/* OpenLayers Permalink control change event handler */
	permalinkUrl: '',
	onPermalinkClick: function(lnk) {		
		var tf = new Ext.form.TextField({
			id: 'permalink-TF',
			readonly: true,
			width: 370,
			value: AM.OL.Control.permalinkUrl
		});
	
		var plWin = new Ext.Window({
			id:'permalink-Win',
			title: 'Link to Map',
			width: 400,
			height: 'auto',
			//x: 50,
			y: 100,
			bodyStyle: 'padding:0 5px 5px 5px;',
			items: [
				{html:'Copy, then paste in email or IM', baseCls:'override', bodyStyle:'padding:5px;'},
				tf
			]
		});
		plWin.show();
		tf.focus(true, true);
	
		return false;
	},
	permalinkUpdate: function() {
	    var center = this.map.getCenter();
	    
	    // Map not initialized yet. Break out of this function.
	    if (!center) { 
	        return; 
	    }
	    
	    var params = OpenLayers.Util.getParameters(this.base);
	    
	    params.zoom = this.map.getZoom(); 
	    var lat = center.lat;
	    var lon = center.lon;
	    
	    if (this.displayProjection) {
	        var mapPosition = OpenLayers.Projection.transform(
	          { x: lon, y: lat }, 
	          this.map.getProjectionObject(), 
	          this.displayProjection );
	        lon = mapPosition.x;  
	        lat = mapPosition.y;  
	    }
	    params.lat = Math.round(lat*100000)/100000;
	    params.lon = Math.round(lon*100000)/100000;
	    
	    var layerCount = this.layerCount ? this.layerCount : this.map.layers.length;
	    //var layerCount = this.map.layers.length;

	    params.layers = '';
	    for (var i=0; i < layerCount; i++) {
	        var layer = this.map.layers[i];
			
			if(layer) {    
				if (layer.isBaseLayer) {
					params.layers += (layer == this.map.baseLayer) ? "B" : "0";
				} else {
					params.layers += (layer.getVisibility()) ? "T" : "F";           
				}
			}
	    }

	    for (var i = 0; i < this.map.layers.length; i++) {
	        var layer = this.map.layers[i];
			if (layer.africaMap) {
	            params['africamap_'+layer.layername] = layer.opacity;
                params.layers += ( layer.getVisibility()) ? "T" : "F" ;
	        }
	    }
		
		// Add param for layers that are currently activated for searching
		var searching = '';
		for(dl in dataLayers) {
			if(dataLayers[dl].searching) {
				if(searching != '') { searching += ','; }
				searching += dl;
			}
		}
		
		if(searching != '') {
			params.searching = searching;
		}
		
		if(AM.Ext.Search.searchTerm && AM.Ext.Search.searchTerm != '') {
			params.srchterm = encodeURI(AM.Ext.Search.searchTerm);
		}
		
		var geonamesDl = dataLayers.GeonamesFeatures6;
		if(geonamesDl && geonamesDl.selectedFCodes && geonamesDl.selectedFCodes.length && geonamesDl.selectedFCodes.length > 0) {
			var codes = '';
			for(var i = 0; i < geonamesDl.selectedFCodes.length; i++) {
				if(i > 0) { codes += ','; }
				codes += geonamesDl.selectedFCodes[i];
			}
			params.fcodes = codes;
		}

		// Construct url
	    var href = this.base;
	    if (href.indexOf('?') != -1) {
	        href = href.substring( 0, href.indexOf('?') );
	    }
	    
	    href += '?' + OpenLayers.Util.getParameterString(params);
	    //this.element.href = href;
		AM.OL.Control.permalinkUrl = href;
	},
	/* Render opacity slider into LayerSwitcher. */
	layerSwitcherRenderAdditional: function(layerDiv, layer) {
	    var sliderDiv = document.createElement("div");
	    sliderDiv.className = "olControlLayerSwitcherLayerDivSlider";
	    layerDiv.appendChild(sliderDiv);    
	    sliderDiv.style.height="20px";
	    var sliderValue = layer.opacity == null ? 100 : layer.opacity * 100;
	    var slider = new Ext.Slider({renderTo: sliderDiv, width: 100, minValue: 0, maxValue: 100, animate: false});
	    slider.on("change", function(slider, newValue) {
	        this.layer.setOpacity(newValue/100);
            if ( AM.OL.Control.permalinkControl ) {
                AM.OL.Control.permalinkControl.updateLink();
            }
	    }, {layer: layer, control: this}); 
	    slider.on("dragstart", function(slider, e) { 
	        this.control.isMouseDown = false; 
	    }, {control: this});  
	    if (this.maximizeDiv.style.display != "none") { 
	        this.maximizeControl();
	        slider.setValue(sliderValue);
	        this.minimizeControl();
	    } else {
	        slider.setValue(sliderValue);
	    }
	},
	layerSwitcherVisible: function() {
		return AM.OL.map.getControl(AM.OL.Control.layerSwitcherControl.id).layersDiv.style.display !== 'none';
	},
	/* Construct and add OpenLayers controls */
	addMapControls: function() {
		AM.OL.Control.layerSwitcherControl = 
			new OpenLayers.Control.LayerSwitcher({layerRenderAdditional: AM.OL.Control.layerSwitcherRenderAdditional});
		AM.OL.map.addControl(AM.OL.Control.layerSwitcherControl);
	    
		AM.OL.map.addControl(new OpenLayers.Control.ScaleLine());
	    
		// The permalink control is split out so we can use (update) it more easily.
		AM.OL.Control.permalinkControl = new OpenLayers.Control.Permalink($('permalink'), null, 
			{updateLink: AM.OL.Control.permalinkUpdate, layerCount: 4});
		AM.OL.map.addControl(AM.OL.Control.permalinkControl);
	    
		AM.OL.map.addControl(new OpenLayers.Control.MousePosition());
	}
};

/* ------------------------------------------------------------------ */
/* Object for handling basic ExtJs and layout controls and functions. */
/* ------------------------------------------------------------------ */
AM.Ext = {
	viewport: null,
	toolbar: null,
	mapLayersChanged: false,
	MapLayers:{},
	Search:{},
	showLoading: function() {
		Ext.get('request-load-pnl').show();
	},
	hideLoading: function() {
		Ext.get('request-load-pnl').hide();	
	},
	showAlert: function(msg, title) {
		if(!title) {
			title = 'Message';
		}
		Ext.Msg.alert(title, msg);
	},
	layerSwitcherActive: false,
	initViewPort:function() {
		AM.Ext.viewport = new Ext.Viewport({
			layout: 'border',
			defaults: { activeItem:0 },
			items: [{
				/* NORTH Section */
	            			region: 'north',
	            			html: AM.config.header_html,
	            			autoHeight: true,
	            			border: false,
	            			margins: '0 0 5 0'
				}, {
				/* CENTER Section */
	            			region: 'center',
	            			xtype: 'tabpanel',
					listeners: { 
						tabchange: { fn:function(panel, tab) {
							if(tab.id == 'view') {
                                			if (AM.OL.Control.permalinkControl) {
                                    			AM.OL.Control.permalinkControl.updateLink();
                                			}
								
								if(AM.Ext.layerSwitcherActive) {
									AM.OL.Control.layerSwitcherControl.maximizeControl();
								}
								
								var rows = AM.Ext.Places.grid.getSelections();
								var selected = [];
								for(var r = 0; r < rows.length; r++) {
									selected.push(rows[r].data.code);
								}
								var selectedTxt = selected.join(',');
								if(selectedTxt != dataLayers.GeonamesFeatures6.selectedFCodes.join(',')) {
									dataLayers.GeonamesFeatures6.searching = true; // If this layer was turned off, force it back on
									dataLayers.GeonamesFeatures6.selectedFCodes = selected;
									AM.Ext.Search.performSearch( AM.Ext.Search.searchTB.getValue() );
								}
							} else {
								if(AM.OL.Control.layerSwitcherVisible()) {
									AM.OL.Control.layerSwitcherControl.minimizeControl();
									AM.Ext.layerSwitcherActive = true;
								} else {
									AM.Ext.layerSwitcherActive = false;
								}
							}
						}
					}
					},
	            			items: [
						{ id:'view', title: 'View',	html:AM.config.map_html },
						AM.Ext.MapLayers.Grid.grid,
						AM.Ext.Places.grid,
						{ xtype:"panel", title:"About", html: AM.config.about_html}
					]
				}]
	    });
	},
	initToolbar: function() {
      
		AM.Ext.toolbar = new Ext.Toolbar({
			id: 'tlbr',
			items:[AM.Ext.Search.searchTB, 
				' ',{
					xtype:'button',
					text:'Search',
					handler:function(btn, e) {
						AM.Ext.Search.performSearch( AM.Ext.Search.searchTB.getValue() );
					}
				},' ',{
					xtype:'button',
					text:'Reset',
					handler:function(brn, e) {
						AM.Ext.Results.reset();
						AM.Ext.Search.reset();
						AM.Ext.Places.reset();
						AM.OL.Control.permalinkControl.updateLink();
					}
				},' ', '-', {
					id: 'request-load-pnl',
					xtype: 'panel',
					html: '<img src="images/ajax-loader.gif" alt="Request loading...">'
				},' ','->', '<a id="permalink" href="javascript:void(0)" onclick="AM.OL.Control.onPermalinkClick(this)">Link to this Map</a>', 
            ' ', ' ', ' ',  '&nbsp; &nbsp; ' ],
			applyTo: 'map'
		});
		Ext.get('request-load-pnl').hide();
		Ext.get('search-tb').on('keyup', function(e, field) {
			if(e.keyCode == '13') {
				var searchTerm = field.value;
				AM.Ext.Search.performSearch(searchTerm);
			}
		});
	}
};

/* ------------------------------------------------------------------------------- */
/* Object for handling OpenLayers-related functions for the overlay layers section */
/* ------------------------------------------------------------------------------- */
AM.Ext.MapLayers.OL = {
	/* Turn layer on, including turning on checkbox. opacity is optional. */
	activateLayer: function(layer, opacity, hidden) {
		var visibility = !hidden;
	    layer.openlayers_layer = new OpenLayers.Layer.TMS(layer.name, 
	        AM.config.tilecache_url, 
			{ africaMap:true, layername:layer.layer_name, extension:'png', isBaseLayer:false, opacity:opacity, buffer:0, visibility: visibility  }
		);
	    AM.OL.map.addLayer(layer.openlayers_layer);
        //layer.openlayers_layer.setVisibility ( visibility );
	    layer.active = true;
	    if (AM.Ext.MapLayers.Grid.grid.rendered) {
			Ext.getDom('cb_add_' + layer.id).checked = true;
	    } else {
	        AM.Ext.MapLayers.Grid.grid.on("render", function() {
				Ext.getDom('cb_add_' + this.layer.id).checked = true;
	        }, {layer:layer});
	    }
        //layer.openlayers_layer.setVisibility ( visibility );
        return layer.openlayers_layer;
	},
	/* Turn layer off, including turning off checkbox. */
	deactivateLayer: function(layer) {
	    AM.OL.map.removeLayer(layer.openlayers_layer);
	    layer.active = false;
	    if (AM.Ext.MapLayers.Grid.grid.rendered) {
			Ext.getDom('cb_add_' + layer.id).checked = false;
	    } else {
	        AM.Ext.MapLayers.Grid.grid.on("render", function() {
				Ext.getDom('cb_add_' + this.layer.id).checked = false;
	        }, {layer:layer});
	    }
	},
	/* Activate any overlay layers specified in the query string params (from the permalink) */
	activateLayersFromQS: function() {
		var numAdded = 0;
	    var params = AM.Util.getParameters(), layers = '', amLayers = [];
		
		for(var k = 0; k < params.length; k++) {
			var key = params[k].key;
			var paramVal = params[k].value;
			if(key == 'layers') {
				layers = paramVal;
			}
	        if (key.substring(0, 10) == "africamap_") {
				amLayers.push({ name:key.substring(10), opacity:paramVal });
			}
            if (key == "searching") {
                dataLayers.GeonamesFeatures6.searching = false;
				var search_layers = paramVal.split(',');
                for(var i = 0; i < search_layers.length; i++) {
                    layer  = search_layers[i];
                    dataLayers[layer].searching = true;
                    numAdded++;
                }
            }
		}

        var visibilityFlags = [];	
        for(var j = 0; j < layers.length; j++) {
            if (layers[j] == 'F' || layers[j] == 'T') {
                visibilityFlags.push(layers[j]);
            }
        }	

		for(var i = 0; i < amLayers.length; i++) {
           // alert ("looking at visibility of: " + amLayers[i].name );
			var index = AM.Ext.MapLayers.Grid.findGridRow(amLayers[i].name);
			if (index != null) {
				var layer = AM.Ext.MapLayers.Grid.store.data.items[index].data;
				var hidden = false;
                //alert("checking visibility for i (" + i.toString() + "), result: " + visibilityFlags[i]);
                var ol_layer;
				//if(1 || visibilityFlags[i] == 'F') {
			//		hidden = true;
			    ol_layer = AM.Ext.MapLayers.OL.activateLayer(layer, amLayers[i].opacity, true );
			//	} else {
			//	    ol_layer = AM.Ext.MapLayers.OL.activateLayer(layer, amLayers[i].opacity, false);
             //       ol_layer.setVisibility(true); 

              //  }
			}
		}
       
        for(var i = 0; i < amLayers.length; i++) {
            var index = AM.Ext.MapLayers.Grid.findGridRow(amLayers[i].name);
            if ( index != null ) {
               var layer = AM.Ext.MapLayers.Grid.store.data.items[index].data;
                if (visibilityFlags[i] == 'F') {
                    layer.openlayers_layer.setVisibility(false);
                } else {
                    layer.openlayers_layer.setVisibility(true);
                }
            }
        } 
		return numAdded > 0;
	},
	/* Activate any overlay layers that are set to be active by default */
	activateDefaultLayers: function() {
		AM.Ext.MapLayers.Grid.store.each(function(rec) {
            var data = dataLayers[rec.data.id];
            
			if(dataLayers[rec.data.id] && dataLayers[rec.data.id].visibleByDefault) {
				AM.Ext.MapLayers.OL.activateLayer(rec.data, 1);
			}

            if (rec.get("visibleByDefault") == true || rec.get("visibleByDefault") == "true") {
                AM.Ext.MapLayers.OL.activateLayer(rec.data, 1);
            }
		});
	},
	/* Activates all layers that should be active by default.  Starts with query string layers, and moves to those set by config. */
	activateStartingLayers: function() {
		// Add layers from query string
		if(!AM.Ext.MapLayers.OL.activateLayersFromQS()) {
			// Add layers that should be on by default
			AM.Ext.MapLayers.OL.activateDefaultLayers();
		}
	},
	zoomTo: function(id) {
		var record = AM.Ext.MapLayers.Grid.store.getById(id);
		if(record && record.data.data_extent.length && record.data.data_extent.length == 4) {
                      AM.OL.map.zoomToExtent(OpenLayers.Bounds.fromArray(record.data.data_extent));
		}
	}
};

/* ------------------------------------------------------------------------------ */
/* Object for handling ExtJS GridPanel column renders for the overlay layers grid */
/* ------------------------------------------------------------------------------ */
AM.Ext.MapLayers.Renderers = {
	make_link:function(text, metadata, row) {
		var text = '<input id="cb_add_' + row.data.id + '" type="checkbox" style="padding:0;margin:0;" ';
		if(row.data.active) {
			text += 'checked="checked" ';
		}
		text += '/>';
		return text;
	},
	metadata_link:function(text, metadata, row) {
		return '<a title="Metadata" target="new" href="' + text + '">Metadata</a>';
	},
	download_link:function(text, metadata, row) {
		return '<a title="Download" target="new" href="' + text + '">Download</a>';
	},
    legend_link:function (val, metadata, rec) {
        if (val != '') {
            return '<a title="View Legend" href="javascript:void(0)" onclick="AM.Ext.MapLayers.Grid.openLegendWindow(\'' + rec.id + '\')">Legend</a>';
        } else {
            return '';
        }
    },
	zoom_link:function(val, metadata, rec) {
        return '<a title="Zoom to this layer" href="javascript:void(0)">Zoom To</a>';
	},
	searchable_render: function(val, metadata, rec) {
		var text = '';
		if(dataLayers[rec.data.id]) {
			text += '<input id="cb_lyr_srch_' + rec.data.id + '" type="checkbox" style="padding:0;margin:0;" ';
			if(dataLayers[rec.data.id].searching) {
				text += 'checked="checked" ';
			}
			text += '/>';
		} else {
			text = 'n/a';
		}
		return text;
	},
	reference_renderer: function(val, metadata, rec) {
		if(val) {
			return '<a href="' + val + '" target="new">Reference</a>';
		} else {
			return '';
		}
	},
	threeD_renderer: function(val, metadata, rec) {
		if(val) {
			return '<a href="' + val + '" target="new">3D</a>';
		} else {
			return '';
		}
	}
};

/* -------------------------------------------------------------- */
/* Object for handling ExtJS GridPanel the overlay layers section */
/* -------------------------------------------------------------- */
AM.Ext.MapLayers.Grid = {
	store:null,
	grid:null,
	gridColumns:[
		{header: "Add", width: 35, renderer: AM.Ext.MapLayers.Renderers.make_link, sortable: false},
		{header: "Search", width: 50, renderer: AM.Ext.MapLayers.Renderers.searchable_render, sortable: false},
		{header: "Type", width: 90, sortable: true, dataIndex: 'type'},
		{header: "Name", width: 160, sortable: true, dataIndex: 'name'},
		{header: "Description", width: 200, sortable: true, dataIndex: 'description'},
		{header: "Date", width: 60, sortable: true, dataIndex: 'date'},
		{header: "Source", width: 90, sortable: true, dataIndex: 'source'},
		{header: "Scale", width: 60, sortable: true, dataIndex: 'scale'},
		{header: "Extent Description", width: 90, sortable: true, dataIndex: 'extent_type'},
		{header: "Language", width: 60, sortable: true, dataIndex: 'language'},
        	{header: "Legend", width: 60, sortable: false, dataIndex: 'legend', renderer: AM.Ext.MapLayers.Renderers.legend_link},
		{header: "Zoom", width: 60, sortable: true, dataIndex: 'bbox', renderer: AM.Ext.MapLayers.Renderers.zoom_link},
		{header: "Reference", width: 60, sortable: false, dataIndex: 'reference', renderer: AM.Ext.MapLayers.Renderers.reference_renderer},
		{header: "3D", width:30, sortable: false, dataIndex: 'ddd', renderer: AM.Ext.MapLayers.Renderers.threeD_renderer},
		{header: "Metadata", width: 60, sortable: true, dataIndex: 'metadata_url', renderer: AM.Ext.MapLayers.Renderers.metadata_link},
		{header: "Download", width: 60, sortable: true, dataIndex: 'download_url', renderer: AM.Ext.MapLayers.Renderers.download_link},
        	{header: "Order", hidden: true, dataIndex: 'order', width: 30}
	],
	record: Ext.data.Record.create([
        { name:'id', mapping:'name' },
        { name:'name', mapping:'metadata.title' },
        { name:'date', mapping:'metadata.date' },
        { name:'scale', mapping:'metadata.scale' },
        { name:'type', mapping:'metadata.map_type' },
        { name:'extent_type', mapping:'metadata.extent_type' },
        { name:'language', mapping:'metadata.language' },
        { name:'metadata_url', mapping:'metadata.metadata_url' },
        { name:'download_url', mapping:'metadata.download_url' },
        { name:'source', mapping:'metadata.source' },
        { name:'description', mapping:'metadata.description' },
        { name:'layer_name', mapping:'name' },
        { name:'legend', mapping: 'metadata.legend' },
        { name:'visibleByDefault', mapping: 'metadata.visibleByDefault' },
        { name:'order', mapping:'metadata.order' },
	 { name:'ddd', mapping:'metadata.threed' },
	 { name:'reference', mapping:'metadata.reference' },
        'srs', 'data_extent', 'bbox', 'searchable'
    ]),
	initStore:function() {
		AM.Ext.MapLayers.Grid.store = new Ext.data.GroupingStore({
            url: AM.config.tilecache_url + '?format=JSON&type=list&has_metadata_key=show_in_' + AM.config.instance_name,
			reader: new Ext.data.JsonReader({root: 'layers', id: 'name'}, AM.Ext.MapLayers.Grid.record),
			sortInfo: {field: 'name', direction: 'ASC'},
			groupField: 'type'
		});
	    AM.Ext.MapLayers.Grid.store.on("load", function(store, records, options) {
			AM.Ext.MapLayers.OL.activateStartingLayers();

			AM.Ext.MapLayers.Grid.store.each(function(rec) {
				if(dataLayers[rec.data.id]) { 
					rec.data.searchable = true;
				}
			});
		});
	    AM.Ext.MapLayers.Grid.store.load();
	},
	cell_click: function(grid,row_index,column_index,event) {
        var layer = AM.Ext.MapLayers.Grid.store.data.items[row_index].data;
		
        if(column_index == 0) {
            if (layer.active) {
                AM.Ext.MapLayers.OL.deactivateLayer(layer);
            } else {
                AM.Ext.MapLayers.OL.activateLayer(layer);
            }
        } else if(column_index == 1) {
			var cb = Ext.getDom('cb_lyr_srch_' + layer.id);
			if(cb) {
				dataLayers[layer.id].searching = cb.checked;
			}
        } else if (column_index == 11) {
             AM.Ext.viewport.items.items[1].activate(0);
             AM.OL.map.zoomToExtent(OpenLayers.Bounds.fromArray(layer.data_extent));
        }
	},
	initGrid: function() {
	    AM.Ext.MapLayers.Grid.grid = new Ext.grid.GridPanel({
	        title:'Map Layers',
	        store: AM.Ext.MapLayers.Grid.store,
	        columns: AM.Ext.MapLayers.Grid.gridColumns,
	        stripeRows: true,
			view: new Ext.grid.GroupingView({
				groupTextTpl: '{group} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',
                		style: 'width: 425px',
				hideGroupedColumn: true,
				startCollapsed: true
			})
	    });
	    AM.Ext.MapLayers.Grid.grid.on("cellclick", AM.Ext.MapLayers.Grid.cell_click);
	},
	/* Take the name of a layer from TileCache data and return its row ID */
	findGridRow: function(key) {
	    for (var i = 0; i < AM.Ext.MapLayers.Grid.store.data.keys.length; i++) {
	        if (key == AM.Ext.MapLayers.Grid.store.data.keys[i]) {
	            return i;
	        }
	    }
	    return null;
	},
	openLegendWindow: function(id) {
		var rec = AM.Ext.MapLayers.Grid.store.getById(id);
		
		var img = document.createElement('img');
		img.src = rec.data.legend;
		
		var width = 800, height = 300;
		if(img && img.width && img.width > 0 && (img.width + 30) < width) {
			width = (img.width + 30);
		}
		if(img && img.height && img.height > 0 && (img.height + 30) < height) {
			height = (img.height + 30);
		}
		
		if(rec) {
			var imgHtml = '<img src="' + rec.data.legend + '" alt="Legend" />';
			var win = new Ext.Window({
				id: 'legend-win',
				title: rec.data.name + ' Legend',
				autoScroll: true,
                width: width,
                height: height,
                x: 50,
                y: 100,
		html: imgHtml
			});
			//win.add(img);
			win.show();
        }
	}
};

/* ------------------------------------------------- */
/* Object for handling search controls and functions */
/* ------------------------------------------------- */
AM.Ext.Search = {
	searchTB: null,
	searchTerm: '',
	initStores: function() {
	},
	initControls: function() {
		AM.Ext.Search.searchTB = new Ext.form.TextField({
			id:'search-tb',
			width:450,
			emptyText:'Enter search...'
		});
	},
	performCountSearch: function(terms) {
		var params = { tables:'', search_strings:terms.join('|'), fcodes:'', bbox:AM.OL.map.getExtent().toBBOX(), instance_name : AM.config.instance_name };
				
		for(dlid in dataLayers) {
			var dl = dataLayers[dlid];
			
			if(dl.searching && (terms.length > 0 || (dl.id == 'geonames' && dl.selectedFCodes.length > 0))) {
				if(params.tables.length > 0) { params.tables += '|'; }
				params.tables += dl.tablename;
				
				params[dl.tablename+'_columns'] = dl.searchableCols.join('|');
				params[dl.tablename+'_geometry_field'] = dl.geometry_col_name;

				if(dl.id == 'geonames' && dl.selectedFCodes && dl.selectedFCodes.length > 0) {
					params.fcodes = dl.selectedFCodes.join('|');
				}
			}
		}
		
		if(params.tables.length > 0) {
            AM.Ext.showLoading();

			Ext.Ajax.request({
				'url':AM.config.feature_count_url,
				'success':function(resp, opts) {
                    AM.Ext.hideLoading();
					if(resp.responseText == '') {
						// do something
					}
				
					var countObj = Ext.decode(resp.responseText);
					
					if(countObj.total_at_extent == 0) { // No features at this search/extent
                        if (countObj.total_in_full_map == 0) {
                            AM.Ext.showAlert('There are no features that match your search.', 'Search Results');
                        } else {
						    AM.Ext.showAlert('There are no features that match your search at this extent.  Zoom out to see more features.', 'Search Results');
					    } 
                    }
				},
				'failure':function(resp, opts) {
                    AM.Ext.hideLoading();
					// something bad happened in request.
				},
				'headers':{},
				'params':params
			});
		}
	},
	performSearch: function(_searchTerm) {
        var searchTerm = _searchTerm;
		AM.Ext.Search.searchTerm = searchTerm;
		AM.Ext.Results.reset();
		AM.OL.Control.permalinkControl.updateLink();
	
        var terms = [];	
		// FOR DOUBLE QUOTES SUPPORT
		//var termsByQuotes = searchTerm.split('"');

		//var terms = [];
		//for(var i = 0; i < termsByQuotes.length; i++) {
            
	//		if(termsByQuotes[i].length > 0) {
	//			terms = terms.concat(termsByQuotes[i].split(' '));
	//		}
	//	}

        var matchingSearchTerm = searchTerm;
        var re = /("[^"]+")/g;
        var m = matchingSearchTerm.match(re);

        var otherSearchTerm = searchTerm; 
        if ( m != null ) {
            for ( i = 0; i < m.length; i++ ) {
                otherSearchTerm = otherSearchTerm.replace(m[i], '');
                terms.push( m[i].replace(/"/g, '' ));
            }	
        } else {
        }
         
        var whitespace_re = /\s+/;
        otherSearchTerm = otherSearchTerm.replace(whitespace_re, ' ');

        terms = terms.concat(otherSearchTerm.split(' '));

        for ( j = 0; j < terms.length; j++) {
        }
        AM.Ext.Search.performCountSearch(terms);
		
		// Clear out previous search results
		var searchCount = 0;
		for(dlid in dataLayers) {
			var dl = dataLayers[dlid];
			// This extra stuff in the "if" statement allows searches to go through, even when there is no search term, if it's the geonames layer AND there is at least one FCode selected in the "Places" grid.
			if(dl.searching && (searchTerm != '' || (dl.id == 'geonames' && dl.selectedFCodes.length > 0))) {
				searchCount++;
				if(dl.olLayer) {
					dl.runSearch(searchTerm);
				}
			} else if(dl.activeOnMap) { // If the layer was on, but has been turned off since the last search, remove it.
				AM.OL.map.removeLayer(dl.olLayer);
				dl.activeOnMap = false;
			}
		}
		
		if(searchCount == 0 && searchTerm != '') {
			var msg = 'There are no layers active for searching.<br />' + 
				'To add searchable layers, click the "Map Layers" tab, and select at least one layer as searchable.';
			AM.Ext.showAlert(msg, 'Search');
		}
	},
	searchByXY: function(x, y) {
		var pixel = new OpenLayers.Pixel(x, y);
		var lonlat = AM.OL.map.getLonLatFromPixel(pixel);
		
		AM.Ext.Results.reset();
		
		//if(AM.Ext.Results.gridMarker) {
		//	AM.OL.markerLayer.removeMarker(AM.Ext.Results.gridMarker);
		//}
		AM.Ext.Results.gridMarker = new OpenLayers.Marker(lonlat, AM.OL.markerIcon.clone());
		AM.OL.markerLayer.addMarker(AM.Ext.Results.gridMarker);

		var count = 0, successCount = 0;
		var features = [];
		
		for(dl in dataLayers) {
			if(dataLayers[dl].searching) {
				count++;
			}
		}
		
		if(count > 0) {
			var zoom = AM.OL.map.getZoom();
			var xyDivisor = zoom - 2;
			if(xyDivisor < 1) { xyDivisor = 1; }
		
			AM.Ext.showLoading();
			for(dlid in dataLayers) {
				var dl = dataLayers[dlid];
				
				if(!dl.olLayer.map) {
					dl.olLayer.map = AM.OL.map;
				}
				
				if(dl.searching) {
					var params = { //SRS:'EPSG:4326',
						REQUEST: "GetFeatureInfo",
						EXCEPTIONS: "application/vnd.ogc.se_xml",
						BBOX: AM.OL.map.getExtent().toBBOX(),
						X: (x/xyDivisor),
						Y: (y/xyDivisor),
						INFO_FORMAT: 'text/html',
						QUERY_LAYERS: dl.olLayer.params.LAYERS,
						CQL_FILTER: (dl.olLayer.params.CQL_FILTER ? dl.olLayer.params.CQL_FILTER : ''),
                        FEATURE_COUNT: 50,
						//MAXFEATURES: '50000',
                        //MAXFEATURES: '0',
						WIDTH: (AM.OL.map.size.w/xyDivisor),
						HEIGHT: (AM.OL.map.size.h/xyDivisor),
						VERSION: '1.1.1'
					};
					params = dl.getFeatureParams(params);
					var url =  dl.olLayer.getFullRequestString(params,
						AM.config.mapserver_base_url // map server feature requests
						//"http://localhost/AfricaMapProxy/urlproxy.aspx" // local proxy for dev
					);
					
					if(!dl.activeOnMap) {
						dl.olLayer.map = null;
					}
					
					Ext.Ajax.request({
						'url':url,
						'success':function(resp, opts) {
							successCount++;
							
							if(resp.responseText != '' && resp.responseText.substr(0,1) != "[") {
								var msg = 'The feature request returned an error.';
								msg += '<br />details: ' + resp.responseText;
								AM.Ext.showAlert(msg, 'Request Error');
								return;
							}
							
							if(resp.responseText != '') {					
                                var responseText = resp.responseText.replace(/population: "0"/g, "population: ''");
								var tempFeatures = Ext.decode(responseText);
														
								// This is because the last item should be a null
								if(tempFeatures.length > 0) {
									tempFeatures.pop();
								}
								
								features = features.concat(tempFeatures);
							}
							
							if(successCount == count) {
								if(features.length == 0) {
									AM.Ext.showAlert('No features found at this location.', 'Map Results');
								} else {								
									AM.Ext.Results.displayXYResults(features, x, y);
								}
								AM.Ext.hideLoading();
							}
						},
						'failure':function(resp, opts) {
							var msg = 'The feature request failed.';
							msg += '<br />details: ' + resp.responseText;
							AM.Ext.showAlert(msg, 'Request Failed');
							AM.Ext.hideLoading();
						},
						'headers':{},
						'params':{}
					});
				}
			}
		} else {
			//AM.Ext.showAlert('There are no searchable layers active.', 'Searchable Layers');
		}
	},
	performFromQS: function() {
		var runSearch = false;
		var searchTerm = '';
	    var params = OpenLayers.Util.getParameters();
		if(params.fcodes && params.fcodes != '') {
			var codes = params.fcodes.toString().split(',');
			if(codes.length > 0) {
				dataLayers.GeonamesFeatures6.selectedFCodes = codes;
				runSearch = true;
			}
		}
		if(params.srchterm && params.srchterm != '') {
			searchTerm = decodeURI(params.srchterm.toString());
			AM.Ext.Search.searchTB.setValue(searchTerm);
			runSearch = true;
		}
		if(runSearch) {
			AM.Ext.Search.performSearch(searchTerm);
		}
	},
	reset: function() {
		AM.Ext.Search.searchTB.setValue('');
		AM.Ext.Search.searchTerm = '';
		
		if(AM.Ext.Results.gridMarker) {
			AM.OL.markerLayer.removeMarker(AM.Ext.Results.gridMarker);
		}

		for(dl in dataLayers) {
			if(dataLayers[dl].activeOnMap) {
				dataLayers[dl].activeOnMap = false;
                		dataLayers[dl].reset();
				AM.OL.map.removeLayer(dataLayers[dl].olLayer);
			}
		}
	}
};

/* --------------------------------------------------------- */
/* Object for handling search results controls and functions */
/* --------------------------------------------------------- */
AM.Ext.Results = {
	gridWin: null,
	resultWin: null,
	currentFeatures: [],
	gridMarker: null,
	resultMarker: null,
	reset: function() {
		if(AM.Ext.Results.resultWin) {
			AM.Ext.Results.resultWin.close();
			AM.Ext.Results.resultWin = null;
		}
		if(AM.Ext.Results.resultMarker) {
			AM.OL.markerLayer.removeMarker(AM.Ext.Results.resultMarker);
			AM.Ext.Results.resultMarker = null;
		}
		if(AM.Ext.Results.gridWin) {
			AM.Ext.Results.gridWin.close();
			AM.Ext.Results.gridWin = null;
		}
		if(AM.Ext.Results.gridMarker) {
			AM.OL.markerLayer.removeMarker(AM.Ext.Results.gridMarker);
			AM.Ext.Results.gridMarker = null;
		}
	},
	resultsToKml: function() {
	},
	displayXYResults: function(features, x, y) {
		AM.Ext.Results.currentFeatures = features;
		var reader = new Ext.data.JsonReader({}, [
		   {name: 'type'},
		   {name: 'name'},
		   {name: 'category'},
		   {name: 'id'},
		   {name: 'layer'}
		]);
		
		var winWidth = 450;
		var mapDiv = Ext.get('map');
		var mapWidth = mapDiv.getSize().width;
		var startX = 50;
		if(x < (mapWidth / 2)) { // on the right
			startX = mapWidth - (winWidth + 50);
		}
        var gridPanel = new Ext.grid.GridPanel({
			store:new Ext.data.GroupingStore({
				reader: reader,
				data: AM.Ext.Results.currentFeatures,
				sortInfo:{field: 'name', direction: "ASC"},
				groupField:'layer'
			}),
			columns:[
				{ id:'name', sortable:true, header:'Name', dataIndex:'name', width:190 },
				{ header:'Feature Type', dataIndex:'type', width:0, hidden:true },
				{ header:'Type', sortable:true, dataIndex:'category', width:215, renderer:function(v, md, r) {
						if(r.data.layer === 'Places') {
							return v;
						} else {
							return '';
						}
					} },
				{ header:'Layer', sortable:false, dataIndex:'layer', width:0, hidden:true }
			],
			view: new Ext.grid.GroupingView({
				//forceFit:true,
				groupTextTpl: '{group}',
                		style: 'width: 425px'
			}),
			sm: new Ext.grid.RowSelectionModel({
				singleSelect: true,
				listeners: {
					rowselect: { 
						fn: function(sm, rowIndex, rec) {
							AM.Ext.Results.displaySingleResult(rowIndex, rec.data);
						} 
					}
				}
			}),
			layout: 'fit',
			frame:false,
			collapsible: true,
			title: '',
			iconCls: 'icon-grid',
		     autoHeight:true,
            style: 'width: 425px',
            width: '400'

			//autoExpandColumn:'name',
		});

		AM.Ext.Results.gridWin = new Ext.Window({
			id:'search-results-win',
			title: 'Results',
			items: gridPanel,
			height: 300,
			width:winWidth,
			autoScroll:true
		});
		AM.Ext.Results.gridWin.setPosition(startX, 140);
		AM.Ext.Results.gridWin.show();
		gridPanel.getSelectionModel().selectFirstRow();
		AM.Ext.Results.gridWin.on('close', function() {
			if(AM.Ext.Results.gridMarker) {
				AM.OL.markerLayer.removeMarker(AM.Ext.Results.gridMarker);
			}
		});
	},
	displaySingleResult: function(rowIndex, gridFeature) {
		// Close the previous single result window, if open
		if(AM.Ext.Results.resultWin) {
			AM.Ext.Results.resultWin.close();
			AM.Ext.Results.resultWin = null;
		}
		if(AM.Ext.Results.resultMarker) {
			AM.OL.markerLayer.removeMarker(AM.Ext.Results.resultMarker);
		}
		
		var feature = null;
		// Look for the feature in the full collection of features (the grid store only has simplified objects)
		for(var i = 0; i < AM.Ext.Results.currentFeatures.length; i++) {
			if(AM.Ext.Results.currentFeatures[i].id == gridFeature.id) {
				feature = AM.Ext.Results.currentFeatures[i];
			}
		}
		
		if(!feature) {
			return;
		}
		
		if(AM.Ext.Results.resultMarker) {
			AM.OL.markerLayer.removeMarker(AM.Ext.Results.resultMarker);
		}
		if(feature.longitude) {
			var lonlat = new OpenLayers.LonLat(feature.longitude, feature.latitude);
			lonlat = lonlat.transform(AM.OL.map.displayProjection, AM.OL.map.projection);
			
			AM.Ext.Results.resultMarker = new OpenLayers.Marker(lonlat, AM.OL.markerIcon2.clone());
			AM.OL.markerLayer.addMarker(AM.Ext.Results.resultMarker);
		}
		
		var gridWinXY = AM.Ext.Results.gridWin.getPosition(true);
		
		var tpl = new Ext.Template(amTemplates[gridFeature.type]);

        if (feature.start_d == '1899-12-30') {
            feature.start_d = '';
        }

        if (feature.end_d == '1899-12-30') {
            feature.end_d = '';
        }

		var featureHtml = tpl.applyTemplate(feature);
		AM.Ext.Results.resultWin = new Ext.Window({
			id: 'feature-win',
			title: 'Feature Details',
			html:featureHtml,
			width:AM.Ext.Results.gridWin.getSize().width,
			height:'auto'
		});
		AM.Ext.Results.resultWin.setPagePosition(gridWinXY[0], 450);
		AM.Ext.Results.resultWin.show();
		AM.Ext.Results.resultWin.on('close', function() {
			if(AM.Ext.Results.resultMarker) {
				AM.OL.markerLayer.removeMarker(AM.Ext.Results.resultMarker);
			}
		});
	}
}

/* ---------------------------------------------- */
/* Object for handling the Geonames features grid */
/* ---------------------------------------------- */
AM.Ext.Places = {
	store: null,
	record: [
		{ name:'fclass', mapping:'feature_class' },
		{ name:'code' },
		{ name:'count', type:'int' },
		{ name:'name' },
		{ name:'description' }
	],
	grid: null,
	gridColumns: [
		{ header: "", dataIndex: 'fclass', hidden:true, hideable: false },
		{ header: "Code", width: 60, dataIndex: 'code', sortable:true },
		{ header: "Name", width: 200, dataIndex: 'name', sortable:true },
		{ header: "Quantity", width: 50, dataIndex: 'count', sortable:true },
		{ header: "Description", dataIndex: 'description', id:'desc' }
	],
	activateQSRecords: function() {
	    var params = OpenLayers.Util.getParameters();
		if(params.fcodes && params.fcodes != '') {
			var codes = params.fcodes.toString().split(',');
			if(codes.length > 0) {
				var sm = AM.Ext.Places.grid.getSelectionModel();
				var recIndexes = [];
				for(var i = 0; i < codes.length; i++) {
					var recIndex = AM.Ext.Places.store.find('code', codes[i]);
					if(recIndex > -1) {
						recIndexes.push(recIndex);
					}
				}
				if(recIndexes.length > 0) {
					sm.selectRows(recIndexes);
				}
			}
		}
	},
	initStore: function() {
		AM.Ext.Places.store = new Ext.data.GroupingStore({
			url: 'fcodes.json',
			reader: new Ext.data.JsonReader({ root:'fcodes' }, AM.Ext.Places.record),
			sortInfo:{field: 'count', direction: "DESC"},
			groupField:'fclass'
		});
		AM.Ext.Places.store.load();
	},
	initGrid: function() {
		var checkBoxSM = new Ext.grid.CheckboxSelectionModel({ header: 'Add', id:'Show', width:40 });
		AM.Ext.Places.gridColumns.splice(0, 0, checkBoxSM);
		AM.Ext.Places.grid = new Ext.grid.GridPanel({
			title: 'Places',
			store: AM.Ext.Places.store,
			columns: AM.Ext.Places.gridColumns,
			sm: checkBoxSM,
			view: new Ext.grid.GroupingView({
				groupTextTpl: '{text}',
				enableGroupingMenu: false
			}),
			stripRows: true,
			autoExpandColumn:'desc',
            showGroupName: false
		});
		AM.Ext.Places.grid.on('render', function() {
			AM.Ext.Places.activateQSRecords();
		});
	},
	reset: function() {
		AM.Ext.Places.grid.getSelectionModel().clearSelections();
	}
};

AM.Util = {
	// This is exactly the same function as OpenLayers.Util.getParameters, except it returns and ordered Array instead of an Object literal
	getParameters: function(url) {
		// if no url specified, take it from the location bar
		url = url || window.location.href;

		//parse out parameters portion of url string
		var paramsString = "";
		if (OpenLayers.String.contains(url, '?')) {
			var start = url.indexOf('?') + 1;
			var end = OpenLayers.String.contains(url, "#") ?
				url.indexOf('#') : url.length;
			paramsString = url.substring(start, end);
		}

		var parameters = [];
		var pairs = paramsString.split(/[&;]/);
		for(var i=0, len=pairs.length; i<len; ++i) {
			var keyValue = pairs[i].split('=');
			if (keyValue[0]) {
				var key = decodeURIComponent(keyValue[0]);
				var value = keyValue[1] || ''; //empty string if no value

				//decode individual values
				value = value.split(",");
				for(var j=0, jlen=value.length; j<jlen; j++) {
					value[j] = decodeURIComponent(value[j]);
				}

				//if there's only one value, do not return as array                   
				if (value.length == 1) {
					value = value[0];
				}               

				parameters.push({
					key: key,
					value: value
				});
			}
		}
		return parameters;
	}
};

/* ---------------------------------------------------------- */
/* OnReady function.  Handles things that need to run on init */
/* ---------------------------------------------------------- */
Ext.onReady(function() {
	AM.Ext.MapLayers.Grid.initStore();
	AM.Ext.MapLayers.Grid.initGrid();

	AM.Ext.Places.initStore();
	AM.Ext.Places.initGrid();
	
	AM.Ext.Search.initControls();
	
	AM.Ext.initViewPort();
	AM.Ext.initToolbar();

	AM.OL.init();
	
    AM.OL.map = new OpenLayers.Map('map', AM.OL.map_options);

	AM.OL.map.events.register('click', AM.OL.map, AM.OL.onMapClick);
	
	AM.OL.addBaseLayers();
	AM.OL.Control.addMapControls();
    
    if (!AM.OL.map.getCenter()) {
        AM.OL.map.zoomToExtent(
			new OpenLayers.Bounds(-18.2813, -35.5078, 52.0313, 38.6719).transform(AM.OL.map.displayProjection, AM.OL.map.projection)
		);
    }
	
	// For some reason running this right away is causing problems with the base maps.
	setTimeout('AM.Ext.Search.performFromQS()', 500);
});

