/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/*
 * main()
 * Start up script.
 */

var Map = false;
var ReferenceMap = false;
var Services = false; /* Service Manager */
var LatLongProjection = false;
var Tabs;
var Catalog = false;

var Tools = new Array();

function main() {
	/* This sets up the layout functionality in the interface */
	manageLayout();

	Proj4js.libPath = './proj4js/lib/';
	/*
	Proj4js.defs["EPSG:26915"] = "+title=NAD83 / UTM zone 15N +proj=utm +zone=15 +ellps=GRS80 +datum=NAD83 +units=m +no_defs";
	Proj4js.defs["EPSG:26914"] = "+title=NAD83 / UTM zone 14N +proj=utm +zone=14 +ellps=GRS80 +datum=NAD83 +units=m +no_defs";
	*/

	/* Capture onresize so that we can manage the layout as necessary */
	window._onresize = window.onresize;
	window.onresize = function () {
		manageLayout();
		if(window._onresize) {
			window._onresize();
		}
	}

	/* Load the Mapbook */
	OpenLayers.loadURL('php/getmapbook.php', {}, {}, onLoadedMapbook);
}

function configureMap () {
	CONFIGURATION.scales = CONFIGURATION.scales.sort().reverse();

	/* Setup the Map */	
	Map = new OpenLayers.Map('mapContainer', 
		{
			resolutions : CONFIGURATION.scales,
			maxExtent : OpenLayers.Bounds.fromArray(CONFIGURATION.max_extent),
			controls : [new OpenLayers.Control.PanZoomBar(), new OpenLayers.Control.ScaleJumper({target: 'scale-jumper'})],
			units: CONFIGURATION.ground_units,
			projection: new OpenLayers.Projection(CONFIGURATION.projection),
			displayProjection: new OpenLayers.Projection(CONFIGURATION.projection),
			fractionalZoom: CONFIGURATION.fractional_zoom
		}
	);

	//ReferenceMap = new OpenLayers.Control.OverviewMap();
	//Map.addControl(ReferenceMap);


	/* This is the only base layer and allows all of the rest of the layers to be selectively
		shown/hidden without effecting the navigation */
	var navigationLayer = new OpenLayers.Layer.Image('GeoMOOSE Navigation Layer',
		'./images/blank.gif',
		new OpenLayers.Bounds.fromArray(CONFIGURATION.max_extent),
		new OpenLayers.Size(500,500), {
			projection: new OpenLayers.Projection(CONFIGURATION.projection),
			resolutions: CONFIGURATION.scales
		});

	Map.addLayers([navigationLayer]);
	//ReferenceMap.maximizeControl();

	/* Setup the Built-in Tools */
	Tools['pan'] = new OpenLayers.Control.Navigation();
	Tools['zoomin'] = new OpenLayers.Control.ZoomBox();
	Tools['zoomout'] = new OpenLayers.Control.ZoomBox({out: true});

	var measureToolStyle = new OpenLayers.Style();
	measureToolStyle.addRules([
		new OpenLayers.Rule({symbolizer: CONFIGURATION.measure_tool.style})
	]);
	var styleMap = new OpenLayers.StyleMap({"default": measureToolStyle});

	var measureToolOptions = {
		handlerOptions: {
		    style: "default", // this forces default render intent
		    layerOptions: {styleMap: styleMap},
		    persist: true
		}
	};


	Tools['measure'] = new OpenLayers.Control.GeoMooseMeasure(OpenLayers.Handler.MeasurePath, measureToolOptions);
	Tools['measurearea'] = new OpenLayers.Control.GeoMooseMeasure(OpenLayers.Handler.Polygon, measureToolOptions);


	var showMeasureTab = function () { Tabs.showTab(CONFIGURATION.measure_tool.target_tab); };

	Tools['measure'].events.register('activate', Tools['measure'], startupMeasureLength);
	Tools['measure'].events.register('measuremove', Tools['measure'], measureToolLengthLog);
	Tools['measure'].events.register('measurepartial', Tools['measure'], measureToolPartial);

	Tools['measurearea'].events.register('activate', Tools['measurearea'], startupMeasureArea);
	Tools['measurearea'].events.register('measuremove', Tools['measurearea'], measureToolAreaLog);
	Tools['measurearea'].events.register('measurepartial', Tools['measurearea'], measureToolAreaPartial);

	var NavHistory = new OpenLayers.Control.NavigationHistory();
	Map.addControl(NavHistory);

	/* displayCoordinates not only formats the output but adds reprojection when available */
	var coordDisplay = new OpenLayers.Control.MousePosition({div: document.getElementById('coordinate-display')});
	coordDisplay.formatOutput = displayCoordinates;
	Map.addControl(coordDisplay);

	LatLongProjection = new OpenLayers.Projection('WGS84');

	for(var t in Tools) {
		Map.addControl(Tools[t]);
	}
	/* Wrapper objects for navigation history */
	var previous = { activate: function() { NavHistory.previous.trigger(); }, deactivate: function() { } };
	var next = { activate: function() { NavHistory.next.trigger(); }, deactivate: function() { } };

	var fullExtent = { activate: function() { Map.zoomToMaxExtent(); }, deactivate: function () { } };

	Tools['previous'] = previous;
	Tools['next'] = next;
	Tools['fullextent'] = fullExtent;

//	Map.zoomToMaxExtent(); /* Zoom to the initial extent for startup */

	Map.zoomToExtent(OpenLayers.Bounds.fromArray(CONFIGURATION.initial_extent));

}

