
var ServiceRequest = {
	textParameter : function(obj, parent, nodeName, text) {
		var existing = parent.getElementsByTagName(nodeName);
		if(existing && existing.length == 1) {
			log_message("replacing data");
			existing[0].firstChild.data = text;
		} else {
			var node = obj._request.createElement(nodeName);
			var textElement = obj._request.createTextNode(text)
			node.appendChild(textElement);
			parent.appendChild(node);
		}
		return obj;
	},
	
	emptyNode: function(obj, parent, nodeName) {
		var existing = parent.getElementsByTagName(nodeName);
		if(existing && existing.length == 1) {
			// empty node already exists
			return existing[0];
		} else {
			var node = obj._request.createElement(nodeName);
			parent.appendChild(node);		
			return node;
		}		
	}
};

var ServiceResponse = {
	extractProperties: function(obj, node, csvProperties) {
		var properties = csvProperties.split(',');
		for(var i = 0; i < properties.length; i++) {
			var tagName = properties[i];
			var propName = tagName.substring(0, 1).toLowerCase() + tagName.substring(1, tagName.length);
			var elements = node.getElementsByTagName(tagName);
			if(elements.length != 1) { 
				log_message("Only use extractProperties when property appears once in node, found " + 
				tagName + " " + elements.length + " time(s)");
				alert(xml_docToString(node));
				throw new Exception("Only use extractProperties when property appears once in node, found " + 
				tagName + " " + elements.length + " time(s)");
			}
			var property = elements[0];
			if(property.firstChild) { obj[propName] = property.firstChild.data; }
		}
	}
};

ServiceRequest.Base = Class.create({
	initialize: function(options) {
		// These are just defaults, you can override them when you create instances of this class
		this.options = {
			host:       'localhost',
			port: 8080,
			profile:  'Trapeze6',
			service:     'InfoWeb',
			method:   '',
			onSuccess: function () { log_message('doing nothing'); },
			onFailure: function () { } 
		};
		
		Object.extend(this.options, options || { });	

		this._request = xml_createDocument();
		this._root = this._request.createElement(this.options.method);
		if(this._request.documentElement) {
			this._request.documentElement.appendChild(this._root);
		} else {
			this._request.appendChild(this._root);
		}
	},

	xml: function() {
		return xml_docToString(this._root);
	},

	call: function () {
		//alert(this.xml());
		var xmldoc = xml_loadDocument(startSoapEnv() + this.xml() + endSoapEnv()); 
		var that = this;
		new Ajax.Request('/' + this.options.profile + '/' + this.options.service + '', 
		{
			method:'post',
			postBody: xmldoc,
			contentType: 'text/xml',
			onSuccess: function (transport) { log_message("got ajax response"); that.options.onSuccess(that.responder(transport.responseXML)); } ,
			onFailure: this.options.onFailure
		});  
	}
});

ServiceRequest.GetLocationTypes = Class.create(ServiceRequest.Base, {
	initialize: function($super, options) {
		options.method = 'GetLocationTypes';
		options.service = 'Locations';
		this.responder = function(xmlResponse) { return new ServiceResponse.GetLocationTypes(xmlResponse); };
		$super(options);
		
	}
});

ServiceResponse.GetLocationTypes = Class.create({
	initialize: function(xmlResponse) {
		this._response = xmlResponse;
	}, 
	
	getLocationTypes: function() {
		var locationTypes = new Array();
		var elements = this._response.getElementsByTagName("LocationType");
		for(var i = 0; i < elements.length; i++) {
			var type = {};
			type.locType = elements[i].getElementsByTagName("LocType")[0].firstChild.data;
			type.locTypeDescr = elements[i].getElementsByTagName("LocTypeDescr")[0].firstChild.data;
			locationTypes[i] = type;
		}
		return locationTypes;
	}
});

ServiceRequest.GetLocationsByName = Class.create(ServiceRequest.Base, {
	initialize: function($super, options) {
		if(!options) {
			var options = {};
		}
		
		options.method = 'GetLocationsByName';
		options.service = 'Locations';
		this.responder = function(xmlResponse) { return new ServiceResponse.GetLocationsByType(xmlResponse); };
		$super(options);
	}, 
  
	byName: function(text) { ServiceRequest.textParameter(this, this._root, "LocationName", text.toUpperCase()); }
});

ServiceRequest.GetLocationsByType = Class.create(ServiceRequest.Base, {
	initialize: function($super, options) {
		options.method = 'GetLocationsByType';
		options.service = 'Locations';
		this.responder = function(xmlResponse) { return new ServiceResponse.GetLocationsByType(xmlResponse); };
		$super(options);
	}, 
  
	byType: function(text) { 
		var node1 = this._request.createElement("LocationTypes");
		var node2 = this._request.createElement("LocationType");
		var node3 = this._request.createElement("Type");
		var textElement = this._request.createTextNode(text)
		
		node3.appendChild(textElement); node2.appendChild(node3); node1.appendChild(node2); this._root.appendChild(node1);
		return this;
	}
});

