/*
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.
*/


function ServiceManager(Catalog, ParentId) {
	var MySelf = this;
	
	function getService(serviceName) {
		var services = Catalog.getElementsByTagName('service');
		for(var i = 0; i < services.length; i++) {
			if(services[i].getAttribute('name') == serviceName) {
				return services[i];
			}
		}
		return null;
	}

	function renderInputStep(step, parentId, settingsObj) {
		var inputs = step.getElementsByTagName('input');
		var nUserInputs = 0;

		var headers = step.getElementsByTagName('header');
		var p = document.getElementById(parentId);

		for(var i = 0; i < headers.length; i++) {	
			var span = document.createElement('span');
			p.appendChild(span);
			span.className = 'service-header';
			span.innerHTML = combineTextNodes(headers[i]);
		}

		for(var i = 0; i < inputs.length; i++) {
			var inputType = new String(inputs[i].getAttribute('type')).toLowerCase();
			var inputName = new String(inputs[i].getAttribute('name'));
			if(InputTypes[inputType]) {
				if(settingsObj) {
					nUserInputs += InputTypes[inputType](parentId, inputs[i], settingsObj[inputName]);
				} else {
					nUserInputs += InputTypes[inputType](parentId, inputs[i], undefined);
				}
			}
		}

		var footnotes = step.getElementsByTagName('footnote');
		var p = document.getElementById(parentId);
		for(var i = 0; i < footnotes.length; i++) {	
			var span = document.createElement('span');
			p.appendChild(span);
			span.className = 'service-footnote';
			span.innerHTML = combineTextNodes(footnotes[i]);
		}
		return nUserInputs;
	}

	function onAddFeature(f) {
		/* Clear the layer */
		while(this.layer.features.length > 0) {
			this.layer.removeFeatures(this.layer.features[0]);
		}
		this.layer.addFeatures(f);

		/* Store the WKT of the feature */
		var parser = new OpenLayers.Format.WKT();
		var str = parser.write(f);
		this.xml.setAttribute('wkt', str);

		var OutputFormat = OpenLayers.Format.WKT;
		var outputType = new String(this.xml.getAttribute('format')).toLowerCase();
		if(outputType == 'kml') {
			OutputFormat = OpenLayers.Format.KML;
		} else if(outputType == 'wfs') {
			OutputFormat = OpenLayers.Format.WFS;
		} else if(outputType == 'json') {
			OutputFormat = OpenLayers.Format.JSON;
		} else if(outputType == 'delim') {
			OutputFormat = OpenLayers.Format.Text;
		}
		var output = new OutputFormat();

		var inputs = this.xml.getElementsByTagName('input');

		/* Remove the old input */
		var stepName = this.xml.getAttribute('name');
		for(var i = 0; i < inputs.length; i++) {
			if(inputs[i].getAttribute('name') == stepName) {
				this.xml.removeChild(inputs[i]);
			}
		}

		var input = Catalog.createElement('input');
		input.setAttribute('name', stepName);
		input.appendChild(Catalog.createCDATASection(output.write(f)));
		this.xml.appendChild(input);

		if(parseBoolean(this.xml.getAttribute('jump-start'), false)) {
			MySelf.callService(this.xml.parentNode.getAttribute('name'));
		}
	}

	function changeDrawingTool() {
		/* This code is needed because IE is stupid */
		if(this.type == 'radio') {
			var inputs = this.parentNode.getElementsByTagName('input');
			for(var i = 0; i < inputs.length; i++) {
				if(inputs[i].name == this.name) {
					inputs[i].checked = false;
				}
			}
			this.checked = true;
		}
		if(this.value) {
			for(var t in Tools) {
				Tools[t].deactivate();
			}
			if(Tools[this.value]) {
				Tools[this.value].activate();
				if(Tools[this.value].featureAdded) {
					Tools[this.value].featureAdded = onAddFeature;
				} else {
					Tools[this.value].onModificationEnd = onAddFeature;
				}
				Tools[this.value].xml = this.xml;
			}	
		}
	}

	function renderSpatialStep(step, parentId, settingsObj) {
		/* Restore preview drawing */
		var wkt = false;
		var wktFormat = false;
		if(settingsObj && settingsObj[step.getAttribute('name')]) {
			wkt = settingsObj[step.getAttribute('name')];
			wktFormat = true;
		} else if(step.getAttribute('wkt')) {
			wkt = step.getAttribute('wkt');
			if(!step.getAttribute('format') || step.getAttribute('format').toLowerCase() == 'wkt') {
				wktFormat = true;
			}
		}


		var parser = new OpenLayers.Format.WKT();
		if(wkt) {
			Map.getLayersByName('/drawinglayer')[0].addFeatures(parser.read(wkt));

			var inputs = step.getElementsByTagName('input');
			var input = false;
			for(var i = 0; i < inputs.length; i++) {
				if(inputs[i].getAttribute('name') == step.getAttribute('name')) {
					input = inputs[i];
				}
			}
			if(wktFormat) {
				if(input) {
					step.removeChild(input);
				}
				var input = Catalog.createElement('input');
				input.setAttribute('name', step.getAttribute('name'));
				input.appendChild(Catalog.createCDATASection(wkt));
				step.appendChild(input);
			}
		}
		var p = document.getElementById(parentId);
		p.appendChild(document.createTextNode('Available Tools: '));
		p.appendChild(document.createElement("br"));

		/* Maybe I should move this into configuration ... maybe ... */
		var tools = {
			'pan' : {
				'status' : true,
				'title': 'Navigate'
			}, 
			'edit-polygon' : {
				'status' : false,
				'title' : 'Edit Polygon'
			},
			'point' : {
				'status' : true,
				'title' : 'Draw Point'
			}, 
			'line' : {
				'status' : true,
				'title' : 'Draw Line'
			}, 
			'polygon' : {
				'status' : true,
				'title' : 'Draw Polygon'
			}
		};
		for(var v in tools) {
			if(parseBoolean(step.getAttribute(v), tools[v]['status'])) {
				var radio = document.createElement('input');
				radio.name = 'drawing-tools';
				radio.type = 'radio';
				radio.value = v;
				radio.onclick = changeDrawingTool;
				radio.xml = step;
				p.appendChild(radio);
				p.appendChild(document.createTextNode(tools[v].title));
				p.appendChild(document.createElement('br'));
			}
		}

		if(step.getAttribute('default')) {
			var def = step.getAttribute('default');
			var inputs = p.getElementsByTagName('input');
			for(var input = 0; input < inputs.length; input++) {
				if(inputs[input].name == 'drawing-tools' && inputs[input].value == def) {
					inputs[input].onclick();
				}
			}
		}

		p.appendChild(document.createElement('br'));
		/* return the number of inputs, since the spatial step is one,
			include it in the cound */
		return 1+renderInputStep(step, parentId, settingsObj);
	}

	this.onStart = function(service) {
		/* Override */
	}

	this.onFinish = function() {
		/* Override */
	}

	function changeStep() {
		var panel = document.getElementById(this.step);
		var panels = document.getElementsByTagName('div');
		for(var i = 0; i < panels.length; i++) {
			if(panels[i].className == 'service-step-visible') {
				panels[i].className = 'service-step-hidden';
			}
		}
		panel.className = 'service-step-visible';

		cleanupService();
	}

	function cleanupService() {
		for(var t in Tools) {
			Tools[t].deactivate();
		}

		var drawingLayer = Map.getLayersByName('/drawinglayer')[0];
		while(drawingLayer.features.length > 0) {
			drawingLayer.removeFeatures(drawingLayer.features[0]);
		}

	}

	function cancelService() {
		/* Clear out Service HTML I*/
		var p = document.getElementById(ParentId);	
		p.innerHTML = '';

		cleanupService();
		Tabs.showTab(CONFIGURATION.default_tab);
	}

	this.startService = function(serviceName, settingsObj, forceStart) {
		var service = getService(serviceName);
		if(service) {
			this.onStart(service);
			var p = document.getElementById(ParentId);
			p.innerHTML = '';

			var titleSpan = document.createElement('span');
			titleSpan.className = 'service-title';
			if(service.getAttribute('title')) {
				titleSpan.innerHTML = service.getAttribute('title');
				p.appendChild(titleSpan);
			}

			var steps = service.getElementsByTagName('step');
			/* No "steps" */

			var nInputs = 0;
			var submitParent = ParentId;
			if(steps.length == 0) {
				nInputs += renderInputStep(service, ParentId, settingsObj);
			} else {
				var rootName = 'service-';
				for(var i = 0; i < steps.length; i++) {
					var stepType = new String(steps[i].getAttribute('type')).toLowerCase();
					var div = document.createElement('div');
					if(i > 0) {
						div.className = 'service-step-hidden';
					} else {
						div.className = 'service-step-visible';
					}
					div.id = rootName + i;
					p.appendChild(div);

					if(stepType == 'input') {
						nInputs += renderInputStep(steps[i], div.id, settingsObj);
					} else if(stepType == 'select' || stepType == 'spatial') {
						nInputs += renderSpatialStep(steps[i], div.id, settingsObj);
					}
					var table = document.createElement('table');
					table.width = '95%';
					var tbody = document.createElement('tbody');
					div.appendChild(table);
					table.appendChild(tbody);

					var tr = document.createElement('tr');
					tbody.appendChild(tr);

					var left = document.createElement('td');
					left.align = 'left';
					tr.appendChild(left);

					var right = document.createElement('td');
					right.align = 'right';
					tr.appendChild(right);

					right.id = rootName + '-right-' + i;

					if(i > 0) {
						var prevButton = document.createElement('input');
						prevButton.value = 'Back';
						prevButton.type = 'button';
						left.appendChild(prevButton);
						prevButton.className = 'service-button-back';

						prevButton.step = rootName + (i-1);
						prevButton.onclick = changeStep;
					} else {
						var cancelButton = document.createElement('input');
						cancelButton.type = 'button';
						cancelButton.value = 'Cancel';
						left.appendChild(cancelButton);
						cancelButton.className = 'service-button-back';
						cancelButton.onclick = cancelService;
					}
					if(i < steps.length - 1) {
						var nextButton = document.createElement('button');
						nextButton.innerHTML = 'Next';
						right.appendChild(nextButton);
						nextButton.className = 'service-button-next';

						nextButton.step = rootName + (i+1);
						nextButton.onclick = changeStep;
					}
				}
				submitParent = rootName + '-right-' + (steps.length - 1);
			}
			var buttonTitle = service.getAttribute('submit');
			if(!buttonTitle) {
				buttonTitle = 'Go!';
			}

			var button = document.createElement('button');
			button.id = 'submit-service';
			button.setAttribute('service-name', serviceName);
			button.innerHTML = buttonTitle;
			button.onclick = function () {
				MySelf.callService(this.getAttribute('service-name'));	
			}
			var buttonParent = document.getElementById(submitParent);
			buttonParent.appendChild(button);
			if(nInputs <= 0 || forceStart) {
				button.onclick();
			}


		} else {
			alert('Service with the name '+serviceName+' cannot be found');
		}
	}

	this.callService = function(serviceName, values) {
		var service = getService(serviceName);
		var inputs = service.getElementsByTagName('input');
		var url = combineTextNodes(service.getElementsByTagName('url')[0]);
		var params = '?';
		for(var i = 0; i < inputs.length; i++) {
			var name = inputs[i].getAttribute('name');
			var v = '';
			if(inputs[i].getAttribute('value')) {
				v = inputs[i].getAttribute('value');
			} else {
				var testV = combineTextNodes(inputs[i]);
				if(testV != undefined) {
					v = escape(testV);
				}
			}
			if(values && values[name]) {
				v = values[name];
			}
			params += '&' + name + '=' + v;
		}

		/** Clear out the old spatial stuff **/
		var drawingTools = ['point','line','polygon'];
		for(var d = 0; d < drawingTools.length; d++) {
	//		Tools[drawingTools[d]].deactivate();
		}

		var drawingLayer = Map.getLayersByName('/drawinglayer')[0];
		while(drawingLayer.features.length > 0) {
			drawingLayer.removeFeatures(drawingLayer.features[0]);
		}
		
		OpenLayers.loadURL(url, params, MySelf, MySelf.onServiceReturn);	
		MySelf.onFinish();
	}

	this.onServiceReturn = function (response) {
		/* Override */
		if(response.responseText) {
			document.getElementById('results-tab').innerHTML = response.responseText;
		}
	}

	this.initialize = function() {
		if(CONFIGURATION.startup_service) {
			this.startService(CONFIGURATION.startup_service);
		}
	}

	var InputTypes = new Array();

	function combineTextNodes(node) {
/*		var s = new String();
		for(var i = 0; i < node.childNodes.length; i++) {
			if(node.childNodes[i].nodeName == '#text'
				|| node.childNodes[i].nodeName == '#cdata-section') {
				s += node.childNodes[i].nodeValue;
			}
		}*/
		return OpenLayers.Util.getXmlNodeValue(node);
	}

	function clearChildren(node) {
		while(node.childNodes.length > 0) {
			node.removeChild(node.childNodes[0]);
		}
	}

	function updateServiceSetting() {
		var node = this.xml;
		var currentValue = combineTextNodes(node);

		clearChildren(node);
		node.appendChild(Catalog.createCDATASection(this.value));
	}

	function keyCallService(event) {
		if(window.event) { event = window.event; }
		var keyCode = null;
		if(event.which) {
			keyCode = event.which;
		} else if(event.keyCode) {
			keyCode = event.keyCode;
		}
		if(keyCode == 13) {
			this.onchange();
			document.getElementById('submit-service').onclick();
		}
	}

	InputTypes['user'] = function(inputParent, input, value) {
		var p = document.getElementById(inputParent);
		var inputParent = document.createElement('span');
		inputParent.className = 'service-input-parent';
		p.appendChild(inputParent);

		var i = document.createElement('input');
		i.className = 'service-input';
		i.xml = input;
		i.id = 'service-input-'+input.getAttribute('name');
		
		var xmlValue = combineTextNodes(input);
		if(xmlValue != undefined) {
			i.value = xmlValue;
		}
		if(value != undefined) {
			i.value = value;
		/*	input.setAttribute('value', value);*/
			clearChildren(input);
			input.appendChild(Catalog.createCDATASection(value));
		}
		i.onchange = updateServiceSetting;
		i.onkeypress = keyCallService;

		var title = document.createElement('span');
		title.className = 'service-input-title';
		inputParent.appendChild(title);
		title.innerHTML = input.getAttribute('title');
		inputParent.appendChild(i);
		return 1;	/* 1 = requires user input */

	}

	InputTypes['hidden'] = function(inputParent, input, value) {
		if(value != undefined) {
			clearChildren(input);
			input.appendChild(Catalog.createCDATASection(value));
		}
		return 0;
	}

	InputTypes['visiblelayers'] = function(inputParent, input, value) {
		var services = Catalog.getElementsByTagName('map-source');
		var layersList = new Array();
		for(var i = 0; i < services.length; i++) {
			var root = services[i].getAttribute('name');
			var layers = services[i].getElementsByTagName('layer');
			for(var l = 0; l < layers.length; l++) {
				if(parseBoolean(layers[l].getAttribute('visible'))) {
					var n = layers[l].getAttribute('name');
					layersList.push(root+'/'+n);
				}
			}
		}
		var delim = ':';
		if(input.getAttribute('delim')) {
			delim = input.getAttribute('delim');
		}
		clearChildren(input);
		input.appendChild(Catalog.createCDATASection(layersList.join(delim)));
		return 0; /* 0 = no user input required */
	}

	InputTypes['print_info'] = function(inputParent, input, value) {
		//input.appendChild(Catalog.createCDATASection(getPrintRequest()));
		var info = getPrintRequest(); /* returns an object with all the inputs needed for a print */
		var inputs = input.parentNode.getElementsByTagName('input');
		var p = input.parentNode;
		
		for(var i = inputs.length -1; i >= 0; i--) {
			if(inputs[i] != input) {
				p.removeChild(inputs[i]);
			}
		}

		for(var i in info) {
			var ipt = Catalog.createElement('input');
			p.appendChild(ipt);
			ipt.setAttribute('name', i);
			ipt.setAttribute('type', 'hidden');
			ipt.appendChild(Catalog.createCDATASection(info[i]));
			//console.log(i, info[i]);
		}
		return 0;
	}

	InputTypes['select'] = function(inputParent, input, value) {
		var p = document.getElementById(inputParent);
		var inputParent = document.createElement('span');
		inputParent.className = 'service-input-parent';
		p.appendChild(inputParent);

		var i = document.createElement('select');
		i.xml = input;

		var title = document.createElement('span');
		title.className = 'service-input-title';
		inputParent.appendChild(title);
		title.innerHTML = input.getAttribute('title');
		inputParent.appendChild(i);

		var opts = input.getElementsByTagName('option');
		var selIndex = 0;
		var selectValue =  input.getAttribute('value');

		if(value != undefined) { selectValue = value; }
		for(var o = 0; o < opts.length; o++) {
			var opt = document.createElement('option');
			opt.value = opts[o].getAttribute('value');
			i.appendChild(opt);
			if(opt.value == selectValue) {
				opt.selected = true;
				selIndex = 0;
			}
			var labelValue = combineTextNodes(opts[o]);
			if(labelValue) {
				opt.appendChild(document.createTextNode(labelValue));
			}
		}


		i.onchange = function() {
			var opts = this.getElementsByTagName('option');
		/*	clearChildren(this.xml);
			this.xml.appendChild(Catalog.createCDATASection(opts[this.selectedIndex].value));*/
			this.xml.setAttribute('value', opts[this.selectedIndex].value);
		}

		i.onchange();

		return 1;
	}

}