function displayCoordinates(lonLat) {
        var digits = parseInt(this.numDigits);
	var html = '';
	if(CONFIGURATION.coordinate_display.xy) {
		html += '<span class="coordinate-type">X,Y: </span>';
		html += lonLat.lon.toFixed(digits) + ', ' + lonLat.lat.toFixed(digits);
	}
	var degrees = {'x': lonLat.lon, 'y' : lonLat.lat};
	OpenLayers.Projection.transform(degrees, 
		Map.getProjectionObject(),
		LatLongProjection);

	if(CONFIGURATION.coordinate_display.latlon) {
		html += '<span class="coordinate-type">Lat, Lon: </span>';
		html += degrees.x.toFixed(3) + ', ' + degrees.y.toFixed(3);
	}

	if(CONFIGURATION.coordinate_display.usng) {
		html += '<span class="coordinate-type">USNG: </span>';
		// Map.getResolution returns map units per pixel
        	var inches = OpenLayers.INCHES_PER_UNIT;
		/* Convert the map units to inches, and then inches to meters */
		var metersPerPx = Map.getResolution() * (inches[Map.getUnits()] * (1/inches['m']));
		var digits = 6;
		if(metersPerPx > 10000) {
			digits = 0;
		} else if(metersPerPx > 1000) {
			digits = 1;
		} else if(metersPerPx > 100) {
			digits = 2;
		} else if(metersPerPx > 10) {
			digits = 3;
		} else if(metersPerPx > 1) {
			digits = 4;
		} else if(metersPerPx > .1) {
			digits = 5; 
		}

		html += (new USNG()).fromLatLong(degrees, digits);
	}
/*        var newHtml =
            this.prefix +
            lonLat.lon.toFixed(digits) +
            this.separator + 
            lonLat.lat.toFixed(digits) +
            this.suffix;
*/
        return html;
}


var MENU_TOGGLE_ICON = '';

function displayMenu(rXML) {
	var menuBar = new Menubar();
	menuBar.setParent('menubar');
	menuBar.populateMenuBar(rXML.responseXML);
}

function loadMenu() {
	OpenLayers.loadURL('menu.xml', {}, {}, displayMenu);
}