ServiceResponse.GetLocationsByType = Class.create({
	initialize: function(xmlResponse) {
		this._response = xmlResponse;
	}, 
	
	getLocations: function() {
		var locations = new Array();
		var elements = this._response.getElementsByTagName("Location");
		for(var i = 0; i < elements.length; i++) {
			var location = {};
			ServiceResponse.extractProperties(location, elements[i], "LocId,LocName,StreetNo");
			locations[i] = location;
		}
		return locations;
	},
	
	formatAutocompleteSuggestions: function(suggestions) {
		log_message("got results, formatting now");
		var locations = this.getLocations();

		for(var i=0;i<locations.length;i++)	{
			var loc = locations[i];
			suggestions.push( { 'id': loc.locId, 'value': loc.locName, 'info': '' }  );
		}
	}
});

ServiceRequest.GetAddressMatches = Class.create(ServiceRequest.Base, {
	initialize: function($super, options) {
		if(!options) {
			var options = {};
		}
		
		options.method = 'Search';
		options.service = 'AddrMatchManager';
		this.responder = function(xmlResponse) { return new ServiceResponse.GetAddressMatches(xmlResponse); };
		$super(options);
	},
	
	byAddress: function(text) {				
		var soughtNode = ServiceRequest.emptyNode(this, this._root, "Sought");
		ServiceRequest.textParameter(this, soughtNode, "AddrAddress", text.toUpperCase());
		ServiceRequest.textParameter(this, soughtNode, "AddrHint", text.toUpperCase());
		ServiceRequest.emptyNode(this, soughtNode, "AddrType");
		ServiceRequest.emptyNode(this, soughtNode, "AddrName");
		ServiceRequest.emptyNode(this, soughtNode, "AddrPlace");
		ServiceRequest.emptyNode(this, soughtNode, "AddrCode");
		ServiceRequest.emptyNode(this, soughtNode, "MapPage");
		return this;
	}	
	//xmlRequest = '<Search><Sought><AddrType/><AddrHint/><AddrName/><AddrAddress>' + str + '</AddrAddress><AddrPlace/><AddrCode/><MapPage/></Sought></Search>'; 
});

ServiceResponse.GetAddressMatches = Class.create({
	initialize: function(xmlResponse) {
		this._response = xmlResponse;
	},
	
	getMatches: function() {
		log_message('getting matches');
		var matches = new Array();
		var element = this._response.getElementsByTagName("Found");
		var elements;
		var Geos;
		if(element.length > 0) {	
			elements = element[0].getElementsByTagName("Found");
			Geos = element[0].getElementsByTagName("Geocode");
		} else {
			log_message("unexpected input in getMatches()");
			return;
		}
		for(var i = 0; i < elements.length; i++) {
			var match = {};
			ServiceResponse.extractProperties(match, elements[i], "AddrType,AddrHint,SiteName,AddrAddress,AddrPlace,AddrCode,MapPage");
			match.lon = Geos[i].getElementsByTagName("Lon")[0].firstChild.data;
			match.lat = Geos[i].getElementsByTagName("Lat")[0].firstChild.data;
			matches[i] = match;
		}
		return matches;
	},
	
	// This method is expected by the autocomplete.js behavior
	formatAutocompleteSuggestions: function(suggestions) {
		log_message("got matches, formatting");
		var matches = this.getMatches();

		for(var i=0;i<matches.length;i++)	{
			var m = matches[i];
			suggestions.push( { 'id': i, 'value': m.addrHint, 'info': '' , 'lon': m.lon, 'lat': m.lat }  );
		}
	}
});

ServiceRequest.GetListOfLines = Class.create(ServiceRequest.Base, {
	initialize: function($super, options) {
		if(!options) {
			var options = {};
		}
	
		options.method = 'GetRoutes';
		options.service = 'InfoWeb';
		this.responder = function(xmlResponse) { return new ServiceResponse.GetListOfLines(xmlResponse); };
		$super(options);
		
	}, 
		  
	byNumerical: function(text) { 
		ServiceRequest.textParameter(this, this._root, "AllLineAbbrs", text.replace(/^\s+|\s+$/g, '').toUpperCase());
		//var soughtNode = ServiceRequest.emptyNode(this, this._root, "GetRoutesRequest");
		//ServiceRequest.textParameter(this, soughtNode, "AllRoutes", text);
		//return this;
		}

});

