/**
 *	Copyright 2002-2006 by Earth Resource Mapping Ltd.  All rights reserved.
 *	Image Integration Framework generated pages may only be served by organizations that have
 *	a license for the Image Integration Framework product. Image Integration Framework may only be used by
 *	Application Service Providers (ASPs) with a license for the Image Integration Framework ASP Edition product.
 *
 *  @fileoverview Contains the SIX.Access.WMS access class
 */

SIX.WMSServiceInfoCache = new SIX.ServiceInfoCache();
/**
* @class Interfaces to OGC style Web Map Server (WMS) servers that use the WMS style interface.
* @constructor
* @base SIX.Access
*/
SIX.Access.WMS = function (service) 
{
	SIX.Access.call(this, service);
	service.sSRS = "";
	this.name = "Web Map Server";
	this.version = "1.1.1";
	this.hasMetadata = false;
	this.hasLegend = false;
	this.hasSearch = false;		// may be set later
	this.hasQuery = false;
	this.canReorder = false;
	this.imageFormat = "image/png";
	this.xml = new SIX.Xml();	
}
SIX.Util.extend(SIX.Access.WMS, SIX.Access);

/**
*	purpose: is the wms service an image web server
*/

SIX.Access.WMS.prototype.isImageWebServer = function (sHost)
{	
	var bImageWebServer = false;
	
	if (sHost.indexOf('ecw_wms.dll') > 0)
		{
		bImageWebServer = true; 
		}
	
	return bImageWebServer;
	
}

SIX.Access.WMS.prototype.getSelectedChildLayers = function (map, layerParent)
{
	var aLayerList = new Array();
	var aLayers = (layerParent.constructor == SIX.Service ? layerParent.layers : layerParent.aLayers);
	
	for (var i=0; i<aLayers.length; i++)
	{
		var layer = aLayers[i];

		if (!layer.supportsCoordSys(map.coordSys))
			continue;
				
		//Set layer coordinate system to map coordinate system.
		layer.coordSys = map.coordSys.clone(layer.coordSys);

		if (layer.aLayers.length)
		{
			var aChildLayerList = this.getSelectedChildLayers(map, layer);
		
			var bAllChildLayersSelected = false;
			if (aChildLayerList.length == layer.aLayers.length)
			{
				bAllChildLayersSelected = true;
				for (var j=0; j<layer.aLayers.length; j++)
				{
					if (layer.aLayers[j] != aChildLayerList[j])
					{
						bAllChildLayersSelected = false;
						break;
					}
				}
			}

			if (bAllChildLayersSelected && layer.id != "" && layer.bSelected && layer.bInScale)
				aLayerList[aLayerList.length] = layer;
			else
				aLayerList = aLayerList.concat(aChildLayerList);
		}
		else if (layer.id != "" && layer.bSelected && layer.bInScale)
		{
			aLayerList[aLayerList.length] = layer;
		}

	}
	return aLayerList;
}

