/*----------------    FILE HEADER ---------------------------------------
 This file is part of Geoide.
 Copyright (C) 2005-2006 by:
 IDgis B.V.
 http://www.idgis.nl

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

 Contact:

 Herman Assink
 IDgis bv
 P.O. Box 15
 7450 AA Holten
 The Netherlands
 E-Mail: herman.assink@idgis.nl

 * @version 1.4.0
 * @author IDgis team
 * 
 ------------------------------------------------------------------------*/

import nl.idgis.giclient.webserviceconnector.QueryConnector;
import nl.idgis.giclient.webserviceconnector.MapConnector;
import nl.idgis.giclient.webserviceconnector.TransformConnector;
import nl.idgis.giclient.webserviceconnector.TransformConnectorListener;
import nl.idgis.giclient.webserviceconnector.giconnector.GIConnectorListener;
import nl.idgis.giclient.webserviceconnector.giconnector.GITransformResponse;
import nl.idgis.giclient.geoma.Envelope;
import nl.idgis.giclient.util.XMLTools;
import nl.idgis.giclient.gis.Layer;
import nl.idgis.giclient.gis.GILayer;
import nl.idgis.giclient.query.QueryColumn;
import nl.idgis.giclient.framework.SinglePaneComponent;
import nl.idgis.giclient.framework.buttons.ActionButtonConfig;
import nl.idgis.giclient.GIClientConfig;
import nl.idgis.giclient.gis.Column;
import nl.idgis.giclient.gis.TimeRange;
import nl.idgis.giclient.StringTools;
import nl.idgis.giclient.gis.CentreScale;
import nl.idgis.giclient.gis.GIS;
import nl.idgis.giclient.gis.AnimationLayer;
import nl.idgis.giclient.gis.SelectableLayer;
import nl.idgis.giclient.util.ArrayTools;
import nl.idgis.giclient.geoma.GMLFactory;
import nl.idgis.giclient.geoma.Geometry;
import nl.idgis.giclient.io.XMLServer;
import nl.idgis.giclient.geoma.Point;
import nl.idgis.giclient.webserviceconnector.giconnector.TransformAction;
import nl.idgis.giclient.filterencoding.ComplexFilter;
import nl.idgis.giclient.filterencoding.OperationDefines;
import nl.idgis.giclient.io.XMLResponseListener;
import nl.idgis.giclient.webserviceconnector.giconnector.FeaturesByFilterResponseHandler;
import nl.idgis.giclient.query.QueryAspect;
import nl.idgis.giclient.query.OperationViewer;
import nl.idgis.giclient.query.QueryAspectViewer;
import nl.idgis.giclient.io.XMLResponse;
import nl.idgis.giclient.webserviceconnector.giconnector.GIQueryResponse;
import nl.idgis.giclient.webserviceconnector.giconnector.SpatialOperationResponse;

class nl.idgis.giclient.webserviceconnector.giconnector.GIConnector implements MapConnector, TransformConnector, QueryConnector {
	
	static private var instances:Array = new Array();
	static private var timeOut:Number = 20000;
		
	static function getInstance(url:String, clientConfig:GIClientConfig, version:String, title:String):GIConnector {
		var instance:GIConnector = null;
		//trace("url = " + url + " version = " + version);
		for (var i:String in instances) {
	    instance = GIConnector(instances[i]);
	    if (instance.getURL() == url) {
	        return instance;
	    }
		}
		instance = new GIConnector(url, clientConfig, instances.length, version, title);
		instances.push(instance);
		return instance;
	}
	
	static function getFirstInstance():GIConnector {
		return instances[0];
	}
	
	static function loadCapabilities(listener:GIConnectorListener, connector:TransformConnector):Void {
		
		if(instances.length > 0)
		{
			var xml:XML = new XML();
			xml.ignoreWhite = true;
			xml["listener"] = listener;
			xml["connector"] = instances.length - 1;
			xml["instances"] = instances;
			xml["transformConnector"] = connector;
			xml["timeOut"] = timeOut;
			xml["onTimeOut"] = function()
			{		
				clearInterval(this["intervalId"]);
						
				this["timedOut"] = true;
				this.onLoad(false);
			};
			xml.onData = function(src) {
				if(this["timedOut"]) {
					return;
				}
				
				clearInterval(this["intervalId"]);
			
				if (src == undefined) {
				    this.onLoad(false);
				  } else {
				    this.parseXML(src);
				    this.loaded = true;
				    this.onLoad(true);
				  }
			};
			xml.onLoad = function(success:Boolean) {				
				if(success)
				{
					if(this["instances"][this["connector"]].capabilities == null)
					{
						this["instances"][this["connector"]].capabilities = this.firstChild;
						this["instances"][this["connector"]].preProcessCapabilities();
					}
				}
			  else
			  	listener.onGIError(this["instances"][this["connector"]]);
				this["connector"]--;
				if(this["connector"] >= 0)
				{
					xml["timedOut"] = false;				
					xml["intervalId"] = setInterval(xml, "onTimeOut", this["timeOut"]);
					this.load(this["instances"][this["connector"]].getURL() + "request=GetCapabilities" +
					          "&roles=" + this["instances"][this["connector"]].getRequestedRolesString());
				}
				else
				{
					var ta:TransformAction = new TransformAction(this["instances"], this["transformConnector"], this["listener"]);
					ta.transform();					
				}
			};
			if(instances[xml["connector"]].capabilities == null)
			{
				xml["timedOut"] = false;				
				xml["intervalId"] = setInterval(xml, "onTimeOut", timeOut);
				xml.load(instances[xml["connector"]].getURL() + "request=GetCapabilities" +
				         "&roles=" + instances[xml["connector"]].getRequestedRolesString());
			}
			else
			{
				xml.onLoad(true);
			}
		}
		else		
			listener.onGIReady();
	}
	