ServiceResponse.GetListOfLines = Class.create({
	initialize: function(xmlResponse) {
		this._response = xmlResponse;
	}, 
	
	getRoutes: function() {
		var routes = new Array();		
		var elements = this._response.getElementsByTagName("Route");
		for(var i = 0; i < elements.length; i++) {		
			var route = {};	
			route.lineAbbr = elements[i].getElementsByTagName("LineAbbr")[0].firstChild.data;
			route.lineName = elements[i].getElementsByTagName("LineName")[0].firstChild.data;					
			
			//** TL specific: Change any route beginning with '9' to an undefined array element 
			if( route.lineAbbr.substr(0,1) == "9" ) {			     
			    routes[i] = "undefined";			    
			}
			else {
			    routes[i] = route; 
			}
		}
		//** TL specific: remove undefined array elements
		for (var i=routes.length-1;i>=0;i--) {        
		    if (routes[i] == "undefined")  {            
		        routes.splice(i, 1);        
		    }           
                }	
		return routes;		
	},	
	
//	getRoutes: function() {
//		var routes = new Array();
//		var elements = this._response.getElementsByTagName("Route");
//		for(var i = 0; i < elements.length; i++) {		
//			var route = {};
//			route.lineName = elements[i].getElementsByTagName("LineName")[0].firstChild.data;
//			route.lineAbbr = elements[i].getElementsByTagName("LineAbbr")[0].firstChild.data;
//			routes[i] = route;
//		}
//		return routes;
//	},
	
	// This method is expected by the autocomplete.js behavior
	formatAutocompleteSuggestions: function(suggestions) {
		log_message("got Lines, formatting");
		var lines = this.getRoutes();

		for(var i=0;i<lines.length;i++)	{
			var m = lines[i];
			suggestions.push( { 'id': i, 'value': m.lineAbbr + ' - ' + m.lineName, 'info': '' }  );
		}
	}
});


function xml_docToString(xmldoc)
{
	//IE
	if(xmldoc.xml)
	{
		return xmldoc.xml;
	}
	else 
	{
		try
		{
			return (new XMLSerializer()).serializeToString(xmldoc);
		}
		catch(e)
		{
			alert(e.message());
		}
	}
}

function xml_createDocument()
{
	if(document.implementation && document.implementation.createDocument)
	{
		return document.implementation.createDocument("", "doc", null);
	}
	else
	{
		try
		{
			return new ActiveXObject("Microsoft.XMLDOM"); 
		}
		catch(e)
		{
			alert(e.message());
		}
	}
}

function xml_loadDocument(xmlstr)
{
	var xmlDoc;
	
	try
	{
		xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
		xmlDoc.async="false";
		xmlDoc.loadXML(xmlstr);
		return xmlDoc;
	}
	catch(e)
	{
		try //Firefox, Mozilla, Opera, etc.
		{
			var parser=new DOMParser();
			xmlDoc = parser.parseFromString(xmlstr,"text/xml");
			return xmlDoc;
		}
		catch(e) {alert(e.message)}
	}
}
	function initLocationsAutoComplete(form,Cntrl) {
				var getLocationsByName = new ServiceRequest.GetLocationsByName();
				
				var options = {
					host: form.Host.value,
					profile: form.Profile.value, 
					port: parseInt(form.Port.value),
					serviceRequestObject: getLocationsByName,
					serviceRequestSetter: 'byName',
					varname:'input',
					shownoresults:true,
					maxresults:10
				};
				
				new AutoComplete(Cntrl,options);
			}
			
			function initAddressAutoComplete(form,Cntrl) {
				var getMatchResults = new ServiceRequest.GetAddressMatches();
				
				var options = {
					host: form.Host.value,
					profile: form.Profile.value, 
					port: parseInt(form.Port.value),
					serviceRequestObject: getMatchResults,
					serviceRequestSetter: 'byAddress',
					varname:'input',
					shownoresults:true,
					maxresults:10,
					cache: false,
					atSelected: function (selectedValue) { 
						if(Cntrl == 'Start')
							form.StartGeo.value = ';' + selectedValue.lon + ';' + selectedValue.lat;
						else 
							form.EndGeo.value = ';' + selectedValue.lon + ';' + selectedValue.lat;
					}
				};
								
				new AutoComplete(Cntrl,options);
			}
			function initLinesAutoComplete(form,Cntrl) {
			
				var getLinesResults = new ServiceRequest.GetListOfLines();
				
				var options = {
					host: form.Host.value,
					profile: form.Profile.value, 
					port: parseInt(form.Port.value),
					serviceRequestObject: getLinesResults,
					serviceRequestSetter: 'byNumerical',
					varname:'input',
					shownoresults:true,
					maxresults:10,
					cache: false
				};
								
				new AutoComplete(Cntrl,options);
			}
			
function startSoapEnv() { return '<?xml version="1.0" encoding="UTF-8" standalone="no"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SOAP-ENV:Body>'; }

function endSoapEnv() { return '</SOAP-ENV:Body></SOAP-ENV:Envelope>'; }