SIX.Access.WMS.prototype.loadMap = function (map)
{		
	this.service.nMapLoadState = SIX.eMapLoadState.NOT_LOADED;
	if( !this.service.loaded || !map.loaded || !this.service.active || this.service.bFeatureServer || (this.service.nLayersLoadState != SIX.eLayersLoadState.LOADED))
	{
		return;
	}

	this.service.busy(true);
	var control = document[map.uid];

	var dLayerTLX = map.tlx;
	var dLayerTLY = map.tly;	
	var dLayerBRX = map.brx;
	var dLayerBRY = map.bry;
		
	var dLayerTLLat = map.getTopLeftLatitude();
	var dLayerTLLon = map.getTopLeftLongitude();
	var dLayerBRLat = map.getBottomRightLatitude();	
	var dLayerBRLon = map.getBottomRightLongitude();

	var nRequestWidth = map.width > map.width1K ? map.width1K : map.width;
	var nRequestHeight = map.height > map.height1K ? map.height1K : map.height;

	var bGotLayers = false;
	
	var sMapDatum = map.datum();
	var sMapProjection = map.projection();
	var nEPSG = GDTEPSGMappings.getEPSGCode(sMapDatum, sMapProjection);
	if (!nEPSG || isNaN(nEPSG) || !this.service.supportsEPSG(nEPSG))
	 	return;
	var sSRS = "EPSG:" + nEPSG;
	
	var aLayerList = this.getSelectedChildLayers(map, this.service);
	var layerList = "";
	for (var i=0; i<aLayerList.length; i++)
		layerList += (i > 0 ? "," : "") + aLayerList[i].id;
	
	// turn off the layer in the control if no layers are selected.
	// might make more generic later on
	if (layerList == "")
	{
		map.setLayerParameter(this.service.uid,"visibility=false");
		this.service.busy(false);
		return;
	}
	else
	{
		map.setLayerParameter(this.service.uid,"visibility=true");
	}

	var dBBoxMinX = dLayerTLLon;
	var dBBoxMinY = dLayerBRLat;	
	var dBBoxMaxX = dLayerBRLon;
	var dBBoxMaxY = dLayerTLLat;
	
	var dRequestTLX = dLayerTLX;
	var dRequestTLY = dLayerTLY;	
	var dRequestBRX = dLayerBRX;
	var dRequestBRY = dLayerBRY;			
			
	var objDatumProj = GDTEPSGMappings.getDatumProjection(nEPSG);
	if (objDatumProj)
	{
		sDatum = objDatumProj.sDatum;
		sProjection = objDatumProj.sProjection;
		var dRequestTLLat = dLayerTLLat;
		var dRequestTLLon = dLayerTLLon;
		var dRequestBRLat = dLayerBRLat;	
		var dRequestBRLon = dLayerBRLon;
		
		if (((( dRequestTLLon <= map.getTopLeftLongitude()) && (dRequestBRLon >= map.getTopLeftLongitude())) || 
		((dRequestTLLon >= map.getTopLeftLongitude()) && (dRequestTLLon <= map.getBottomRightLongitude()))) && 
		(((dRequestTLLat >= map.getTopLeftLatitude()) && (dRequestBRLat <= map.getTopLeftLatitude())) || 
		((dRequestTLLat <= map.getTopLeftLatitude()) && (dRequestTLLat >= map.getBottomRightLatitude()))))
		{
			//SIX.Layer is within view.
			if (dRequestTLLat > map.getTopLeftLatitude())
				dRequestTLLat = map.getTopLeftLatitude();
			if (dRequestTLLon < map.getTopLeftLongitude())
				dRequestTLLon = map.getTopLeftLongitude();	
			if (dRequestBRLat < map.getBottomRightLatitude())
				dRequestBRLat = map.getBottomRightLatitude();
			if (dRequestBRLon > map.getBottomRightLongitude())
				dRequestBRLon = map.getBottomRightLongitude();		
		}							
		
		
		if (sMapProjection.toLowerCase() == "geodetic")
		{
				dRequestTLX = dBBoxMinX = dRequestTLLon;
				dRequestTLY = dBBoxMaxY = dRequestTLLat;	
				dRequestBRX = dBBoxMaxX = dRequestBRLon;
				dRequestBRY = dBBoxMinY = dRequestBRLat;
		}
		else
		{	
			var aTopLeft = map.getENPoint(dRequestTLLon, dRequestTLLat, sDatum, sProjection);
			var aBottomRight = map.getENPoint(dRequestBRLon, dRequestBRLat, sDatum, sProjection);
			if (aTopLeft.length && aBottomRight.length)
			{
				dRequestTLX = dBBoxMinX = aTopLeft[0];
				dRequestTLY = dBBoxMaxY = aTopLeft[1];	
				dRequestBRX = dBBoxMaxX = aBottomRight[0];
				dRequestBRY = dBBoxMinY = aBottomRight[1];	
			}			
		}
	}
	
	/// WMS swap tlLatitude and brLatitude order
	var	 url =	'mode=0;action=GET;'
		+ 'worldTLX=' + dRequestTLX + ';worldTLY=' + dRequestTLY + ';worldBRX=' + dRequestBRX + ';worldBRY=' + dRequestBRY + ';'
		+ 'url=http://' + this.service.host + this.service.service
		+ '&VERSION=' + this.version + '&REQUEST=GetMap&STYLES='
		+ '&FORMAT=' + this.imageFormat + '&WIDTH=' + nRequestWidth + '&HEIGHT=' + nRequestHeight
		+ '&SRS=' + sSRS
		+ (this.service.sBackgroundColor ? '&BGCOLOR=0x' + SIX.Util.hexNoPrefix(this.service.sBackgroundColor) : '')		
		+ '&BBOX=' + dBBoxMinX + ',' + dBBoxMinY + ',' + dBBoxMaxX + ',' + dBBoxMaxY
		+ '&LAYERS=' + layerList
		+ ';body=;';
	
	if (map.setLayerParameter(this.service.uid, url))
		this.service.nMapLoadState = SIX.eMapLoadState.LOADING;
}