	private var url:String = null;
	private var capabilities:XMLNode = null;
	private var id:Number;
	private var version:String = null;	
	private var majorVersion:Number;
	private var title:String = null;
	
	private var clientConfig:GIClientConfig;
	
	//Layer Cache
	private var layerNodes:Object = null;	 
	private var layerScales:Object = null;
	private var layerBoxes:Object = null;
	private var layerTypes:Object = null;
	private var layerColumns:Object = null;
	private var layerHyperlinkColumns:Object = null;
	private var layerGeometryType:Object = null;
	private var layerPrefixes:Object = null;
	private var layerTimeRanges:Object = null;
	private var layerMaxFrames:Object = null;
	private var layerWFSUrls:Object = null;
	private var layerFeatureTypes:Object = null;
	var layerEnvelopes:Object = null;
	private var brokenLayers:Array;
	
	//QueryAspect Cache
	private var queryAspectNodes:Object = null;
	private var queryAspectLabels:Object = null;
	private var queryAspectTypes:Object = null;
	private var queryAspectColumns:Object = null;
	private var queryAspectConfigs:Object = null;

	//user & roles
	private var username:String = null;
	private var requestedRoles:Array = null;
	private var assignedRoles:Array = null;
	
	var srs:Array = null;	
	var toTransformNames:Array = null;
	var toTransformPoints:Array = null;
	var toTransformMaps:Array = null;
	
	function GIConnector(url:String, clientConfig:GIClientConfig, id:Number, version:String, title:String) {
		if (url == null) {
	    // EXCEPTION
	    return;
		}
		this.url = url;
		this.clientConfig = clientConfig;
		this.id = id;
		this.version = version;
		this.title = title;
		majorVersion = StringTools.stringToNumber(version.substring(0, version.indexOf(".")));
		
		layerScales = new Object();
		layerBoxes = new Object();
		layerTypes = new Object();
		layerColumns = new Object();
		layerHyperlinkColumns = new Object();
		layerGeometryType = new Object();
		layerPrefixes = new Object();
		layerTimeRanges = new Object();
		layerMaxFrames = new Object();
		layerEnvelopes = new Object();
		layerWFSUrls = new Object();
		layerFeatureTypes = new Object();
		
		brokenLayers = new Array();
		
		queryAspectNodes = new Object();
		queryAspectLabels = new Object();
		queryAspectTypes = new Object();
		queryAspectColumns = new Object();
		queryAspectConfigs = new Object();
		
		requestedRoles = new Array();
		assignedRoles = new Array();
		
		srs = new Array();	
	    toTransformNames = new Array();
	    toTransformPoints = new Array();
	    toTransformMaps = new Array();
		
		var hostName:String = url.substring(url.indexOf("//") + 2);
		hostName = hostName.substring(0, hostName.indexOf("/"));
		
		if (hostName.indexOf(":") != -1) {
		    hostName = hostName.substring(0, hostName.indexOf(":"));
		}
		
		System.security.allowDomain(hostName); // Allows the map layers's graphics to fire ready events.				
	}
	
	function getURL():String {
  	return url;
  }
  
  function getItem(operationViewer:OperationViewer, queryAspectViewer:QueryAspectViewer):Void {
    var serverRequest:String = getServerRequest("GetDistinctColumnValues", queryAspectViewer.queryAspect, operationViewer.queryColumn);
    var filter:ComplexFilter = queryAspectViewer.getFilter();
    if(filter == null) {
      //var xmlResponse:XMLResponse = new XMLResponse(operationViewer, serverRequest);
      var xmlResponse:XMLResponse = new XMLResponse(new GIQueryResponse(operationViewer), serverRequest);
      //xmlResponse.setRuler(ruler);
      //xmlResponse.setPath(GILayer(layer).getServerURL());
      xmlResponse.setPath(queryAspectViewer.queryAspect.getServerURL());
      xmlResponse.load();        
    } else {
      var xml:XML = null;
      xml = filter.toXML("operandFiltering");
      xml.docTypeDecl = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
      //var xmlServer:XMLServer = ruler.getGIClientConfig().getXMLServer();
      var xmlServer:XMLServer = new XMLServer(queryAspectViewer.queryAspect.getServerURL());
      //xmlServer.postAndLoad(xml, operationViewer, serverRequest, null);
      xmlServer.postAndLoad(xml, new GIQueryResponse(operationViewer), serverRequest, null);
    }
  }
  
  function getServerRequest(requestType:String, queryAspect:QueryAspect, queryColumn:QueryColumn):String {
    //trace("GIConnector - URL = " + this.url);
    var req:String = null;
    if ( requestType == "GetDistinctColumnValues" ) {
      //req = http://portal.prvlimburg.nl/risicokaart_prof/giservlet?request=GetDistinctColumnValues&asp=Gemeente&col=Gemeente&valueField=app:naam&labelField=app:naam&layerName=Gemeentegrenzen
      req = "request=GetDistinctColumnValues";
      req += "&asp=" + queryAspect.getName();
      req += "&col=" + queryColumn.getName();
      req += "&labelField=" + queryColumn.getOperandLabelField();
      req += "&valueField=" + queryColumn.getOperandValueField();  
		    
      if(queryColumn.getOperandValueFT() == null) {     
        req += "&layerName=" + queryAspect.getLayerName();
      } else {
        req += "&valueFT=" + queryColumn.getOperandValueFT();
      }  		
    } else {
      trace("ERROR: this requestType (" + requestType + ") is not supported yet!");
    }
    //trace("serverRequest = " + req);
    return req;
  }
  