function onLoadedMapbook(response) {
	var toolbarId = 'toolbar';
	var maxSelectedTool = null;
	if(response.responseXML) {
		/* check version */
		var version = parseFloat(response.responseXML.documentElement.getAttribute('version'));
		if(version < 2.0) {
			alert('Specified mapbook version '+version+' is older than 2.0. No promises this will work.');
		}

		/* Load the configuration bits */
		var configuration = response.responseXML.getElementsByTagName('configuration')[0];
		if(configuration && configuration.childNodes) {
			var params = configuration.getElementsByTagName('param');
			for(var p = 0; p < params.length; p++) {
				var param = params[p];
				var name = param.getAttribute('name');
				var value = OpenLayers.Util.getXmlNodeValue(param);

				/* this is a bit naughty... but it works quite well */
				var typeValue = eval('CONFIGURATION.'+name);
				var jsString = 'CONFIGURATION.'+name+' = ';
				if(typeValue instanceof Array) {
					if(value[0] == '[' && value[value.length-1] == ']') {
						value = value.substring(1,value.length-1);
					}
					jsString += '['+value+']';
				} else if(typeof(typeValue) == "string" || typeValue instanceof String) {
					jsString += 'value';
				} else if(typeValue == true || typeValue == false) {
					jsString += 'parseBoolean(value)';
				} else {
					value = '('+value+')';
					jsString += 'eval(value)';
				}
				//console.log(name, typeof(typeValue));
				//console.log(jsString);
				//console.log(eval(value));
				eval(jsString);
			}
		}

		var mandatory = ['mapfile_root', 'mapserver_url'];
		for(var i =0; i < mandatory.length; i++) {
			if(CONFIGURATION[mandatory[i]] == "") {
				alert(mandatory[i]+' must be configured in the mapbook'); 
			}
		}

		configureMap();

		var tools = response.responseXML.getElementsByTagName('tool');
		for(var i = 0; i < tools.length; i++) {
			addTool(toolbarId, tools[i]);
			if(parseBoolean(tools[i].getAttribute('selected'))) {
				maxSelectedTool = tools[i];
			}
		}
	
		var services = response.responseXML.getElementsByTagName('map-source');
		for(var i = services.length - 1; i >= 0; i--) {
			var type = services[i].getAttribute('type').toLowerCase();
			var layer = false;
			var params = services[i].getElementsByTagName('param');
			var paramHash = {};

			var url = OpenLayers.Util.getXmlNodeValue(services[i].getElementsByTagName('url')[0]);

			var tiled = !parseBoolean(services[i].getAttribute('tiled'), false);

			for(var p = 0; p < params.length; p++) {
				var param = params[p];
				paramHash[param.getAttribute('name')] = param.getAttribute('value');
			}

			if(type == 'wms') {
				layer = new OpenLayers.Layer.WMS(services[i].getAttribute('name'),
					url, paramHash, {singleTile : tiled, isBaseLayer: false});
			} else if(type == 'mapserver') {
				if(!url) {
					url = CONFIGURATION.mapserver_url;
				}

				var file = '';
				try {
					file = OpenLayers.Util.getXmlNodeValue(services[i].getElementsByTagName('file')[0]);
					if(file.substring(0,1) == '.') {
						file = CONFIGURATION.mapfile_root + file.substring(1);
					}
				} catch(e) {
					file = '';
				}
				paramHash['map'] = file;
				layer = new OpenLayers.Layer.MapServer(services[i].getAttribute('name'),
					url, paramHash, {singleTile : tiled, isBaseLayer: false});
			}


			if(layer) {
				if(layer.params['map_imagetype']) {
					delete layer.params['map_imagetype'];
				}
				layer.setVisibility(false);
				Map.addLayer(layer);
				if(parseBoolean(services[i].getAttribute('reference'))) {
					var ref_layer = layer.clone();
					//ReferenceMap.ovmap.addLayer(ref_layer);
					ref_layer.setVisibility(true);
				}
			}
		}

		/* Add the drawing tools */
		var drawingLayer = new OpenLayers.Layer.Vector('/drawinglayer');
		Map.addLayer(drawingLayer);

		Tools['point'] = new OpenLayers.Control.DrawFeature(drawingLayer, OpenLayers.Handler.Point);
		Tools['line'] = new OpenLayers.Control.DrawFeature(drawingLayer, OpenLayers.Handler.Path);
		Tools['polygon'] = new OpenLayers.Control.DrawFeature(drawingLayer, OpenLayers.Handler.Polygon);
		Tools['edit-polygon'] = new OpenLayers.Control.ModifyFeature(drawingLayer);
		var drawingtools = ['point','line','polygon','edit-polygon'];

		for(var d = 0; d < drawingtools.length; d++) {
			Map.addControl(Tools[drawingtools[d]]);
		}

		/* Add the layer for the popus */
		var popupsLayer = new OpenLayers.Layer.Markers('/popups');
		Map.addLayer(popupsLayer);

		configureUserInterface(response.responseXML);


		/* reload for the layers to update */
		Map.zoomToExtent(OpenLayers.Bounds.fromArray(CONFIGURATION.initial_extent));
	}

	document.getElementById('menubar').innerHTML = CONFIGURATION.links_bar_html;

	manageLayout();
	if(maxSelectedTool != null) {
		var name = maxSelectedTool.getAttribute('name');
		var tool = document.getElementById('tool-'+name);
		tool.onclick();
		tool = null;
	}


	var args = OpenLayers.Util.getParameters();
	if(args.call) {
		GeoMOOSE.startService(args.call, args, true);
	}
}