SIX.Access.WMS.prototype.loadLayers = function (map)
{
	if( !this.service.loaded || !map.loaded || !this.service.isOnline || this.service.bFeatureServer)
	{
		this.service.nLayersLoadState = SIX.eLayersLoadState.NOT_LOADED;
		return;
	}
	
	this.service.busy(true);
	var control = document[map.uid];

	var sUrl = 'http://' + this.service.host + this.service.service + '&VERSION=' + this.version + '&REQUEST=GetCapabilities&SERVICE=WMS';
	var sAction = 'GET';
	var	 sParams  = 'mode=0;action=' + sAction + ';';
	sParams	+='worldTLX=' + map.tlx + ';worldTLY=' + map.tly + ';worldBRX=' + map.brx + ';worldBRY=' + map.bry + ';'
		+ 'url=' + sUrl
		+ ';body=;';

	SIX.WMSServiceInfoCache.getServiceInfo(map, this.service, sParams, sUrl, "", sAction);
}

SIX.Access.WMS.prototype.queryLayers = function (map) {
	if (!this.service.isOnline || (this.service.sSRS == ""))
		return;		// WMT spec requires a GetImage call before queries
	var layerList = "";
	if( this.service.layers.length ) {
		layerList = '';
		var first = true;
		var count = 3;
		for( var i=0; i<this.service.layers.length; i++ ) {
			var layer = this.service.layers[this.service.layersOrder[i]];
			if( layer.doQuery ) {
				if( first )
					first = false;
				else
					layerList += ",";
				layerList += layer.id;
				count -= 1;
				if( !count )
					break;	// stop if exceeding GET length
			}
		}
		if( first )
			return;
	}
	var control = document[map.uid];
	var pixelX = map.getTopLeftLongitude() - map.getBottomRightLongitude();
	var pixelY = map.getTopLeftLatitude()  - map.getBottomRightLatitude();
	if( !pixelX || !pixelY )
		return;
	pixelX = parseInt((map.getTopLeftLongitude() - map.getMouseLongitude()) / pixelX * map.width1K);
	pixelY = parseInt((map.getTopLeftLatitude() - map.getMouseLatitude())  / pixelY * map.height1K);
	if( pixelX < 0 || pixelX > map.width1K || pixelY < 0 || pixelY > map.height1K )
		return;

	var	 url =	'mode=0;action=GET;'
		+ 'worldTLX=' + map.tlx + ';worldTLY=' + map.tly + ';worldBRX=' + map.brx + ';worldBRY=' + map.bry + ';'
		+ 'url=http://' + this.service.host + this.service.service
		+ '&VERSION=' + this.version + '&REQUEST=GetFeatureInfo'
		+ '&FORMAT=' + this.imageFormat
		+ '&WIDTH=' + map.width1K + '&HEIGHT=' + map.height1K
		+ '&SRS=' + this.service.sSRS
		+ '&SERVICE=WMS'
		+ '&BBOX=' + map.getTopLeftLongitude() + ',' + map.getTopLeftLatitude() + ',' + map.getBottomRightLongitude() + ',' + map.getBottomRightLatitude()
		+ '&QUERY_LAYERS=' + layerList + '&LAYERS=' + layerList
		+ "&X=" + pixelX + "&Y=" + pixelY + '&INFO_FORMAT='
		+ ';body=;';

	this.service.busy(true);
	var result = map.setLayerParameter(this.service.uid, url);
}