  private function getSRSForLayer(node:XMLNode):String {
  	 var parent:XMLNode = node.parentNode;
  	 //trace(parent.nodeName);
  	 return null;
  	 if(parent.nodeName != "Map") {
  	 		getSRSForLayer(parent);
  	 }
  	 else {
  	 		return XMLTools.getStringValue("SRS", parent);
  	 }
  }
  
  private function getLatLongBoundingBoxNode(layer:XMLNode):Envelope {  	
  	var latLongBoundingBoxNode:XMLNode = 
  		XMLTools.getChild("LatLongBoundingBox", layer);
    if (latLongBoundingBoxNode != null) {
        var minX:Number = XMLTools.getNumberValue("minx", latLongBoundingBoxNode);
        var minY:Number = XMLTools.getNumberValue("miny", latLongBoundingBoxNode);
        var maxX:Number = XMLTools.getNumberValue("maxx", latLongBoundingBoxNode);
        var maxY:Number = XMLTools.getNumberValue("maxy", latLongBoundingBoxNode);
        return new Envelope(getSRSForLayer(layer), minX, minY, maxX, maxY);
    }
  }
  
  private function getLayerNode(layerName:String):XMLNode {
  	var storedLayerNode:XMLNode = XMLNode(layerNodes[layerName]);
  	if(storedLayerNode != null)
  	{
  		return storedLayerNode;
  	}
  	
  	for(var i:Number = 0;i < brokenLayers.length; i++) {
  		if(brokenLayers[i] == layerName) {
  			return null;
  		}
  	}
  	
  	//trace(" majorVersion = " + majorVersion);
  	if(majorVersion <= 2)	{
	  	var maps:XMLNode = XMLTools.getChild("Maps", capabilities);
	  	var mapArray:Array = XMLTools.getChildNodes("Map", maps);
	  	for(var i:Number = 0; i < mapArray.length; i++)
	  	{
	  		var layers:XMLNode = XMLTools.getChild("Layers", mapArray[i]);
	  		var layerArray:Array = XMLTools.getChildNodes("Layer", layers);
	  		
				var node:XMLNode = getLayer(layerName, layerArray);
				if(node != null)
				{
					layerNodes[layerName] = layerName;				
					return node;
				}
	  	}	  	
  	}
  	else {
  		  var layerArray:Array = XMLTools.getChildNodes("Layer", capabilities);
	  		
				var node:XMLNode = getLayer(layerName, layerArray);
				if(node != null)
				{
					layerNodes[layerName] = layerName;				
					return node;
				}
  	}
  	
  	brokenLayers.push(layerName);  	
  	trace("ERROR, layer " + layerName + " not found at service " + this.url + " check context and server configuration");
  	
  	return null;
  }
  
  private function getLayer(layerName:String, layerArray:Array):XMLNode {  	
 		//trace("layerName = " + layerName + " layerArray.length = " + layerArray.length);
 		for(var i:Number = 0; i < layerArray.length; i++)
 		{
 			var name:String = XMLTools.getStringValue("Name", layerArray[i]);
 			//trace("name = " + name);
 			if(name == layerName)
 				return layerArray[i];
 			
 			if(majorVersion <= 2) { 				
	 		  var layers:XMLNode = XMLTools.getChild("Layers", layerArray[i]);
	 		  if(layers != null)
	 		  {
	 		  	var subLayerArray:Array = XMLTools.getChildNodes("Layer", layers);
	 		  	if(subLayerArray != null)
	 		  	{
	 		  		var sub:XMLNode = getLayer(layerName, subLayerArray);
	 		  		if(sub != null)
	 		  			return sub;
	 		  	}
	 		  }
 			}
 		}
 		
 		return null;
  }
  
  function getMaxFrames(layerName:String):Number {
  	if(layerMaxFrames[layerName] != null)
  		return layerMaxFrames[layerName];
  	
  	var maxFrames:Number = XMLTools.getNumberValue("MaxFrames", getLayerNode(layerName));
  	
  	layerMaxFrames[layerName] = maxFrames;
  	
  	return maxFrames;
  }
  
  function getTimeRange(layerName:String):TimeRange {
  	if(layerTimeRanges[layerName] != null)
  		return layerTimeRanges[layerName];
  	
	  var timeRangeNode:XMLNode = XMLTools.getChild("TimeRange", getLayerNode(layerName));
		var timeRangeBegin:String = XMLTools.getStringValue("Begin", timeRangeNode);
		var begin:Date = StringTools.parseDateTime(timeRangeBegin);
		var timeRangeEnd:String = XMLTools.getStringValue("End", timeRangeNode);
		var end:Date = StringTools.parseDateTime(timeRangeEnd);
  	
  	var timeRange:TimeRange = new TimeRange(begin, end);
  	
  	layerTimeRanges[layerName] = timeRange;
  	
  	return timeRange;
  }
  
  function getLatLongBoundingBox(layerName:String):Envelope {
  	if(layerBoxes[layerName] != null)
  		return layerBoxes[layerName];
  		
  	var envelope:Envelope = getLatLongBoundingBoxNode(getLayerNode(layerName)); 
  	
  	layerBoxes[layerName] = envelope;
  	return envelope; 	
  }
  
  function getScales(layerName:String):Array {
  	if(layerScales[layerName] != null)
  		return layerScales[layerName];
  	var scales:Array = XMLTools.getNumberValues("Scale", XMLTools.getChild("Scales",
  		getLayerNode(layerName)));
  	layerScales[layerName] = scales;  	  
  	return scales;
  }
  