function changeExtentBySelect() {
	var options = this.getElementsByTagName('option');
	var selectedIndex = this.selectedIndex;

	/* reset to the "blank" option */
	for(var i = 0; i < options.length; i++) {
		options[i].selected = false;
	}
	options[0].selected = true;
	
	/* zoom to the extents */
	var arr = new String(options[selectedIndex].value).split(',');
	GeoMOOSE.zoomToExtent(arr[0],arr[1],arr[2],arr[3]);
}

function configureUserInterface(mapbook) {
	/* Configure The Zoom To's */
	var zoomToObject = CONFIGURATION.zoomto;
	var zoomToParent = document.getElementById('zoomto-boxes');
	for(var zoomToTitle in zoomToObject) {
		var zoomToContainer = document.createElement('div');
		zoomToParent.appendChild(zoomToContainer);
		zoomToContainer.className = 'zoomto-container';
		zoomToContainer.appendChild(document.createTextNode(zoomToTitle));

		var zoomToSelect = document.createElement('select');
		zoomToSelect.className = 'zoomto-select';
		zoomToContainer.appendChild(zoomToSelect);

		zoomToSelect.onchange = changeExtentBySelect;

		var blankOption = document.createElement('option');
		zoomToSelect.appendChild(blankOption);

		for(var extentTitle in zoomToObject[zoomToTitle]) {
			var option = document.createElement('option');
			option.value = zoomToObject[zoomToTitle][extentTitle].join(',');
			zoomToSelect.appendChild(option);
			option.appendChild(document.createTextNode(extentTitle));
		}
	}

	/* Tabs */
	Tabs = new GeomooseTabs('tab-controls');
	Tabs.initialize();

	/* Catalog */
	Catalog = new GeomooseCatalog(Tabs.getTab(CONFIGURATION.catalog_name), Map);
	Catalog.onLoadedMapbook(mapbook);

	/* Service Manager */
	Services = new ServiceManager(mapbook, Tabs.getTab(CONFIGURATION.show_service_settings_in));
	Services.initialize();

	Services.onStart = function (service) {
		if(parseBoolean(service.getAttribute('display'), true)) {
			Tabs.showTab(CONFIGURATION.show_service_settings_in);
		}
	}

	Services.onFinish = function () {
		document.getElementById(Tabs.getTab(CONFIGURATION.show_service_settings_in)).innerHTML = '';
	}

	Services.onServiceReturn = function (response) {
		clearPopups();
		var showResults = false;
		var contentType = new String(response.getResponseHeader('Content-type')).toLowerCase();
		if(contentType.indexOf('xml') > 0 && response.responseXML) {
			var xml = new OpenLayers.Format.XML();
			var scripts = response.responseXML.getElementsByTagName('script');
			var js = '';
			for(var s = 0; s < scripts.length; s++) {
				js += xml.concatChildValues(scripts[s]);
			}
			if(scripts.length > 0) { eval(js); }

			var texts = response.responseXML.getElementsByTagName('html');
			var html = '';
			for(var h = 0; h < texts.length; h++) {
				html += xml.concatChildValues(texts[h]);
			}
			document.getElementById('results-tab').innerHTML = html;
			if(html != '') {
				showResults = true;
			}


			var popups = response.responseXML.getElementsByTagName('popup');
			for(var p = 0; p < popups.length; p++) {
				var pp = popups[p];
				addPopup(parseFloat(pp.getAttribute('x')), parseFloat(pp.getAttribute('y')),
					parseFloat(pp.getAttribute('width')), parseFloat(pp.getAttribute('height')),
					xml.concatChildValues(pp));
			}
		} else if(response.responseText) {
			document.getElementById('results-tab').innerHTML = response.responseText;
			showResults = true;
		} else {
			alert('No Valid Response!');
		}
		if(showResults) {
			Tabs.showTab(CONFIGURATION.show_results_in);
		}
	}

}