SIX.Access.WMS.prototype.responseMap = function (map, layerName, url, body, action, tlx, tly, brx, bry, response)
{
	var retVal = true;
	if( response == "NCS_OVERLAY_SUCCESS" ) {
		this.service.busy(false);
		return true;
	}
	
	// image webservers can only display one layer
	var bImageVisible = false;

	this.xml.loadXmlText(response);
	
	var node = this.xml.rootXml.getElementsByTagName("WMT_MS_Capabilities")[0];
	if (node)
		node = node.getElementsByTagName("Capability")[0];

	var layerInfo = 0;
	var featureInfo = 0;
	var mapInfo = 0;
	if (node) 
	{
		SIX.WMSServiceInfoCache.fireResponseMapCallbacks(url, response);
		layerInfo = node.getElementsByTagName("Layer")[0];
		featureInfo = node.getElementsByTagName("GetFeatureInfo")[0];
		mapInfo = node.getElementsByTagName("GetMap")[0];
	}
	if( featureInfo )
		this.hasQuery = true;
	if( mapInfo ) {
		var formatNodes = mapInfo.getElementsByTagName("Format");
		var formats = new Object();
		for( var i=0; i<formatNodes.length; i++ ) {
			if( formatNodes[i].childNodes.length < 1 )
				continue;
			var item = formatNodes[i].childNodes[0];
			formats[item.nodeValue.toLowerCase()] = true;
		}
		// try for preferred order for image request.
		// Some WMS servers lie about supporting GIF when they don't,
		// so try for PNG first
		var preferred = ["image/png", "image/gif", "image/jpg"];
		for (var f=0; f<preferred.length; f++) 
		{
			if( formats[preferred[f]] ) {
				this.imageFormat = preferred[f];
				break;
			}
		}
	}
		
	if( layerInfo ) 
	{
		this.service.deleteLayers(map);
		this.createLayers(map, layerInfo, this.service);

		this.service.nLayersLoadState = SIX.eLayersLoadState.LOADED;

		map.hiddenLayers(this.service);
		this.service.update();
		this.service.busy(false);		
		
		if (this.service.parentService)
		{
			this.service.parentService.nLayersLoadState = SIX.eLayersLoadState.LOADED;
			
			for (var i=0; i<this.service.parentService.aServices.length; i++)
			{ 
				if (this.service.parentService.aServices[i].nLayersLoadState != SIX.eLayersLoadState.LOADED)
				{
						this.service.parentService.nLayersLoadState = SIX.eLayersLoadState.LOADING;
						break;
				}
			}
			if (this.service.parentService.nLayersLoadState == SIX.eLayersLoadState.LOADED)
			{				
				map.hiddenLayers(this.service.parentService);
				this.service.parentService.update();
				this.service.parentService.busy(false);							
			}
		}
		this.updateMap(map);

		return true;
	}

	this.service.busy(false);
	//this.service.online(false);	
	return retVal;
}

SIX.Access.WMS.prototype.getScaleFromScaleHint = function (dScaleHint)
{
	var dScale = 0.0;
	if (dScaleHint > 0.0)
	{
		var dMetresPerPixel = Math.pow((Math.pow(dScaleHint, 2) / 2), 0.5);//Assumes square cells.

		var nDPI = SIX.Util.getScreenXDPI();
		var dScreenMetresPerPixel = (0.0254 / nDPI);
	
		dScale = dMetresPerPixel / dScreenMetresPerPixel;
	}
	return dScale;
}