  function getType(layerName:String):String {
  	if(layerTypes[layerName] != null)
  		return layerTypes[layerName];
  	  	
  	var layerNode:XMLNode = getLayerNode(layerName);
  	var type:String = majorVersion > 2 ? XMLTools.getStringValue("ContentType", layerNode).toUpperCase()
  		: XMLTools.getStringValue("Type", layerNode).toUpperCase();
  			
  	layerTypes[layerName] = type;
  	return type;
  }
  
  function getGeometryType(layerName:String):String {
  	if(layerGeometryType[layerName] != null)
  		return layerGeometryType[layerName];
  	
  	var geometryType:String = XMLTools.getStringValue("GeometryType",
  		getLayerNode(layerName));
  		
    layerGeometryType[layerName] = geometryType;
  		
    return geometryType;
  }
  
  function getFeatureIdPrefix(layerName:String):String {
  	if(layerPrefixes[layerName] != null)
  		return layerPrefixes[layerName];
  	
  	var prefix:String = XMLTools.getStringValue("FeatureIdPrefix",
  		getLayerNode(layerName));
  		
  	layerPrefixes[layerName] = prefix;
  		
  	return prefix;
  }
  
  private function loadColumns(columnNodes:Array):Array {
        var path:String = "";
        var alias:String = null;
        var type:String = null;
        var urlPrefix:String = null;
        var width:Number = 0;
        var column:Column = null;
        var columns:Array = new Array();
        var searchType:String = null;

        for (var i:Number = 0; i < columnNodes.length; i++) {
            path = XMLTools.getStringValue("Path", columnNodes[i]);
            column = new Column(path);

            alias = XMLTools.getStringValue("Alias", columnNodes[i]);
            if (alias != null) {
                column.setAlias(alias);
            }
            type = XMLTools.getStringValue("Type", columnNodes[i]);
            if (type != null) {
                column.setType(type);
            }
            urlPrefix = XMLTools.getStringValue("Urlprefix", columnNodes[i]);
            if (urlPrefix != null) {
                column.setURLPrefix(urlPrefix);
            }
            width = XMLTools.getNumberValue("Width", columnNodes[i]);
            if (width > -1) {
                column.setWidth(width);
            }
            searchType = XMLTools.getStringValue("SearchType", columnNodes[i]);
            if (searchType != null) {
                column.setSearchType(searchType);
            }

            columns.push(column);
        }
        return columns;
    }
  
  function getColumns(layerName:String):Array {  	
  	if(layerColumns[layerName] != null)
  		return layerColumns[layerName];
  	
  	var columnsNode:XMLNode = XMLTools.getChild("Columns", getLayerNode(layerName));
  	var columnNodes:Array = XMLTools.getChildNodes("Column", columnsNode);
  	
  	var columns:Array = loadColumns(columnNodes);
  	layerColumns[layerName] = columns;
  	
  	return columns;
  }
  
  function getHyperlinkColumnName(layerName:String):String {
		if(layerHyperlinkColumns[layerName] != null)
			return layerHyperlinkColumns[layerName];
  	
  	var hyperlinkNode:XMLNode = XMLTools.getChild("HyperlinkColumn",
  		getLayerNode(layerName));
    var path:String = XMLTools.getStringValue("path", hyperlinkNode);
    
    layerHyperlinkColumns[layerName] = path;
    
    return path;
  }
  
  function getQueryAspectNode(queryName:String):XMLNode { 
  	var storedNode:XMLNode = queryAspectNodes[queryName];
  	if(storedNode != null)
  		return storedNode;
  	 	
  	var maps:XMLNode = XMLTools.getChild("Maps", capabilities);
  	var mapArray:Array = XMLTools.getChildNodes("Map", maps);
  	
  	for(var i:Number = 0; i < mapArray.length; i++)
  	{
  		var aspects:XMLNode = XMLTools.getChild("QueryAspects", mapArray[i]);
  		var aspectArray:Array = XMLTools.getChildNodes("QueryAspect", aspects);
  		for(var j:Number = 0; j < aspectArray.length; j++)
  		{
  			var name:String = XMLTools.getStringValue("Name", aspectArray[j]);
  			if(name == queryName)
  			{
  				queryAspectNodes[queryName] = aspectArray[j];
  				return aspectArray[j];
  			}
  		}
  	}
  	  	  	  	
  	return null;
  }
  
  function getQueryAspectLabel(queryName:String):String {  
  	if(queryAspectLabels[queryName] != null)
  		return queryAspectLabels[queryName];
  			
  	var queryLabel = XMLTools.getStringValue("Label", getQueryAspectNode(queryName));
  	queryAspectLabels[queryName] = queryLabel;
  	return queryLabel;
  }
  
  function getQueryAspectType(queryName:String):String {
  	if(queryAspectTypes[queryName] != null)
  		return queryAspectTypes[queryName];
  	
  	var queryType = XMLTools.getStringValue("QueryType", getQueryAspectNode(queryName));
		queryAspectTypes[queryName] = queryType;
		return queryType;  
  }
  