var onSelectTool = new Array();
function selectTool(elm) {
	var p = document.getElementById('toolbar');
	var tools = p.getElementsByTagName('a');
	for(var i = 0; i < tools.length; i++) {
		if(tools[i].className.match(/Tool/)) {
			/* remove the tool specific selected styling */
			tools[i].className = tools[i].className.replace(/sprite\-control\-[A-Za-z]*\-selected/,'');
			/* remove the generic selected styling */
			tools[i].className = tools[i].className.replace(' selected', '');
		}
	}
	if(elm) {
		var toolName = elm.id.split('-')[1];
		toolName = 'sprite-control-'+toolName;

		var p = elm.parentNode;
		elm.className += ' '+toolName+'selected';
		elm.className += ' selected';
	}
	for(var i = 0; i < onSelectTool.length; i++) {
		onSelectTool[i]();
	}
	/* run 'em all and then get rid of 'em */
	onSelectTool = new Array();
}

function internalToolAction() {
	if(this.selectable) {
		selectTool(this);
		for(var t in Tools) {
			Tools[t].deactivate();
		}
	}
	var action = this.getAttribute('action');
	if(Tools[action]) {
		Tools[action].activate();
	}
}

function serviceToolAction() {
	if(this.selectable) {
		selectTool(this);
	}

	var serviceName = this.getAttribute('service');
	Services.startService(serviceName);
}

function addTool(parentId, toolXML) {
	var p = document.getElementById(parentId);
	var tool = document.createElement('a');
	var container = document.createElement('span');
	p.appendChild(tool);
	tool.className = 'Tool sprite-control sprite-control-'+toolXML.getAttribute('name');
	tool.id = 'tool-'+toolXML.getAttribute('name');
	var span = document.createElement('span');
	container.className = 'ToolContent';
	tool.appendChild(container);
	container.appendChild(span);
	span.className = 'ToolText';
	span.appendChild(document.createTextNode(toolXML.getAttribute('title')));

	tool.href = '#';
	tool.title = toolXML.getAttribute('title');
	tool.selectable = parseBoolean(toolXML.getAttribute('selectable'), true);

	var toolType = toolXML.getAttribute('type').toLowerCase();
	if(toolType == 'internal') {
		tool.setAttribute('action', toolXML.getAttribute('action'));
		tool.onclick = internalToolAction;
	} else if(toolType == 'service') {
		tool.setAttribute('service', toolXML.getAttribute('service'));
		tool.onclick = serviceToolAction;
	}

	container = null;
	span = null;
	tool = null;
	p = null;
}