SIX.Access.WMS.prototype.createLayers = function (map, nodeLayer, parent, inheritedProperties)
{	
	var sMapDatum = map.datum();
	var sMapProjection = map.projection();
	var nMapEPSG = GDTEPSGMappings.getEPSGCode(sMapDatum, sMapProjection);
	
	var sDatum = sMapDatum;
	var sProjection = sMapProjection;
	
	var bRootLayer = false;
	var aChildLayerNodes = new Array();	
	if (arguments.length < 4)
	{
		bRootLayer = true;
		var inheritedProperties = new Object();
		inheritedProperties.dLatLonBoundingBoxMinX = 0.0;
		inheritedProperties.dLatLonBoundingBoxMinY = 0.0;
		inheritedProperties.dLatLonBoundingBoxMaxX = 0.0;
		inheritedProperties.dLatLonBoundingBoxMaxY = 0.0;
		inheritedProperties.aEPSGCodes = new Object();//Associative array.  
		inheritedProperties.dScaleMin = 0.0;
		inheritedProperties.dScaleMax = 0.0;
	}

	var sName = "";
	var sTitle = "";
	
	for (var i=0; i<nodeLayer.childNodes.length; i++) 
	{
		var item = nodeLayer.childNodes[i];
		if (item.nodeType != Node.ELEMENT_NODE)
			continue;
		var sNodeName = item.nodeName.toLowerCase();
		
		switch(sNodeName)
		{
			case "name":
				if (item.childNodes.length != 1) continue;
				var value = item.childNodes[0];
				if (value.nodeType != Node.TEXT_NODE) continue;			
				sName = value.nodeValue;
			break;
			case "title":	
				if (item.childNodes.length != 1) continue;
				var value = item.childNodes[0];
				if (value.nodeType != Node.TEXT_NODE) continue;		
				sTitle =  value.nodeValue;
				break;
			case "layer":
				aChildLayerNodes[aChildLayerNodes.length] = item;
				break;
			case "latlonboundingbox":
				var sAttrMinX = item.getAttribute("minx");
				var sAttrMinY = item.getAttribute("miny");	
				var sAttrMaxX = item.getAttribute("maxx");	
				var sAttrMaxY = item.getAttribute("maxy");

				if (sAttrMinX)
					inheritedProperties.dLatLonBoundingBoxMinX = parseFloat(sAttrMinX);
				if (sAttrMinY)
					inheritedProperties.dLatLonBoundingBoxMinY = parseFloat(sAttrMinY);
				if (sAttrMaxX)
					inheritedProperties.dLatLonBoundingBoxMaxX = parseFloat(sAttrMaxX);
				if (sAttrMaxY)
					inheritedProperties.dLatLonBoundingBoxMaxY = parseFloat(sAttrMaxY);				
				break;
			case "boundingbox":
				var sSRS = item.getAttribute("SRS");
				var sAttrMinX = item.getAttribute("minx");
				var sAttrMinY = item.getAttribute("miny");	
				var sAttrMaxX = item.getAttribute("maxx");	
				var sAttrMaxY = item.getAttribute("maxy");		
				if (sAttrMinX && sAttrMinY && sAttrMaxX && sAttrMaxY && sSRS && (sSRS.indexOf("EPSG:") > -1))
				{
					var sEPSG = sSRS.substring(5, sSRS.length);
					nEPSG = parseInt(sEPSG);
					if (!isNaN(nEPSG))
					{
						if (!inheritedProperties.aEPSGCodes["#" + nEPSG])
							inheritedProperties.aEPSGCodes["#" + nEPSG] = new Object();
							
						var objSRSBoundingBox = inheritedProperties.aEPSGCodes["#" + nEPSG];
												
						objSRSBoundingBox.dBoundingBoxMinX = parseFloat(sAttrMinX);
						objSRSBoundingBox.dBoundingBoxMinY = parseFloat(sAttrMinY);
						objSRSBoundingBox.dBoundingBoxMaxX = parseFloat(sAttrMaxX);
						objSRSBoundingBox.dBoundingBoxMaxY = parseFloat(sAttrMaxY);			
					}
				}
				break;
			case "srs": 
				if (item.childNodes.length != 1) continue;
				var value = item.childNodes[0];
				if (value.nodeType != Node.TEXT_NODE) continue;			
				var sNodeValue = value.nodeValue;	
				var aSRS = sNodeValue.split(" ");
				for (var j=0; j<aSRS.length; j++)
				{
					var sSRS = aSRS[j];
					if (sSRS && (sSRS.indexOf("EPSG:") > -1))
					{
						//this.service.sSRS must be set for GetFeatureInfo queries. 
						//Set service.sSRS to "EPSG:4326" (WGS84/GEODETIC) if the root layer 
						//supports it, otherwise use the first SRS defined for the root layer.
						if (bRootLayer && (sSRS == "EPSG:4326") || (this.service.sSRS == ""))
							this.service.sSRS = sSRS;
							
						var sEPSG = sSRS.substring(5, sSRS.length);
						nEPSG = parseInt(sEPSG);

						if (!inheritedProperties.aEPSGCodes["#" + nEPSG])
							inheritedProperties.aEPSGCodes["#" + nEPSG] = new Object();			
					}
				}
				
			break;
			case "scalehint":
				var sAttrMin = item.getAttribute("min");	
				var sAttrMax = item.getAttribute("max");		
				if (sAttrMin && sAttrMax)
				{
					var dScaleHintMin = parseFloat(sAttrMin);
					var dScaleHintMax = parseFloat(sAttrMax);
					if (!isNaN(dScaleHintMin) && !isNaN(dScaleHintMax))
					{
						inheritedProperties.dScaleMin = this.getScaleFromScaleHint(dScaleHintMin); 
						inheritedProperties.dScaleMax = this.getScaleFromScaleHint(dScaleHintMax);
					}
				}
				break;
			default:
				break;
		}
	}

	var visible = (this.service.layers.length <= 2 ? true : false);
		
	var query = false;
	if (this.hasQuery) 
	{
		var sQueryable = nodeLayer.getAttribute("queryable");
		if (sQueryable)
			query = ((sQueryable == "1") ? true : false);
		else
			query = true;// some servers incorrectly mark false yet can query	
	}
	
	var objLayerDefaults = this.service.filterLayer(sName, sTitle);
	if (objLayerDefaults.bInList)
	{
		var sParams = "selected=" + (objLayerDefaults.bSelected ? "true" : "false") + 
					";doquery="  + (query ? "true" : "false") + 
					";canquery="  + (query ? "true" : "false"); 
		layer = parent.addLayer(new SIX.Layer(parent,sTitle,sName,null,sParams));	
		parent = layer;

		if (inheritedProperties.dScaleMin != inheritedProperties.dScaleMax)
			layer.setScale(inheritedProperties.dScaleMin, inheritedProperties.dScaleMax);
		for (var i in inheritedProperties.aEPSGCodes)
		{
			if (inheritedProperties.aEPSGCodes.hasOwnProperty(i))
			{
				var objSRSBoundingBox = inheritedProperties.aEPSGCodes[i];
				var sEPSG = i.substring(1, i.length);
				var nEPSG = parseInt(sEPSG);
				var objDatumProj = GDTEPSGMappings.getDatumProjection(nEPSG);
				if (objDatumProj)
				{
					layer.setSupportedCoordSys(objDatumProj.sDatum, objDatumProj.sProjection);	
					if (nEPSG == nMapEPSG)
					{
						if (objDatumProj.sDatum && objDatumProj.sProjection)
						{
							layer.setCoordSys(objDatumProj.sDatum, objDatumProj.sProjection);
							
							if (objSRSBoundingBox.dBoundingBoxMinX && objSRSBoundingBox.dBoundingBoxMinY && 
								objSRSBoundingBox.dBoundingBoxMaxX && objSRSBoundingBox.dBoundingBoxMaxY)
							{
								if (objDatumProj.sProjection == "GEODETIC")
								{
									layer.setExtents(objSRSBoundingBox.dBoundingBoxMaxY, objSRSBoundingBox.dBoundingBoxMinX,
													objSRSBoundingBox.dBoundingBoxMinY,objSRSBoundingBox.dBoundingBoxMaxX);
								}
								else
								{
									var aTopLeft = map.getGeoPoint(objSRSBoundingBox.dBoundingBoxMinX, objSRSBoundingBox.dBoundingBoxMaxY, sDatum, sProjection);
									var aBottomRight = map.getGeoPoint(objSRSBoundingBox.dBoundingBoxMaxX, objSRSBoundingBox.dBoundingBoxMinY, sDatum, sProjection);
									if (aTopLeft.length && aBottomRight.length)
										layer.setExtents(aTopLeft[1],aTopLeft[0],aBottomRight[1],aBottomRight[0]);
								}
							}
							else if (objDatumProj.sDatum == "WGS84")
							{
								//The LatLonBoundingBox coordinates corresponds to WGS84/GEODETIC, so if the current map datum is WGS84, 
								//set the layer extents to the LatLonBoundingBox coordinates.
								layer.setExtents(inheritedProperties.dLatLonBoundingBoxMaxY, inheritedProperties.dLatLonBoundingBoxMinX,
												inheritedProperties.dLatLonBoundingBoxMinY, inheritedProperties.dLatLonBoundingBoxMaxX);	
							}					
						}
					}
				}
			}
		}
	}
	else
	{
		this.service.aExcludedLayers[this.service.aExcludedLayers.length] = sName;
	}
	
	for (var i=0; i<aChildLayerNodes.length; i++)
		this.createLayers(map, aChildLayerNodes[i], parent, inheritedProperties);
}