  function getQueryAspectColumns(queryName:String):Array {
  	if(queryAspectColumns[queryName] != null)
  		return queryAspectColumns[queryName];
  	
  	var queryColumns:Array = new Array();
  	
  	var aspectNode:XMLNode = getQueryAspectNode(queryName);
  	var queryColumnsNode:XMLNode = XMLTools.getChild("QueryColumns", aspectNode);
  	var queryColumnNodes:Array = XMLTools.getChildNodes("QueryColumn", queryColumnsNode);
  	
  	for(var i:Number = 0; i < queryColumnNodes.length; i++)
  	{
  		var queryColumnName:String = XMLTools.getStringValue("Name", queryColumnNodes[i]);
	    var queryColumnFTQueryField:String = XMLTools.getStringValue("FTQueryField", queryColumnNodes[i]);
	    var operandValueFT:String = XMLTools.getStringValue("OperandValueFT", queryColumnNodes[i]);
	    var queryColumnFTParentProperty:String = null;
	    var queryColumnFTGeometryNode:String = null;
	    var queryColumnOperandLabelField:String = XMLTools.getStringValue("OperandLabelField", queryColumnNodes[i]);
	    var queryColumnOperandValueField:String = XMLTools.getStringValue("OperandValueField", queryColumnNodes[i]);
	    var searchType:String = XMLTools.getStringValue("SearchType", queryColumnNodes[i]);
	    var format:String = XMLTools.getStringValue("Format", queryColumnNodes[i]);
	    var tooltip:String = XMLTools.getStringValue("ToolTip", queryColumnNodes[i]);
	    var operatorsNode:XMLNode = XMLTools.getChild("Operators", queryColumnNodes[i]);
	    var operatorNodes:Array = XMLTools.getChildNodes("Operator", operatorsNode);
	    var operatorNames:Array = new Array();
	    for (var k:Number = 0; k < operatorNodes.length; k++) {
	      var operatorName:String = XMLTools.getStringValue("name", operatorNodes[k]);
	      operatorNames.push(operatorName);
	    }
	    var queryColumn:QueryColumn = new QueryColumn(queryColumnName, null, queryColumnFTQueryField, 
	                                     operandValueFT, null, queryColumnFTParentProperty, queryColumnFTGeometryNode,
	                                     queryColumnOperandLabelField, queryColumnOperandValueField,
	                                     operatorNames, searchType, format, tooltip);
	    queryColumns.push(queryColumn);
  	}
  	
  	queryAspectColumns[queryName] = queryColumns;
  	return queryColumns;
  }
  
  function getQueryAspectActionButtonConfigs(queryName:String):Array {
  	if(queryAspectConfigs[queryName] != null)
  		return queryAspectConfigs[queryName];
  	
  	var actionButtonConfigs:Array = new Array();
  	
  	var queryAspectNode:XMLNode = getQueryAspectNode(queryName);  	
  	var queryActionsNode:XMLNode = XMLTools.getChild("QueryActions", queryAspectNode);
	  var queryActionNodes:Array = XMLTools.getChildNodes("QueryAction", queryActionsNode);
	   
		
		for (var j:Number = 0; j < queryActionNodes.length; j++) {
			var queryActionName:String = XMLTools.getStringValue("name", queryActionNodes[j]);
			var paneComponent:SinglePaneComponent = SinglePaneComponent(
				clientConfig.getGIComponent("QueryBuilder"));
			var giButtonConfigs:Array = paneComponent.getGIButtonConfigs();
			for (var k:Number = 0; k < giButtonConfigs.length; k++) {
			    if (giButtonConfigs[k] instanceof ActionButtonConfig) {
			        if (ActionButtonConfig(giButtonConfigs[k]).getActionClass()==queryActionName){
			            actionButtonConfigs.push(giButtonConfigs[k]);
			        }
			    }
			}
		}
		
		queryAspectConfigs[queryName] = actionButtonConfigs;
		
		//trace(actionButtonConfigs.length);
				
		return actionButtonConfigs;
  }
  
	function getLegendGraphic(layer:Layer, scale:Number):String {
		if(this.capabilities == null) //Connector not properly initialized
		{
			return null;
		}
		
		if(getLayerNode(layer.getName()) == null) { //Layer not found
			return null;
		}
		
	    var url:String = this.url;
	    url += "request=GetLegendGraphic";
	    url += "&layer=" + layer.getName();
	    //url += "&scale=" + scale;
	    if(GILayer(layer).legendIsDynamic()){
	    	url += "&symbolOnly=false";
	    } else {	
	    	url += "&symbolOnly=true";
	    }
	    
	    return url;
  }
  
	function getMap(layer:Layer, centreScale:CentreScale, width:Number, height:Number, gis:GIS):String {
		if(this.capabilities == null)
		{
			return null;
		}
		
		var url:String = this.url;
  	url += "request=GetMap";
  	url += "&width=" + width;
  	url += "&height=" + height;
  	url += "&layer=" + layer.getName();
  	url += "&srs=" + centreScale.getSRS();
  	
  	var envelope:Envelope = centreScale.toEnvelope(width, height, 
  		gis.getCoordPixFactorForSrs(centreScale.getSRS()));  	
  	url += "&envelope=" + envelope.getMinX() + "," + envelope.getMinY() + "," + envelope.getMaxX() + "," + envelope.getMaxY();        
  	
  	var inVisibleLayers:Boolean = gis.isVisibleLayer(layer);
  	if ((!inVisibleLayers) || ((layer instanceof SelectableLayer) && (SelectableLayer(layer).getFilteredFeatures().length > 0))) {
			var sl:SelectableLayer = SelectableLayer(layer);
			var features:Array = null;
			if ((!inVisibleLayers) && (sl.getFilteredFeatures().length > 0)) {
			    var selectedFeatures:Array = sl.getSelectedFeatures();
			    var filteredFeatures:Array = sl.getFilteredFeatures();
			    features = ArrayTools.intersect(selectedFeatures, filteredFeatures);
			} else if (!inVisibleLayers) {
			    features = sl.getSelectedFeatures();
			} else if (sl.getFilteredFeatures().length > 0) {
			    features = sl.getFilteredFeatures();
			}
			var ids:Array = new Array();
			for (var i:Number = 0; i < features.length; i++) {
			    ids.push(features[i].getID());
			}
			if(ids.length != 0){
				url += "&id=" + ids.toString();
			}	
		}
  	
  	if (layer instanceof AnimationLayer) {
	    url += "&TimeRangeStart=" + StringTools.dateTimeToString(gis.getCurrentTimeRangeBegin());
	    url += "&TimeRangeEnd=" + StringTools.dateTimeToString(gis.getCurrentTimeRangeEnd());
	   	url += "&TimeScale=" + gis.getCurrentTimeScale().toString();
	    url += "&fps=" + gis.getCurrentFPS().toString();
    }  	
  	
  	return url;
  }
  