function parseBoolean(bool, def) {
	if(!bool) { return def; }
	var boolString = new String(bool);
	if(boolString.match(/true/i)) { return true; }
	else if(boolString == '1') { return true; }
	else if(boolString.match(/on/i)) { return true; }
	return false;
}

function commifyNumber(number) {
	var numberOrig = new String(number);
	var numberArr = new String(number).split('.');
	var numberSign = number.substring(0,1);
	/* Oh, snap, it's negative */
	if(numberSign == '-') {
		number = new String(number).substring(1);
		numberArr = new String(number).split('.');
	} else {
		numberSign = '';
	}
	number = numberArr[0];
	number = '' + number;
	if (number.length > 3) {
		var mod = number.length % 3;
		var output = (mod > 0 ? (number.substring(0,mod)) : '');
		for (i=0 ; i < Math.floor(number.length / 3); i++) {
			if ((mod == 0) && (i == 0))
				output += number.substring(mod+ 3 * i, mod + 3 * i + 3);
			else
				output+= ',' + number.substring(mod + 3 * i, mod + 3 * i + 3);
		}
		} else {
		output=number;
	}
	if(numberOrig.match('.') && numberArr[1]) {
		output = output + '.' + numberArr[1];
	}
	return numberSign+output;
}

function stripCommas(v) {
	v = new String(v);
	while(v.match(/\,/)) {
		v = v.replace(',','');
	}
	return v;
}



function clearPopups() {
	while(Map.popups.length > 0) {
		Map.removePopup(Map.popups[0]);
	}
}

function addPopup(x,y,w,h,html) {
	var size = new OpenLayers.Size(w,h);
	var offset = new OpenLayers.Pixel(-1,-1);
	var icon = new OpenLayers.Icon('images/blank.gif', new OpenLayers.Size(1,1), offset);

	var modifiedHTML = '<div class="popupContainer" style="width: '+w+'px; height: '+h+'px">'+html+'</div>';
	var popup = new OpenLayers.Popup.FramedCloud('id0', new OpenLayers.LonLat(x,y),
			size, modifiedHTML, icon, true);

/*	popup.minSize = size.clone();*/
	popup.autoSize = false;

	Map.addPopup(popup);
}


/*
 * This generates a mapping request.
 * There exist so much data needed to request printing that I simply
 * put it in it's own script/function
 */

function getPrintRequest() {
	var layer_names = [], layer_urls = [], layer_opacity = [];
	for(var i in Map.layers) {
		var layer = Map.layers[i];
		if(layer.getVisibility()) {
			if(layer instanceof OpenLayers.Layer.WMS || layer instanceof OpenLayers.Layer.MapServer) {

				layer_names.push(layer.name);
				layer_urls.push(layer.getURL(OpenLayers.Bounds.fromArray([0,0,0,0])));

				var o = layer.opacity;
				if(o != null) { o = 1.0; }
				layer_opacity.push(o);
			}
		}
	}

	var x = {
		'layers' : layer_names.join(':'),
		'size' : '500,500',
		'extent' : Map.getExtent().toArray().join(',')
	};

	for(var i = 0; i < layer_names.length; i++) {
		x[layer_names[i]+'_url'] = layer_urls[i];
		x[layer_names[i]+'_opacity'] = layer_opacity[i];
	}

//	console.info(OpenLayers.Util.getParameterString(x));
//	window.open('php/print.php?' + OpenLayers.Util.getParameterString(x));
//	return '='+layer_names.join(':')+OpenLayers.Util.getParameterString(x);
	return x;
}