  function transform(geom:Geometry, from:String, to:String, listener:TransformConnectorListener):Void {
  	var geometryNode:XMLNode = GMLFactory.createGeometryNode("transformGeom", geom, from);  	
    var geometryXML:XML = new XML();        
		geometryXML.appendChild(geometryNode.firstChild);				
		var serverRequest:String = "request=Transform&toSRS=" + to;
			//var xmlServer:XMLServer = mapViewer.ruler.getGIClientConfig().getXMLServer();
		var xmlServer:XMLServer = 
		new XMLServer(GIConnector.getFirstInstance().getURL());
		var transformResponse:GITransformResponse = new GITransformResponse(listener);
		xmlServer.postAndLoad(geometryXML, transformResponse, serverRequest, null);
	}
	
	function getID():String {
		return "GI" + id;
	}
		
	private function preProcessLayer(parent:XMLNode, mapNb:Number):Void {
		var layers:Array = XMLTools.getChildNodes("Layer", parent);
		for(var i:Number = 0; i < layers.length; i++)
		{
			
			var name:String = XMLTools.getStringValue("Name", layers[i]);			
			if(name != null)
			{			
				for(var j:Number = 0; j < srs.length; j++) {
					toTransformPoints[mapNb].push(new Array());
					toTransformNames[mapNb].push(new Array());
										
					var bbox:XMLNode;
					if(majorVersion <= 2) {
						bbox = XMLTools.getChild("LatLongBoundingBox", layers[i]);
					}
					else {
						bbox = XMLTools.getChild("LatLonBoundingBox", layers[i]);
					}
					
					if(srs[j] != toTransformMaps[mapNb])
					{
						var alreadyKnown:Boolean = false;						
						
						if(majorVersion >= 3) {
							var boundingBoxes:Array = XMLTools.getChildNodes("BoundingBox", layers[i]);
							for(var k:Number = 0; k < boundingBoxes.length; k++) {
								var SRS:String = XMLTools.getStringValue("SRS", boundingBoxes[k]);								
								if(SRS == srs[j])	{
									var envelope:Envelope = new Envelope(SRS,
										XMLTools.getNumberValue("minx", boundingBoxes[k]),
										XMLTools.getNumberValue("miny", boundingBoxes[k]),
										XMLTools.getNumberValue("maxx", boundingBoxes[k]),
										XMLTools.getNumberValue("maxy", boundingBoxes[k]));
									layerEnvelopes[srs[j] + name] = envelope;
									alreadyKnown = true;
								}
							}
						}
						
						if(!alreadyKnown) {												
							toTransformNames[mapNb][j].push(name);
							Array(toTransformPoints[mapNb][j]).push(new Point(srs[j], XMLTools.getNumberValue("minx", bbox), XMLTools.getNumberValue("miny", bbox)));
							Array(toTransformPoints[mapNb][j]).push(new Point(srs[j], XMLTools.getNumberValue("maxx", bbox), XMLTools.getNumberValue("maxy", bbox)));
						}
					}
					else
					{
						layerEnvelopes[srs[j] + name] = new Envelope(srs[j], XMLTools.getNumberValue("minx", bbox),
							XMLTools.getNumberValue("miny", bbox), 
								XMLTools.getNumberValue("maxx", bbox),
									XMLTools.getNumberValue("maxy", bbox));
					}		
		
  				layerWFSUrls[name] = XMLTools.getStringValue("WebFeatureServiceUrl", layers[i]);
				layerFeatureTypes[name] = XMLTools.getStringValue("FeatureType", layers[i]);
				}
			}
			
			if(majorVersion <= 2)
			{
				var layersNode:XMLNode = XMLTools.getChild("Layers", layers[i]);
				if(layersNode != null)
					preProcessLayer(layersNode, mapNb);
			}			
		}		
	}
	
	function preProcessCapabilities():Void {
		if(majorVersion <= 2) {
			var maps:Array = XMLTools.getChildNodes("Map",
				XMLTools.getChild("Maps", capabilities));				
			for(var i:Number = 0; i < maps.length; i++)	
			{
				var mapSrs = XMLTools.getStringValue("SRS", maps[i]);	
				if(mapSrs == null){
					mapSrs = "EPSG:28992";
				}
				
				toTransformMaps.push(mapSrs);
				toTransformPoints.push(new Array());
				toTransformNames.push(new Array());
				
				preProcessLayer(XMLTools.getChild("Layers", maps[i]), i);
			}		
		}
		else {						
			toTransformMaps.push("EPSG:4326");
			toTransformPoints.push(new Array());
			toTransformNames.push(new Array());			
			
			preProcessUserAndRoles(capabilities);
			preProcessLayer(capabilities, 0);
		}
	}
	
	private function preProcessUserAndRoles(parent:XMLNode):Void {
		username = XMLTools.getStringValue("User", parent);
		//trace("preProcessUserAndRoles, username = " + username);
		var roleNodes:Array = XMLTools.getChildNodes("Role", parent);
		//trace("preProcessUserAndRoles, roleNodes.length = " + roleNodes.length);
   		for (var i:Number = 0; i < roleNodes.length; i++) {
   			var roleName:String = roleNodes[i].firstChild.nodeValue;
			//trace("preProcessUserAndRoles, roleName = " + roleName);
   			assignedRoles.push(roleName);
   		}
	}

	function addSRS(srs:String):Void {
		for(var i:Number = 0; i < this.srs.length; i++)
		{
			if(this.srs[i] == srs)
				return;
		}
		this.srs.push(srs);
	}	
	
	function getEnvelope(layerName:String, srs:String) {
		return layerEnvelopes[srs + layerName];
	}
	
	function getFeatureTypeName(layerName:String) {
		return layerFeatureTypes[layerName];
	}
	
	function isSRSSupported(layer : Layer, srs : String) : Boolean {
		if(this.majorVersion >= 3) {
			var layerNode:XMLNode = getLayerNode(layer.getName());
			if(layerNode != null) {
				var supportedSRS:Array = XMLTools.getStringValues("SRS", layerNode);
				for(var i:Number = 0; i < supportedSRS.length; i++) {
					if(supportedSRS[i] == srs) {
						return true;
					}
				}
			}			
			return false;
		} else {
			return true;
		}
	}
	
	function getFeaturesFromLayerByFilter(layer:GILayer, serverParams:Object, responseHandler:Object, filter:ComplexFilter):Void{
    //voeg responseHandler toe aan de serverParams
        if(serverParams==null){
            serverParams = new Object();
        }
        serverParams["responseHandler"] = responseHandler;
    //Ook rekening houden met bestaande filter op een laag
        var complexFilter:ComplexFilter = null;
        var currentFilter:ComplexFilter = null;
        /*if(layer instanceof SelectableLayer)
        	currentFilter = SelectableLayer(layer).getCurrentFilter(); */
        if (currentFilter!=null && currentFilter!=filter) {
            complexFilter = new ComplexFilter(null,currentFilter,filter,OperationDefines.AND);
          } else {
            complexFilter = filter;
          }
        var serverRequest:String = "request=GetFeature&layer=" + layer.getName();
        serverRequest += "&filtered=true";
        serverRequest += "&srs=" + layer.getSRS();
        var xml:XML = complexFilter.toXML("ftFiltering");
        xml.docTypeDecl = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
        var xmlServer:XMLServer = new XMLServer(this.getURL());       
                 
        var handler:XMLResponseListener = new FeaturesByFilterResponseHandler(null, this, layer.getSRS());
        xmlServer.postAndLoad(xml, handler, serverRequest, serverParams);
  }
	
	function getFeaturesByFilter(queryAspect:QueryAspect, serverParams:Object, responseHandler:Object, filter:ComplexFilter):Void{
    //add responseHandler to serverParams
        if(serverParams==null){
            serverParams = new Object();
        }
        serverParams["responseHandler"] = responseHandler;
    	//Take into account existing filters on a layer
        var complexFilter:ComplexFilter = null;
        var currentFilter:ComplexFilter = null;
        /*if(layer instanceof SelectableLayer)
        	currentFilter = SelectableLayer(layer).getCurrentFilter(); */
        if (currentFilter!=null && currentFilter!=filter) {
            complexFilter = new ComplexFilter(null,currentFilter,filter,OperationDefines.AND);
          } else {
            complexFilter = filter;
          }
        var serverRequest:String = "request=GetFeature&layer=" + queryAspect.getLayerName();
        serverRequest += "&filtered=true";
        var srsName:String = null;        
        if(majorVersion > 2) {
        	srsName = XMLTools.getStringValues("SRS", 
        		getLayerNode(queryAspect.getLayerName()))[0];
        }
        serverRequest += "&srs=" + srsName;
        //trace(getSRSForLayer(getLayerNode(queryAspect.getLayerName())));
        //trace(getLayerNode(queryAspect.getLayerName()));
        var xml:XML = complexFilter.toXML("ftFiltering");
        xml.docTypeDecl = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
        var xmlServer:XMLServer = new XMLServer(this.getURL());       
                 
        var handler:XMLResponseListener = new FeaturesByFilterResponseHandler(queryAspect, this, srsName);
        xmlServer.postAndLoad(xml, handler, serverRequest, serverParams);
  }
  
	function getFeaturesByFilterForLayer(layer:SelectableLayer, serverParams:Object, responseHandler:XMLResponseListener, filter:ComplexFilter):Void{
    //voeg responseHandler toe aan de serverParams
        if(serverParams==null){
            serverParams = new Object();
        }
        serverParams["responseHandler"] = responseHandler;
    //Ook rekening houden met bestaande filter op een laag
        var complexFilter:ComplexFilter = null;
        var currentFilter:ComplexFilter = null;
        /*if(layer instanceof SelectableLayer)
        	currentFilter = SelectableLayer(layer).getCurrentFilter(); */
        if (currentFilter!=null && currentFilter!=filter) {
            complexFilter = new ComplexFilter(null,currentFilter,filter,OperationDefines.AND);
          } else {
            complexFilter = filter;
          }
        var serverRequest:String = "request=GetFeature&layer=" + layer.getName();
        serverRequest += "&filtered=true";
        serverRequest += "&srs=" + layer.getSRS();
        var xml:XML = complexFilter.toXML("ftFiltering");
        xml.docTypeDecl = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
        var xmlServer:XMLServer = new XMLServer(this.getURL());       
                 
        //var handler:XMLResponseListener = new FeaturesByFilterResponseHandler(queryAspect, this);
        xmlServer.postAndLoad(xml, responseHandler, serverRequest, serverParams);
  }  
  
  function getTitle():String {
  	return title;
  }
  
  function doSpatialOperation(operandFC:XMLNode, operationObject:Object, featureTypeNames:Array, featureId:String, destinationObject:Object):Void {
  	var xmlServer:XMLServer = new XMLServer(this.getURL());
  	var request:XMLNode = new XMLNode(1, "Spatial_Operation_Request");
  	request.attributes["xmlns:gml"] = "http://www.opengis.net/gml";
  	var operationNode:XMLNode = new XMLNode(1, operationObject["operation"]);
  	request.appendChild(operationNode);
  	
  	if(featureId != null) {
  		var featureIdNode:XMLNode = new XMLNode(1, "FeatureId");
  		featureIdNode.appendChild(new XMLNode(3, featureId));
  		operationNode.appendChild(featureIdNode);
  	}
  	
  	for(var i:Number=0;i<featureTypeNames.length;i++){
  		var featureTypeNode:XMLNode = new XMLNode(1, "FeatureTypeName");
  		featureTypeNode.appendChild(new XMLNode(3, featureTypeNames[i]));
  		operationNode.appendChild(featureTypeNode);
  		var nameSpace:String = featureTypeNames[i].substr(0,featureTypeNames[i].indexOf(":"));
  		request.attributes["xmlns:" + nameSpace] = "http://www.deegree.org/app";
  	}	
  	operationNode.appendChild(operandFC);
  	
  	if(operationObject["operation"]=="Intersect"){
  		if (operationObject["bufferDistance"]!=null){
	  		var bufferNode:XMLNode = new XMLNode(1,"Buffer");
	  		bufferNode.appendChild(new XMLNode(3,operationObject["bufferDistance"]));
	  		operationNode.appendChild(bufferNode);
  		}	
	  	var geometryResponses:Array = Array(operationObject["geometryResponses"]);
	  	for(var i:Number=0;i<geometryResponses.length;i++){
	  		var geometryResponseNode:XMLNode = new XMLNode(1, "GeometryResponse");
	  		geometryResponseNode.appendChild(new XMLNode(3, geometryResponses[i]));
	  		operationNode.appendChild(geometryResponseNode);
	  	}
  	}
  	var responseHandler:SpatialOperationResponse = new SpatialOperationResponse(destinationObject);
  	xmlServer.postAndLoad(new XML(request.toString()), responseHandler, "request=SpatialOperation", null);
  }  	
  
  function split(geom:Geometry, featureTypeNames:Array, featureId:String, destinationLayer:SelectableLayer):Void {
  	var destinationObject:Object = Object(destinationLayer); 
  	var operationObject:Object = new Object();
  	operationObject["operation"] = "Split";  	
  	var collection:XMLNode = new XMLNode(1, "gml:FeatureCollection");
  	var member:XMLNode = new XMLNode(1, "gml:featureMember");
  	var splitObject:XMLNode = new XMLNode(1, "SplitObject");
  	var geometry:XMLNode = new XMLNode(1, "Geometry");  	  	
  	geometry.appendChild(GMLFactory.createGMLNode(geom));
  	splitObject.appendChild(geometry);
  	member.appendChild(splitObject);
  	collection.appendChild(member);  	
  	doSpatialOperation(collection, operationObject, featureTypeNames, featureId, destinationObject);
  }
  
  function intersect(features:Array, featureTypeNames:Array, 
  	destinationObject:Object, bufferDistance:Number, geometryResponses:Array):Void{
  	var operationObject:Object = new Object();
  	operationObject["operation"] = "Intersect";
  	operationObject["bufferDistance"] = bufferDistance;
  	operationObject["geometryResponses"] = geometryResponses;
   	var operandFC:XML = GMLFactory.createEmptyFC();
   	var featureCollectionNode:XMLNode = XMLTools.getChild("gml:FeatureCollection", operandFC);
      	
   	for (var i:Number = 0; i < features.length; i++) {
   		var featureMemberNode:XMLNode = operandFC.createElement("gml:featureMember");
        featureCollectionNode.appendChild(featureMemberNode);
        var featureNode:XMLNode = operandFC.createElement("IntersectObject");
        featureMemberNode.appendChild(featureNode);
        featureNode.attributes["fid"] = features[i].getID();
        var geometryNode:XMLNode = GMLFactory.createGeometryNode("Geometry", features[i].getGeometry(),null); 
        featureNode.appendChild(geometryNode);
   	}
  	doSpatialOperation(operandFC, operationObject, featureTypeNames, null, destinationObject);
  }
	
	public function getBuffer(geometry : Geometry, buffer : Number, listener : XMLResponseListener) : Void {
		var url:String = this.getURL() + "request=GetBuffer&buffer=" + buffer;
		
		
		var xmlServer:XMLServer = new XMLServer(url);
		var xml:XML = new XML();
		xml.appendChild(GMLFactory.createGMLNode(geometry));
		xmlServer.postAndLoad(xml, listener, "");
	}
	
	public function addRequestedRoles(roles:String):Void {
		var roleArray:Array = roles.split(",");
   		for (var i:Number = 0; i < roleArray.length; i++) {
   			var roleFound:Boolean = false;
   			for (var j:Number = 0; j < requestedRoles.length; j++) {
   				if (requestedRoles[j] == roleArray[i]) {
   					roleFound = true;
   					break;
   				}
   			}
   			if (!roleFound) {
   				requestedRoles.push(roleArray[i]);
   			}
   		}
	}
	
	private function getRequestedRolesString():String {
		return requestedRoles.join(",");
	}

	public function getAssignedRoles():Array {
		return assignedRoles;
	}

    public function getUsername():String {
    	return username;
    }
}