/**
 * @author prakash 
 * @modifiedby David Cordner
 *
 * Date: 03/05/2009
 * 
 * Class: Astun.iSharemaps.OLLayerControl
 * Controls iShareMaps layers in extended OpenLayers maps.  Instances of this
 * class are created with the <Astun.iSharemaps.OLLayerControl> consructor.  
 */
if (!OpenLayers || !Prototype) throw new Error('Dependent library not found');  // Needs to be nicer, possibly iSharemaps-wide library error?

if (!Astun) var Astun = {};
if (!Astun.iSharemaps) Astun.iSharemaps = {};
Astun.iSharemaps.OLLayerControl = Class.create(
{
    /**
    * Constructor: Astun.iSharemaps.OLLayerControl
    * Create a new iShareMaps layer control object. This class interacts 
    *     with Astun.iSharemaps.OLMap objects to set the displayed layers.
    *
    * Parameters:
    * layergroups - {Array} Array of layergroups
    * atolMap - {<Astun.iSharemaps.OLMap>} Astun iShareMaps wrapper for
    *     OpenLayers map object.
    */
    initialize: function(atolMap, layerGroups, forceThematic) {
        this.mapWrapper = atolMap;
        this.layerGroups = $A([]);
        this.geometrySources = $H();
        //keep currently displayed theme info
        this.thematicInfo = {};
        this.thematicInfo.force = forceThematic;
        //keep cached data Json string
        this.themeDataSources = $H();
        //if current theme being processed
        this.thematicInfo.themeBeingProcessed = false;
        this.thematicInfo.panning = false;
        this.cutOffDate = new Date();
        if (layerGroups) {
            this.importLayerGroups(layerGroups);
        }
        this.defaultStyleMap = new OpenLayers.StyleMap({
            'default': {
                fillColor: '#eeeeee',
                fillOpacity: 0.3,
                hoverFillColor: "purple",
                strokeColor: '#333333',
                strokeOpacity: 0.5,
                strokeWidth: 1
            }
        });
        this.queryHandler = new Astun.iSharemaps.Common.QueryHandler({
            'layers': 'l'
        });

        var urlLayers = this.queryHandler.getParameter('layers');
        if (urlLayers) {
            if (!Object.isArray(urlLayers)) {
                urlLayers = [urlLayers];
            }
            var original = urlLayers.length;
            for (var l = 0; l < original; l++) {
                var layers = urlLayers.shift();
                layers = layers.split(',');
                while (layers.length) {
                    urlLayers.push(layers.pop() + ''.trim());  //make sure we try to trim string, if this array member is not a string then it should end up just being ignored
                }
            }
            this.urlLayers = urlLayers;
        }

        var toggleHomeFeatureEvent = function(evt) {
            this.toggleHomeFeature(evt.memo);
        }
        Event.observe(this.mapWrapper.mapElement, 'astun:setHomeFeature', toggleHomeFeatureEvent.bindAsEventListener(this));

    },
    getDataLevelForZoom: function(oLayer) {
        //alert(this.mapWrapper.map.zoom);
        //return oLayer.thematic.zoomLevels[this.mapWrapper.map.zoom].geometrySource;
        //return "Local Authority";

        var thisZoom = this.mapWrapper.map.zoom;
        var zoomLevels = oLayer.group.zoomLevels;
        for (i = 0; i < zoomLevels.length; i++) {
            for (j = 0; j < zoomLevels[i].level.length; j++) {
                if (zoomLevels[i].level[j] == thisZoom) {
                    return zoomLevels[i].data;
                    break;
                }
            }
        }
        return 0;
    },
    getGeometryForZoom: function(oLayer) {
        //alert(this.mapWrapper.map.zoom);
        //return oLayer.thematic.zoomLevels[this.mapWrapper.map.zoom].geometrySource;
        //return "Local Authority";

        var thisZoom = this.mapWrapper.map.zoom;
        var zoomLevels = oLayer.group.zoomLevels;
        for (i = 0; i < zoomLevels.length; i++) {
            for (j = 0; j < zoomLevels[i].level.length; j++) {
                if (zoomLevels[i].level[j] == thisZoom) {
                    return zoomLevels[i].geometrySource;
                    break;
                }
            }
        }
        return 0;
    },
    getFriendlyNameForZoom: function(oLayer) {
        //alert(this.mapWrapper.map.zoom);
        //return oLayer.thematic.zoomLevels[this.mapWrapper.map.zoom].geometrySource;
        //return "Local Authority";

        var thisZoom = this.mapWrapper.map.zoom;
        var zoomLevels = oLayer.group.zoomLevels;
        for (i = 0; i < zoomLevels.length; i++) {
            for (j = 0; j < zoomLevels[i].level.length; j++) {
                if (zoomLevels[i].level[j] == thisZoom) {
                    return zoomLevels[i].friendlyName;
                    break;
                }
            }
        }
        return '';
    },
    setZoomLevelKey: function(oLayer) {
        var atGeomPanel = $('atGeomPanel');
        if (atGeomPanel) {
            var fn = this.getFriendlyNameForZoom(oLayer);
            for (var j = 0; j < atGeomPanel.childNodes.length; j++) {
                //atGeomPanel.childNodes[j].setStyle({'background':'white'});
                //atGeomPanel.childNodes[j].setStyle({'color':'green'});
                //var zoomImage = atGeomPanel.select('img'+fn);
                //zoomImage.setOpacity(0.8);
                atGeomPanel.childNodes[j].setOpacity(0.7);
            }
            var element = $('div' + fn);
            element.setOpacity(1.0);

        }
    },

    getAllGroupDisplayName: function() {
        /**
        * Function: getAllGroupDisplayName
        * To return all the layergroups display name in a concatenated string, used for debugging.
        *
        * Parameters:
        * 
        * Returns:
        * {string} 
        */
        var strText = "";
        for (var gj = 0; gj < this.layerGroups.length; gj++)
            strText += " " + this.getGroupDisplayName(gj) + ";" + " \n ";
        return strText;
    },

    getGroupDisplayName: function(grpNO) {
        /**
        * Function: getGroupDisplayName
        * To return all the displayName of a layerGroup,  used for debugging.
        *
        * Parameters:
        * grpNO - {number} The index of the groupd in the layerGroups array
        *
        * Returns:
        * {string} The displayName of the layerGroup
        */

        return this.layerGroups[grpNO].displayName;
    },

    getLayersProperties: function(lyrGrp) {
        /**
        * Function: getLayersProperties
        * To return all the set properties for all layers in a layerGroup, used for debugging.
        *
        * Parameters:
        * lyrGrp - {number} The index of the groupd in the layerGroups array
        *
        * Returns:
        * {string} Concatenated string of all the properties
        */
        var strTxt = '';
        strTxt = " Layer Group displayName: " + this.layerGroups[lyrGrp].displayName + " \n ";
        var aj = 0;
        for (aj = 0; aj < this.layerGroups[lyrGrp].layers.length; aj++) {
            strTxt += "\n" + this.layerGroups[lyrGrp].layers[aj].layerName;
            strTxt += "\n" + this.layerGroups[lyrGrp].layers[aj].displayName;
            strTxt += "\n" + this.layerGroups[lyrGrp].layers[aj].initiallyVisible;
            strTxt += "\n" + this.layerGroups[lyrGrp].layers[aj].currentlyVisible;
        }
        return strTxt;
    },

    addLayerGroup: function(group) {
        /**
        * Function: addLayerGroup
        * Add a layerGroup to the layerControl object.
        *
        * Parameters:
        * group - {object} A layerGroup
        *
        * Returns:
        *
        */
        var newGroup = new LayerGroup(group.displayName, group.guid);
        if (group.themeFields) {
            newGroup.themeFields = group.themeFields;
        }
        if (group.zoomLevels) {
            newGroup.zoomLevels = group.zoomLevels;
        }
        var addLayer = this.addLayer.bind(this);
        newGroup.addLayer = addLayer.methodize();
        newGroup.mapWrapper = this.mapWrapper;
        if (group.layers.length) {
            var layers = $A(group.layers);
            for (var i = 0; i < layers.length; i++) {
                newGroup.addLayer(layers[i]);
                //newGroup.layers.push(layers[i]) ; // prak 26/9
                layers[i].currentlyVisible = false;
                if (layers[i].initiallyVisible) {
                    layers[i].show();
                }
            }
        }
        this.layerGroups.push(newGroup);
    },


    addLayer: function(group, layer) {
        /**
        * Function: addLayer
        * Add a layer to a layerGroup object.
        *
        * Parameters:
        * group - {object} A layerGroup
        * layer - {object} A layer
        *
        * Returns:
        *
        */
        layer.group = group;
        layer.show = function() {
            if (this.active && !this.currentlyVisible) {
                switch (this.type) {
                    case 'base':
                        this.group.mapWrapper.addISMLayer(this.layerName);
                        if (!this.group.mapWrapper.ismDataLayer.visibility) {
                            this.group.mapWrapper.ismDataLayer.setVisibility(true);
                        }
                        else {
                            this.group.mapWrapper.draw();
                        }
                        this.group.activeShown++;
                        this.currentlyVisible = true;
                        this.group.mapWrapper.mapElement.fire('astun:saveSetting', { setting: 'visibleLayers', value: '' + this.group.mapWrapper.ismDataLayer.params.layers });
                        this.group.mapWrapper.mapElement.fire('astun:layerShow', { layer: this });
                        break;
                    case 'thematic':
                        // do nothing - at the moment only toggle is available
                        break;
                    default:
                        break;
                }
            }
        };

        layer.hide = function() {
            /**
            * Function: hide, function of the layer object
            * Turn off the visibility of a layer.
            *
            * Parameters:
            *
            * Returns:
            *
            */
            if (this.active && this.currentlyVisible) {
                switch (this.type) {
                    case 'base':
                        this.group.mapWrapper.removeISMLayer(this.layerName);
                        if (this.group.mapWrapper.ismDataLayer.params.layers.length) {
                            this.group.mapWrapper.draw();
                        }
                        else {
                            this.group.mapWrapper.ismDataLayer.setVisibility(false);
                        }
                        this.group.activeShown--;
                        this.currentlyVisible = false;
                        this.group.mapWrapper.mapElement.fire('astun:saveSetting', {
                            setting: 'visibleLayers',
                            value: '' + this.group.mapWrapper.ismDataLayer.params.layers
                        });
                        this.group.mapWrapper.mapElement.fire("astun:layerHide", {
                            layer: this
                        });
                    case 'thematic':
                        // do nothing - at the moment only toggle is available
                        break;
                    default:
                        break;
                }
            }
        };

        layer.toggle = function() {
            /**
            * Function: toggle, function of the layer object
            * Turn off the visibility of a layer if layer currently visible or ON if not currently visible.
            *
            * Parameters:
            *
            * Returns:
            *
            */
            if (this.toggleThematic) {
                this.toggleThematic();
            }
            else if (this.currentlyVisible) {
                this.hide();
            }
            else {
                this.show();
            }
        }
        group.layers.push(layer);
        if (layer.active) {
            switch (layer.type) {
                case 'base':
                    group.activeLayers.push(layer);
                    break;
                case 'thematic':
                    group.thematicLayers.push(layer);
                    var toggler = this.toggleThematic.bind(this);
                    layer.toggleThematic = toggler.curry(layer.layerName, layer.layerName); //NOTE: remove second argument when events not handled directly by toggleThematic
                    var zoomer = this.zoomThematic.bind(this);
                    layer.zoomThematic = zoomer.curry(layer.layerName, layer.layerName); //NOTE: remove second argument when events not handled directly by toggleThematic
                    var panner = this.panThematic.bind(this);
                    layer.panThematic = panner.curry(layer.layerName, layer.layerName); //NOTE: remove second argument when events not handled directly by toggleThematic
                    var reloader = this.reloadThematic.bind(this)
                    layer.reload = reloader.methodize();

                    break;
            }

        }
    },

    importLayerGroups: function(arlayerGroupsData) {
        /**
        * Function: importLayerGroups, 
        * Creates layerGroups on the layerControl object from a Json string
        *
        * Parameters:
        * arlayerGroupsData - {object} Json object
        *
        * Returns:
        *
        */
        for (var i = 0; i < arlayerGroupsData.length; i++) {
            this.addLayerGroup(arlayerGroupsData[i]);
        }
        var loadVisibleLayers = function(layersString) {
            if (!!layersString) {
                var layerArray = layersString.split(',');
                this.showLayers(layerArray);
            }
        };
        if (this.urlLayers) {
            this.showLayers(this.urlLayers);
        }
        else {
            this.mapWrapper.mapElement.fire('astun:loadSetting', { setting: 'visibleLayers', loadFunction: loadVisibleLayers.bind(this) });
        }

        var loadVisibleThematic = function(thematicName) {
            if (!!thematicName && !!this.findLayer(thematicName)) {
                this.toggleThematic(thematicName, thematicName); //toggleThematic needs to be fixed to rely only on string passed as first argument
            }
            else if (this.thematicInfo.force) {
                for (var groupId = 0, groupLimit = this.layerGroups.length; groupId < groupLimit; ++groupId) {
                    var groupObject = this.layerGroups[groupId];
                    if (groupObject.thematicLayers.length) {
                        var thematicName = groupObject.thematicLayers[0].layerName;
                        this.toggleThematic(thematicName, thematicName); //toggleThematic needs to be fixed to rely only on string passed as first argument
                        //this.mapWrapper.mapElement.fire('astun:themeLoadComplete', {});
                    }
                }
            }
        };
        if (!(this.thematicInfo.force && this.thematicInfo.force == 'off')) {
            this.mapWrapper.mapElement.fire('astun:loadSetting', { setting: 'visibleThematic', loadFunction: loadVisibleThematic.bind(this) });
        }
        this.mapWrapper.mapElement.fire("astun:sourceLoad", { layerControl: this });
    },


    findLayer: function(lyrName) {
        /**
        * Function: findLayer, 
        * Returns a layer Object whose propery 'layerName' equal passed string or null if none found.
        *
        * Parameters:
        * lyrName - {string} String to match 'layerName'
        *
        * Returns:
        * {object} - Layer object or null.
        *
        */
        for (var i = 0; i < this.layerGroups.length; i++) {
            for (var j = 0; j < this.layerGroups[i].layers.length; j++) {
                if (this.layerGroups[i].layers[j].layerName == lyrName) {
                    return this.layerGroups[i].layers[j];
                }
            }
        }
        return null;
    },

    hideLayer: function(lyrName) {
        /**
        * Function: hideLayer, 
        * Turn off the visiblity of a layer on the map display. Method of the layerControl object.
        *
        * Parameters:
        * lyrName - {string} String to match 'layerName' of layer
        *
        * Returns:
        *
        */
        var lyr = this.findLayer(lyrName)
        if (lyr) {
            lyr.hide();
        }
    },

    showLayer: function(lyrName) {
        /**
        * Function: hideLayer, 
        * Turn On the visiblity of a layer on the map display. Method of the layerControl object.
        *
        * Parameters:
        * lyrName - {string} String to match 'layerName' of layer
        *
        * Returns:
        *
        */
        var lyr = this.findLayer(lyrName)
        if (lyr) {
            lyr.show();
        }
    },

    showLayers: function(lyrNameArray) {
        /**
        * Function: showLayers, 
        * Turn On the visiblity of all layer on the map display for a given array of layerNames.
        *
        * Parameters:
        * lyrNameArray - {Array} of layer names
        *
        * Returns:
        *
        */
        var tmpArray = lyrNameArray.slice(0);
        while (tmpArray.length) {
            this.showLayer(tmpArray.shift())
        }
    },

    findLayerGroup: function(layerGroupName) {
        /**
        * Function: findLayerGroup, 
        * Return a layerGroup object with a matching displayName, null if not found
        *
        * Parameters:
        * layerGroupName - {string} Matching displayName of grooup to be returned
        *
        * Returns:
        * {object} - layerGroup object
        */
        for (var i = 0; i < this.layerGroups.length; i++) {
            if (this.layerGroups[i].displayName == layerGroupName) {
                bLyrFound = true;
                return this.layerGroups[i];
            }
        }
        return;
    },


    showLayerGroup: function(layerGroupName) {
        /**
        * Function: showLayerGroup, 
        * Turns ON map visibility of all layers within a layerGroup with matching display name.
        *
        * Parameters:
        * layerGroupName - {string} Matching displayName of grooup to be returned
        *
        * Returns:
        */
        var layerGroup = this.findLayerGroup(layerGroupName);
        layerGroup.showLayers();
        //$(this.group.mapWrapper.mapElement).fire("astun:showLayerGroup", {layerGroup: this});
    },

    hideLayerGroup: function(layerGroupName) {
        /**
        * Function: hideLayerGroup, 
        * Turns OFF map visibility of all layers within a layerGroup with matching display name.
        *
        * Parameters:
        * layerGroupName - {string} Matching displayName of grooup to be returned
        *
        * Returns:
        */
        var layerGroup = this.findLayerGroup(layerGroupName);
        layerGroup.hideLayers();
        //$(this.group.mapWrapper.mapElement).fire("astun:showLayerGroup", {layerGroup: this});
    },


    reloadThematic: function(oLayer) {
        /**
        * Function: reloadThematic, 
        * Reloads the thematic layer corresponding to the layer with theme defined.
        *
        * Parameters:
        * oLayer - {object} layer object with theme
        *
        * Returns:
        */
        //return if already theme being processed.		
        if (this.thematicInfo.themeBeingProcessed) {
            return;
        }
        //indicate a theme being processed
        this.thematicInfo.themeBeingProcessed = true;
        this.mapWrapper.mapElement.fire('astun:themeLoadBegin', {});
        var oThematicMapLayer = this.findThematicMapLayer();
        this.mapWrapper.map.removeLayer(oThematicMapLayer);
        this.createThematicLayer(oLayer);
        var atGeomPanel = $('atGeomPanel');
        this.setZoomLevelKey(oLayer)

        this.mapWrapper.clearPopups();
        //this.getThematicRules(oThematicMapLayer, oLayer);
        //this.thematicLayerFinished(oThematicMapLayer,oLayer,true)
    },
    panThematic: function(lyrName) {
        /**
        * Function: panThematic, 
        * Reloads the thematic layer corresponding to the appropriate bbox.
        *
        * Parameters:
        * oLayer - {object} layer object with theme
        *
        * Returns:
        */
        //alert(lyrName);
        //this.findLayer(lyrName).reload()
        var thisLayer = this.findLayer(lyrName);
        var thisZoom = this.mapWrapper.map.zoom;
        var thisDataLevel = 'all';
        var zoomLevels = thisLayer.group.zoomLevels;
        for (i = 0; i < zoomLevels.length; i++) {
            for (j = 0; j < zoomLevels[i].level.length; j++) {
                if (zoomLevels[i].level[j] == thisZoom) {
                    thisDataLevel = zoomLevels[i].data;
                    break;
                }
            }
        }
        if (thisDataLevel == 'bbox') {

            //this.mapWrapper.mapElement.fire('astun:themeLoadBegin', {});	
            var oThematicMapLayer = this.findThematicMapLayer();
            if (oThematicMapLayer == null) {
                return;
            }

            //return if already theme being processed.		
            //if ( this.thematicInfo.panning ) {
            //	return;
            //}
            //indicate a theme being processed
            //this.thematicInfo.themeBeingProcessed = true ;	
            this.thematicInfo.panning = true;
            //this.mapWrapper.mapElement.fire('astun:themeLoadBegin', {});
            //this.mapWrapper.map.removeLayer(oThematicMapLayer);

            // TODO: FT get strGeometrySource based on Zoom Level
            strGeometrySource = thisLayer.thematic.geometrySource;

            var oJSon = this.getGeoJSONForBBOXAjax(thisLayer);

            //update current datasource being looked at 
            this.thematicInfo.geometrySource = strGeometrySource //oLayer.thematic.geometrySource ;


        }

        //toggleThematic('Burglary@Local Authority');
    },

    zoomThematic: function(lyrName) {
        /**
        * Function: zoomThematic, 
        * Reloads the thematic layer corresponding to the appropriate zoom level.
        *
        * Parameters:
        * oLayer - {object} layer object with theme
        *
        * Returns:
        */
        //alert(lyrName);
        //this.findLayer(lyrName).reload()
        var thisLayer = this.findLayer(lyrName);
        var thisZoom = this.mapWrapper.map.zoom;
        var thisGeometry = thisLayer.thematic.geometrySource;
        var newGeometry = thisGeometry;
        var zoomLevels = thisLayer.group.zoomLevels;
        for (i = 0; i < zoomLevels.length; i++) {
            for (j = 0; j < zoomLevels[i].level.length; j++) {
                if (zoomLevels[i].level[j] == thisZoom) {
                    newGeometry = zoomLevels[i].geometrySource;
                    break;
                }
            }
        }
        if (newGeometry != thisGeometry) {
            thisLayer.thematic.geometrySource = newGeometry;
            thisLayer.thematic.ranges.initialised = false;
            this.findLayer(lyrName).reload()
        }
        //.thematic.geometrySource ;

        //toggleThematic('Burglary@Local Authority');
    },

    toggleThematic: function(lyrName) {
        /**
        * Function: toggleThematic 
        * Turns ON map visibility of the theme for a layer with matching name if not already visible
        * Or Turns OFF map visibility of the theme for a layer with matching name if already visible
        *
        * Parameters:
        * lyrName - {string} layerName for layer object
        *
        * Returns:
        */
        //return if already theme being processed.		
        if (this.thematicInfo.themeBeingProcessed) {
            return;
        }

        var sCutOffDate = this.mapWrapper.date;
        var iMonth = sCutOffDate.substring(5, 7) - 1;
        this.cutOffDate.setFullYear(sCutOffDate.substring(0, 4), iMonth, sCutOffDate.substring(8, 10));

        var data = $A(arguments);
        lyrName = data[1];

        var oLayer = this.findLayer(lyrName);
        var oThematicMapLayer = this.findThematicMapLayer();

        //indicate a theme being processed
        this.thematicInfo.themeBeingProcessed = true;
        this.mapWrapper.mapElement.fire('astun:themeLoadBegin', {});

        if (!this.mapWrapper.map.thematic) {
            this.mapWrapper.map.thematic = {};
        }

        this.mapWrapper.map.thematic.homeEn = new OpenLayers.LonLat(this.mapWrapper.currentLocation.x, this.mapWrapper.currentLocation.y);

        oLayer.thematic.geometrySource = this.getGeometryForZoom(oLayer) //oLayer.thematic.zoomLevels[this.mapWrapper.map.zoom].geometrySource;

        if (!oThematicMapLayer) {
            this.createThematicLayer(oLayer);
            return;
        }

        //Themaric layer present, clear any popup and clear any graph displayed
        this.mapWrapper.clearPopups();
        //$('homeWard').innerHTML = '';
        //$('hoverWard').innerHTML = '';	
        if (this.thematicInfo.layerName === oLayer.layerName && this.thematicInfo.columnName === oLayer.thematic.themeFields[oLayer.group.themeFieldsIndex]) {
            var bLayerVisible = false;
            bLayerVisible = oThematicMapLayer.visibility;
            if (bLayerVisible) {
                this.thematicLayerFinished(oThematicMapLayer, oLayer, false);
            }
            else {
                this.thematicLayerFinished(oThematicMapLayer, oLayer, true);
                //if (this.thematicInfo.homeFeature) {
                //	this.mapWrapper.interactions.showHomeWardGraph(this.thematicInfo);
                //}
            }
        }
        else {
            // FT - do this when zooming in or out based on zoom level geometty source.
            if (this.thematicInfo.geometrySource == oLayer.thematic.geometrySource)
            //if ( this.thematicInfo.geometrySource == this.getGeometryForZoom(oLayer) )
            {
                this.getThematicRules(oThematicMapLayer, oLayer);
                //this.thematicLayerFinished(oThematicMapLayer,oLayer,true);
            }
            else {
                this.mapWrapper.map.removeLayer(oThematicMapLayer);
                this.createThematicLayer(oLayer);
                //this.thematicLayerFinished(oThematicMapLayer,oLayer,false);
            }
        }

    },

    findThematicMapLayer: function() {
        /**
        * Function: findThematicMapLayer 
        * search and retrun the thematic layer, if any
        *
        * Parameters:
        * 
        *
        * Returns:
        * {object) - the thematic layer or null
        */
        for (i = 0; i < this.mapWrapper.map.layers.length; i++) {
            if (this.mapWrapper.map.layers[i].name == 'thematicLayer')
                return this.mapWrapper.map.layers[i];
        }
        return null;
    },

    createThematicLayer: function(oLayer) {
        /**
        * Function: createThematicLayer 
        * Creates and show on map the thematic layer for a given layer object
        *
        * Parameters:
        * oLayer - {object} layer object
        *
        * Returns:
        */
        // TODO: FT get strGeometrySource based on Zoom Level
        strGeometrySource = oLayer.thematic.geometrySource;

        //strGeometrySource = this.getGeometryForZoom(oLayer);

        var oJSon = this.geometrySources.get(strGeometrySource);

        if (!oJSon) {
            oJSon = this.getGeoJSONFromAjax(oLayer);

            //var boundingBox = this.thematicInfo.oThematicLayer.getDataExtent();
            //this.map.zoomToExtent(boundingBox, false);
            return;
        }
        else {
            //update current datasource being looked at 
            this.thematicInfo.geometrySource = strGeometrySource //oLayer.thematic.geometrySource ;
            var geojson = new OpenLayers.Format.GeoJSON();
            var oGeoJSON = geojson.read(oJSon, "FeatureCollection");
            this.generateThematicLayer(oLayer, oGeoJSON);
        }
    },

    getThematicRules: function(oThematicMapLayer, oLayer) {
        /**
        * Function: getThematicRules 
        * Gets the thematic rules data
        *
        * Parameters:
        * oThematicMapLayer - {object} map thematic layer object
        * oLayer - {object} layer object	
        *
        * Returns:
        */
        //Initialise the rules object and destroy any previous ones
        oThematicMapLayer.styleMap = this.defaultStyleMap;
        // If auto = true get the ranges values thru AJAX call 
        if ((!oLayer.thematic.ranges.initialised) && oLayer.thematic.ranges.auto) {
            this.getThematicRangesFromAjax(oThematicMapLayer, oLayer);
            return;
        }
        else {
            this.applyThematicRules(oThematicMapLayer, oLayer);
        }
    },

    applyThematicRules: function(oThematicMapLayer, oLayer) {
        /**
        * Function: applyThematicRules 
        * Creates the rules on the map thematic layer from the object theme rules data
        *
        * Parameters:
        * oThematicMapLayer - {object} map thematic layer object
        * oLayer - {object} layer object	
        *
        * Returns:
        */
        var sRuleName = '';
        var oRuleColour = {};
        var iLower = 0;
        var iUpper = 0;
        var oRules = [];
        var sPropertyToCompare = 'currentValue';
        // only do this if oThematicMapLayer.styleMap.styles.default.rules.length=0
        for (i = 0; i < oLayer.thematic.ranges.range.length; i++) {
            //rules name, values and colour
            sRuleName = 'ruleNo' + i;
            oRuleColour = oLayer.thematic.ranges.range[i].colour;
            iLower = oLayer.thematic.ranges.range[i].low;
            iUpper = oLayer.thematic.ranges.range[i].high;
            var sRuleName = new OpenLayers.Rule(
			{
			    name: oLayer.thematic.ranges.range[i].label,
			    filter: new OpenLayers.Filter.Comparison(
					{
					    type: OpenLayers.Filter.Comparison.BETWEEN,
					    property: sPropertyToCompare,
					    lowerBoundary: iLower,
					    upperBoundary: iUpper
					}),
			    symbolizer: { Polygon: { fillColor: oRuleColour,
			        fillOpacity: 0.6, strokeColor: "black"
			    }
			    }

			});
            oRules[i] = sRuleName;
        }
        var oThematicStyle = new OpenLayers.Style();
        oThematicStyle.addRules(oRules);
        oThematicMapLayer.styleMap = new OpenLayers.StyleMap({ "default": oThematicStyle });

        //Set the currentValue value to -1 to disable any theme 
        for (i = 0; i < oThematicMapLayer.features.length; i++) {
            oThematicMapLayer.features[i].attributes.currentValue = -1;
        }
        //stores thematic info in current object		
        this.thematicInfo.layerName = oLayer.layerName;
        this.thematicInfo.columnName = oLayer.thematic.themeFields[oLayer.group.themeFieldsIndex];
        this.thematicInfo.oLayer = oLayer;
        this.thematicInfo.title = oLayer.thematic.legendTitle;
        this.mapWrapper.map.thematic.title = oLayer.thematic.legendTitle;
        this.getThematicLayerCurrentValueData(oThematicMapLayer, oLayer);
    },


    getThematicLayerCurrentValueData: function(oThematicMapLayer, oLayer) {
        /**
        * Function: getThematicLayerCurrentValueData 
        * Fetch the data for to be used for the application of the theme
        *
        * Parameters:
        * oThematicMapLayer - {object} map thematic layer object
        * oLayer - {object} layer object	
        *
        * Returns:
        */

        sThemeDataSource = oLayer.thematic.dataSource.profile + '.' + oLayer.thematic.dataSource.dimension + '@' + oLayer.thematic.geometrySource //oLayer.thematic.dataSource.name;

        //keep in map object to be subsequently used by popup to show map
        this.mapWrapper.map.thematic.dataSourceName = sThemeDataSource;
        //this.getThemeDataFromAjaxOLD(oThematicMapLayer,oLayer,true);
        //return;
        var oJsonThemeVal = this.themeDataSources.get(sThemeDataSource);

        if (!oJsonThemeVal) {
            oJsonThemeVal = this.getThemeDataFromAjax(oThematicMapLayer, oLayer, true);
            return;
        }
        else {
            var arData = oJsonThemeVal.data;
            var iDataColumnIndex = oJsonThemeVal.columns.indexOf(oLayer.thematic.themeFields[oLayer.group.themeFieldsIndex]);
            this.mapWrapper.map.thematic.columnName = oLayer.thematic.themeFields[oLayer.group.themeFieldsIndex];
            this.mapWrapper.map.thematic.themeField = oLayer.group.themeFields[oLayer.group.themeFieldsIndex];
            var iWardIndex = oJsonThemeVal.columns.indexOf('Region');
            var iDistrictIndex = oJsonThemeVal.columns.indexOf('Parent');
            var currentMonth = null;
            this.fillThematicLayerWithValues(oThematicMapLayer, arData, iDataColumnIndex, iWardIndex, iDistrictIndex, currentMonth, oLayer);
        }

        //$('hoverWard').innerHTML =='' -- used for hover panels below map
        //after last call to function
        this.mapWrapper.mapElement.fire('astun:thematicLoaded', oLayer);


    },

    getThemeDataFromAjaxTEST: function(oThematicMapLayer, oLayer, currentMonth) {
        /**
        * Function: getThemeDataFromAjax 
        * Fetches the data for to be used for the application of the theme by making an Ajax call
        * First it gets for current month and then calls itself to get for previous month	
        *
        * Parameters:
        * oThematicMapLayer - {object} map thematic layer object
        * oLayer - {object} layer object	
        * currentMonth - {bool} true/false flag
        *
        * Returns:
        */

        var successFunc = function(transport) {
            try {
                var oJsonThemeVal = transport.responseText.evalJSON(true);
                var oLayer = this.thematicInfo.oLayer;
                this.themeDataSources.set(oLayer.thematic.dataSource.profile + '.' + oLayer.thematic.dataSource.dimension + '@' + oLayer.thematic.geometrySource, oJsonThemeVal);
                return oJsonThemeVal;

            }
            catch (parsingError) {
                if (transport.status != 0) { // i.e. if not fail because interrupted
                    alert('Error: Failed  to get Theme data for comparison');
                }
            }
        }; //onSuccess
        var options = {
            method: 'get',
            parameters: null,
            onFailure: function(transport) {
                if (transport.status != 0) { // i.e. if not fail because interrupted
                    alert('Error: Failed  to get Theme data for comparison');
                }
                this.mapWrapper.mapElement.fire('astun:themeLoadComplete', {});
            },
            onSuccess: successFunc.bindAsEventListener(this)
        };


        var myURL = this.mapWrapper.dataurl;
        myURL += '?requesttype=json&Type=data&gs=';
        myURL += oLayer.thematic.geometrySource;
        myUrl += "&ms=" + oLayer.thematic.dataSource.type + '/' + oLayer.thematic.dataSource.profile;
        myURL += "&pd=" + oLayer.thematic.dataSource.dimension;


        var dateCriteria = '';
        //builds the Ajax call 

        new Ajax.Request
		(myURL,
			options
		);
    },

    getThemeDataFromAjax: function(oThematicMapLayer, oLayer, currentMonth) {
        /**
        * Function: getThemeDataFromAjax 
        * Fetches the data for to be used for the application of the theme by making an Ajax call
        * First it gets for current month and then calls itself to get for previous month	
        *
        * Parameters:
        * oThematicMapLayer - {object} map thematic layer object
        * oLayer - {object} layer object	
        * currentMonth - {bool} true/false flag
        *
        * Returns:
        */
        var pointInPolygon = function(oPolygon, en) {
            /**
            * Function: pointInPolygon 
            * Checks if a point is in a given geometry boundaries.
            *
            * Parameters:
            * oPolygon - {object} String format of a Polygon geometry 
            * en - {object) OpenLayers.LonLat object
            *
            * Returns:
            * {bool} - true if point is in specified polygon geometry
            */

            var x = en.lon;
            var y = en.lat;
            var _tmp = oPolygon.substring(9, oPolygon.length - 2).split(","); //point list
            var polySides = _tmp.length - 1;
            var polyX = Array();
            var polyY = Array();
            for (var k = 0; k < polySides; k++) {
                var xy = _tmp[k].split(" ");
                polyX.push(parseFloat(xy[0]));
                polyY.push(parseFloat(xy[1]));
            }
            var i, j;
            var oddNodes = false;
            for (var i = 0, j = polySides - 1; i < polySides; j = i++) {
                val = polyX[i] + (y - polyY[i]) / (polyY[j] - polyY[i]) * (polyX[j] - polyX[i]);
                if (polyY[i] < y && polyY[j] >= y || polyY[j] < y && polyY[i] >= y) {
                    if (polyX[i] + (y - polyY[i]) / (polyY[j] - polyY[i]) * (polyX[j] - polyX[i]) < x) {
                        oddNodes = !oddNodes;
                    }
                }
            }
            return oddNodes;
        };
        var successFunc = function(transport) {
            try {
                var oJsonThemeVal = transport.responseText.evalJSON(true);
                var oLayer = this.thematicInfo.oLayer;
                var oThematicMapLayer = this.thematicInfo.oThematicLayer;
                //TODO: FT this.themeDataSources.set(oLayer.thematic.dataSource.name+'GEOMETRY',oJsonThemeVal);
                this.themeDataSources.set(oLayer.thematic.dataSource.profile + '.' + oLayer.thematic.dataSource.dimension + '@' + oLayer.thematic.geometrySource, oJsonThemeVal);
                // FT put this in seperate function applyData(requires new features as array)
                var arData = oJsonThemeVal.data;
                var iDataColumnIndex = oJsonThemeVal.columns.indexOf(oLayer.thematic.themeFields[oLayer.group.themeFieldsIndex]);
                this.mapWrapper.map.thematic.columnName = oLayer.thematic.themeFields[oLayer.group.themeFieldsIndex];
                this.mapWrapper.map.thematic.themeField = oLayer.group.themeFields[oLayer.group.themeFieldsIndex];
                var iWardIndex = oJsonThemeVal.columns.indexOf('Region');
                var iDistrictIndex = oJsonThemeVal.columns.indexOf('Parent');

                this.fillThematicLayerWithValues(oThematicMapLayer, arData, iDataColumnIndex, iWardIndex, iDistrictIndex, currentMonth, oLayer);

                /*
                if ( this.mapWrapper.currentLocation )
                {
                var location =  new OpenLayers.LonLat(this.mapWrapper.currentLocation.x, this.mapWrapper.currentLocation.y);
                //determine the polygon feature containing point								
                var fout = Array();
                // FT only do features that have been retrieved.
                for ( var i=0; i<oThematicMapLayer.features.length; i++)
                {
                var the_geom = oThematicMapLayer.features[i].geometry.toString();
                if (the_geom.indexOf('POLYGON') != -1) 
                {
                if (pointInPolygon(the_geom, location)) 
                {
                fout.push(oThematicMapLayer.features[i]);
                }								
                }
                }//for
					
					for (var k = 0; k < fout.length; k++) 
                {
                var feature = fout[k];
                var layer = feature.layer;
                var featureName = feature.attributes.name;
                this.toggleHomeFeature(featureName);
                }				

				}			
				//$('hoverWard').innerHTML =='' -- used for hover panels below map
                //after last call to function
                this.mapWrapper.mapElement.fire('astun:thematicLoaded', oLayer);	   
                */

            }
            catch (parsingError) {
                if (transport.status != 0) { // i.e. if not fail because interrupted
                    alert('Error: Failed to process Theme data for comparison');
                }
            }
        }; //onSuccess
        var options = {
            method: 'get',
            parameters: null,
            onFailure: function(transport) {
                if (transport.status != 0) { // i.e. if not fail because interrupted
                    alert('Error: Failed  to get Theme data for comparison');
                }
                this.mapWrapper.mapElement.fire('astun:themeLoadComplete', {});
            },
            onSuccess: successFunc.bindAsEventListener(this)
        };


        var myURL = this.mapWrapper.dataurl
        myURL += '?requesttype=json&Type=data&gs=';
        myURL += oLayer.thematic.geometrySource;
        myURL += "&ms=" + oLayer.thematic.dataSource.type + '/' + oLayer.thematic.dataSource.profile;
        myURL += "&pd=" + oLayer.thematic.dataSource.dimension;

        new Ajax.Request
		(myURL,
			options
		);
    },

    // FT - this needs to accept the array of new features
    fillThematicLayerWithValues: function(oThematicMapLayer, arData, iDataColumnIndex, iWardIndex, iDistrictIndex, currentMonth, oLayer) {
        /**
        * Function: fillThematicLayerWithValues 
        * Fill the 'currentMonth' or 'previousMonth' property for each feature of the thematic layer
        * from data obtained from the Ajax call.
        *
        * Parameters:
        * oThematicMapLayer - {object} map thematic layer object
        * arData - {Array} Array of data
        * iDataColumnIndex - {number} the index in data array to be used
        * iWardIndex - {number} the index in data array that contains ward name
        * iDistrictIndex - {number} the index in data array that contains district name
        * currentMonth - {bool} true - current month/false - previous month
        *
        * Returns:
        */

        var sFeatureName = '';
        var tempVal = 0;
        var arTemp = {};

        for (var i = 0; i < oThematicMapLayer.features.length; i++) {
            sFeatureName = oThematicMapLayer.features[i].attributes.name;
            sFeatureCode = oThematicMapLayer.features[i].attributes.code;
            for (var j = 0; j < arData.length; j++) {
                //if ( ( arData[j][iWardIndex]+ '@' + arData[j][iDistrictIndex] ) == sFeatureName )
                if (arData[j][0] == sFeatureCode) {
                    arTemp = arData[j];
                    tempVal = arTemp[iDataColumnIndex];

                    oThematicMapLayer.features[i].attributes.currentValue = tempVal;
                    //oThematicMapLayer.features[i].attributes.previousValue=0 //arTemp[iDataColumnIndex+1];

                    break;

                }
            }
        }

        oThematicMapLayer.redraw();
        this.thematicInfo.themeBeingProcessed = false;
        this.thematicInfo.panning = false;
        this.setZoomLevelKey(oLayer);

        this.thematicLayerFinished(oThematicMapLayer, oLayer, true);
    },



    getThematicRangesFromAjax: function(oThematicMapLayer, oLayer) {
        /**
        * Function: getThematicRangesFromAjax 
        * Fetch the thematic ranges for rules to be used for theme appication
        *
        * Parameters:
        * oThematicMapLayer - {object} map thematic layer object
        * oLayer - {object} map layer object
        *
        * Returns:
        */
        var successFunc = function(transport) {
            var oJsonTheme = transport.responseText.evalJSON();
            var iLow = oJsonTheme.columns.indexOf('low');
            var iHigh = oJsonTheme.columns.indexOf('high');
            var iColour = oJsonTheme.columns.indexOf('colour');
            var iLabel = oJsonTheme.columns.indexOf('label');
            var iLabelRange = oJsonTheme.columns.indexOf('labelrange');
            oLayer.thematic.ranges.range.clear();
            oLayer.thematic.ranges.count = oJsonTheme.data.length;
            // FT create a new range based on the JSON and add it to the oLayer.thematic.ranges
            for (i = 0; i < oJsonTheme.data.length; i++) {
                var thisrange = new Hash();
                thisrange.set('low', oJsonTheme.data[i][iLow]);
                thisrange.set('high', oJsonTheme.data[i][iHigh]);
                thisrange.set('colour', oJsonTheme.data[i][iColour]);
                thisrange.set('label', oJsonTheme.data[i][iLabel]);
                thisrange.set('labelrange', oJsonTheme.data[i][iLabelRange]);
                oLayer.thematic.ranges.range.push(thisrange.toObject());

            }

            // FT set flat that thematic values initialised for this layer
            oLayer.thematic.ranges.initialised = true;
            this.applyThematicRules(oThematicMapLayer, oLayer);

            try {

            }
            catch (parsingError) {
                if (transport.status != 0) { // i.e. if not fail because interrupted
                    alert('Error: Failed to get Theme comparison ranges');
                }
                alert('Error: Failed to process Theme comparison ranges');
            }
        }; //onSuccess
        var options = {
            method: 'get',
            parameters: null,
            onFailure: function(transport) {
                if (transport.status != 0) { // i.e. if not fail because interrupted
                    alert('Error: Failed to get Theme comparison ranges');
                }
            },
            onSuccess: successFunc.bindAsEventListener(this)
        };
        // FT construct and call AJAX to get JSON for rules
        sColumn = oLayer.thematic.themeFields[oLayer.group.themeFieldsIndex];
        var myURL = this.mapWrapper.dataurl
        myURL += '?requesttype=json&Type=ThematicClass&docount=0&gs=';
        myURL += oLayer.thematic.geometrySource;
        myURL += "&ms=" + oLayer.thematic.dataSource.type + '/' + oLayer.thematic.dataSource.profile;
        myURL += "&pd=" + oLayer.thematic.dataSource.dimension + '&field=' + sColumn;

        new Ajax.Request(myURL, options);
    },

    // FT need to get GeoJSON for bbox
    getGeoJSONForBBOXAjax: function(oLayer) {
        /**
        * Function: getGeoJSONForBBOXAjax 
        * Fetch the geometries data for the current bbox
        *
        * Parameters:
        * oLayer - {object} map layer object
        *
        * Returns:
        */
        var onSuccessFunc = function(transport) {
            var featureExists = function(find, source) {
                var rtn = false;
                for (var f = 0; f < source.features.length; f++) {
                    if (find == source.features[f].properties.code) {
                        rtn = true; break;
                    }
                }
                return rtn;
            }
            var oJson = transport.responseText.evalJSON();
            var geojson = new OpenLayers.Format.GeoJSON();
            this.thematicInfo.geometrySource = oLayer.thematic.geometrySource;

            //remove already loaded json
            var existingG = this.geometrySources.get(oLayer.thematic.geometrySource);
            if (existingG) {
                //
                var newFeatures = [];
                var newCount = 0;
                for (var i = 0; i < oJson.features.length; i++) {
                    var foundthis = featureExists(oJson.features[i].properties.code, existingG);
                    if (!foundthis) {
                        newFeatures[newCount] = oJson.features[i];
                        newCount++;
                    }
                }
                if (newFeatures.length > 0) {
                    newCount = existingG.features.length;
                    for (var j = 0; j < newFeatures.length; j++) {
                        existingG.features[newCount] = newFeatures[j];
                        newCount++;
                    }
                    oJson.features = newFeatures;
                    var oGeoJSON = geojson.read(oJson, "FeatureCollection");
                    this.generateThematicLayer(oLayer, oGeoJSON);
                }
                else {
                    this.mapWrapper.mapElement.fire('astun:themeLoadComplete', {});
                }
            }
            else {
                this.geometrySources.set(oLayer.thematic.geometrySource, oJson);
                var oGeoJSON = geojson.read(oJson, "FeatureCollection");
                this.generateThematicLayer(oLayer, oGeoJSON);
            }

            try {


            }
            catch (parsingError) {
                //this.thematicInfo.themeBeingProcessed = false;
                if (transport.status != 0) { // i.e. if not fail because interrupted
                    alert('Error: Failed to get data for boundaries.');
                }
                alert('Error: Failed to process data for boundaries.');
            }
        };
        var options =
		{
		    method: 'get',
		    parameters: null,
		    onFailure: function(transport) {
		        if (transport.status != 0) { // i.e. if not fail because interrupted
		            alert('AJAX Error: Failed to get data for boundaries');
		        }
		    },
		    onSuccess: onSuccessFunc.bindAsEventListener(this)
		};

        var myURL = this.mapWrapper.dataurl
        var bounds = this.mapWrapper.map.getExtent();
        myURL += '?requesttype=GeoJson&gs=';
        myURL += oLayer.thematic.geometrySource;
        myURL += "&ms=" + this.mapWrapper.mapsource;
        myURL += "&bbox=" + bounds.toBBOX();
        myURL += "&zoom=" + this.mapWrapper.map.zoom;
        new Ajax.Request
				(myURL,
					options
				);
    },

    getGeoJSONFromAjax: function(oLayer) {
        /**
        * Function: getGeoJSONFromAjax 
        * Fetch the geometries data for the creation of features on thematic vector layer
        *
        * Parameters:
        * oLayer - {object} map layer object
        *
        * Returns:
        */
        var onSuccessFunc = function(transport) {
            var oJson = transport.responseText.evalJSON();
            var geojson = new OpenLayers.Format.GeoJSON();
            var oGeoJSON = geojson.read(oJson, "FeatureCollection");
            this.thematicInfo.geometrySource = oLayer.thematic.geometrySource;
            this.geometrySources.set(oLayer.thematic.geometrySource, oJson);
            this.generateThematicLayer(oLayer, oGeoJSON);

            try {


            }
            catch (parsingError) {
                if (transport.status != 0) { // i.e. if not fail because interrupted
                    alert('Error: Failed to get data for geometries.');
                }
                alert('Error: Failed to process data for geometries.');
            }
        };
        var options =
		{
		    method: 'get',
		    parameters: null,
		    onFailure: function(transport) {
		        if (transport.status != 0) { // i.e. if not fail because interrupted
		            alert('AJAX Error: Failed to get data for gemetries');
		        }
		    },
		    onSuccess: onSuccessFunc.bindAsEventListener(this)
		};
        // TODO: FT set strGeometrySource based on Zoom Level
        // this needs the proper data source and the bbox added
        var myURL = this.mapWrapper.dataurl
        var bounds = this.mapWrapper.map.getExtent();
        myURL += '?requesttype=GeoJson&gs=';

        //myURL += this.getGeometryForZoom(oLayer)
        myURL += oLayer.thematic.geometrySource;
        myURL += "&ms=" + this.mapWrapper.mapsource;
        if (this.getDataLevelForZoom(oLayer) == 'bbox')
        { myURL += "&bbox=" + bounds.toBBOX() + "&zoom=" + this.mapWrapper.map.zoom; }
        new Ajax.Request
				(myURL,
					options
				);
    },

    // FT new function generateThematicFeatures

    generateThematicFeatures: function(oLayer, oGeoJSON) {
        /**
        * Function: generateThematicFeatures 
        * Create the map vector layer for the theme from for the layer object using data from GeoJSON
        *
        * Parameters:
        * oLayer - {object} map layer object
        * oGeoJSON - {object} GeoJSON object containing geometry data for the polygons
        *
        * Returns:
        */
        var oThematicLayer = new OpenLayers.Layer.Vector('thematicLayer', { styleMap: this.defaultStyleMap });
        //create the features on the vector layer from GeoJSON 

        oThematicLayer.addFeatures(oGeoJSON);
        //create the field in each feature that will contain data to be used to apply theme rules
        //and a previous value for previous month
        // FT only add for newly loaded features
        for (i = 0; i < oThematicLayer.features.length; i++) {
            oThematicLayer.features[i].attributes.currentValue = -1;
            oThematicLayer.features[i].attributes.previousValue = -1;
        }
        //keep a variable for current feature hovered over
        this.thematicInfo.hoverFeature = '';
        this.thematicInfo.oThematicLayer = oThematicLayer;
        this.mapWrapper.thematicLayer = oThematicLayer;
        var featureHoverSelectOption = {
            hover: true,
            onSelect: function(feature) {
                this.map.thematic.featureName = feature.attributes.name;
                this.map.thematic.feature = feature;
            },
            onUnselect: function(feature) {
                this.map.thematic.featureName = '';
                this.map.thematic.feature = null;
            }
        };
        var ctrlHoverSelectFeature = new OpenLayers.Control.SelectFeature(oThematicLayer, featureHoverSelectOption);
        ctrlHoverSelectFeature.handlers["feature"].stopDown = false;
        ctrlHoverSelectFeature.handlers["feature"].stopUp = false;
        ctrlHoverSelectFeature.handlers["feature"].stopClick = false;
        //ctrlHoverSelectFeature.handlers["feature"].dblclick = function () {alert('yes')}
        //this.mapWrapper.addControl('featureClickSelect', ctrlClickSelectFeature);		
        //this.mapWrapper.addControl('featureClickSelect', ctrlHoverSelectFeature);		
        this.mapWrapper.addControl('featureHoverSelect', ctrlHoverSelectFeature);
        //if the 'initialised' flag in 'thematic' for layer does not exit, create and set to false
        if (!oLayer.thematic.ranges.initialised)
            oLayer.thematic.ranges.initialised = false;
        //Create the rules and draw the thematic layer			
        this.getThematicRules(oThematicLayer, oLayer);
        this.mapWrapper.map.addLayer(oThematicLayer);
        //this.thematicLayerFinished(oThematicLayer,oLayer,true);
    },


    generateThematicLayer: function(oLayer, oGeoJSON) {
        /**
        * Function: generateThematicLayer 
        * Create the map vector layer for the theme from for the layer object using data from GeoJSON
        *
        * Parameters:
        * oLayer - {object} map layer object
        * oGeoJSON - {object} GeoJSON object containing geometry data for the polygons
        *
        * Returns:
        */
        var newLayer = false;
        //get the thematic layer if it has already been created
        var oThematicLayer = this.findThematicMapLayer();
        if (oThematicLayer == null) {
            //otherwise create an empty one
            oThematicLayer = new OpenLayers.Layer.Vector('thematicLayer', { styleMap: this.defaultStyleMap });
            newLayer = true;
            //add the new features to this layer
            oThematicLayer.addFeatures(oGeoJSON);
            //create the field in each feature that will contain data to be used to apply theme rules
            //and a previous value for previous month
            // FT only add for newly loaded features
            for (i = 0; i < oThematicLayer.features.length; i++) {
                oThematicLayer.features[i].attributes.currentValue = -1;
                oThematicLayer.features[i].attributes.previousValue = -1;
            }
            //keep a variable for current feature hovered over
            this.thematicInfo.hoverFeature = '';
            this.thematicInfo.oThematicLayer = oThematicLayer;
            this.mapWrapper.thematicLayer = oThematicLayer;
            var featureHoverSelectOption = {
                hover: true,
                onSelect: function(feature) {
                    this.map.thematic.featureName = feature.attributes.name;
                    this.map.thematic.featureCode = feature.attributes.code;
                    this.map.thematic.feature = feature;
                    //clear existing
                    for (var i = 0; i < oLayer.group.zoomLevels.length; i++) {
                        if (oLayer.thematic.geometrySource == oLayer.group.zoomLevels[i].geometrySource) {
                            var hoverSpan = $('span' + oLayer.thematic.geometrySource);
                            if (hoverSpan) { hoverSpan.update(feature.attributes.name.replace('@', ' in ')); }
                        }
                        else {
                            var hoverSpan = $('span' + oLayer.group.zoomLevels[i].geometrySource);
                            if (hoverSpan) { hoverSpan.update('&nbsp;'); }
                        }

                    }

                },
                onUnselect: function(feature) {
                    this.map.thematic.featureName = '';
                    this.map.thematic.featureCode = '';
                    this.map.thematic.feature = null;
                    var hoverSpan = $('span' + oLayer.thematic.geometrySource);
                    if (hoverSpan) {
                        hoverSpan.update('&nbsp;');
                    }
                }
            };
            var ctrlHoverSelectFeature = new OpenLayers.Control.SelectFeature(oThematicLayer, featureHoverSelectOption);
            ctrlHoverSelectFeature.handlers["feature"].stopDown = false;
            ctrlHoverSelectFeature.handlers["feature"].stopUp = false;
            ctrlHoverSelectFeature.handlers["feature"].stopClick = false;
            ctrlHoverSelectFeature.handlers["feature"].dblclick = this.mapWrapper.interactions.showThematicPopUp.bindAsEventListener(this.mapWrapper);
            /*ctrlHoverSelectFeature.handlers["feature"].dblclick = function (evt) {
            this.map.wrapper.interactions.showThematicPopUp.bindAsEventListener(this.map.wrapper);
            this.map.wrapper.interactions.showThematicPopUp(evt);
            Event.stop(evt); return false;
            }*/
            //this.mapWrapper.addControl('featureClickSelect', ctrlClickSelectFeature);		
            //this.mapWrapper.addControl('featureClickSelect', ctrlHoverSelectFeature);		
            this.mapWrapper.addControl('featureHoverSelect', ctrlHoverSelectFeature);
        }
        else {
            oThematicLayer.addFeatures(oGeoJSON);
        }
        //if the 'initialised' flag in 'thematic' for layer does not exit, create and set to false
        if (!oLayer.thematic.ranges.initialised)
            oLayer.thematic.ranges.initialised = false;
        //Create the rules and draw the thematic layer			
        this.getThematicRules(oThematicLayer, oLayer);
        if (newLayer) {
            this.mapWrapper.map.addLayer(oThematicLayer);
        }
        //this.thematicLayerFinished(oThematicLayer,oLayer,true);
    },

    thematicLayerFinished: function(oThematicMapLayer, oLayer, bVisible) {
        /**
        * Function: thematicLayerFinished 
        * Set the layer currentlyVisible propery according to visibility on Map.
        * Sets/unsets hover observation event.
        *
        * Parameters:
        * oThematicMapLayer - {object} theme map vector layer object
        * oLayer - {object} map layer object
        * bVisible - {bool} GeoJSON true - visible / false - not visible
        *
        * Returns:
        */

        if (bVisible) {
            oLayer.currentlyVisible = true;
            if (oLayer.group.currentLayer) {
                var previousLayer = oLayer.group.currentLayer;
                previousLayer.currentlyVisible = false;
                previousLayer.group.currentLayer = null;
                this.mapWrapper.mapElement.fire('astun:thematicHide', {
                    layer: previousLayer
                });
            }

            oLayer.currentlyVisible = true;
            oLayer.group.currentLayer = oLayer;
            oThematicMapLayer.setVisibility(true);


            this.mapWrapper.setControl('drag', new OpenLayers.Control.Drag(
				{
				    'down': function(evt) {
				        this.map.wrapper.installedControls['hover'].deactivate();
				        this.map.wrapper.installedControls['featureHoverSelect'].deactivate();
				    },
				    'up': function(evt) {
				        this.map.wrapper.installedControls['hover'].activate();
				        this.map.wrapper.installedControls['featureHoverSelect'].activate();
				    }
				}
			));

            /*
            this.mapWrapper.setControl('hover',  new OpenLayers.Control.Hover(
            {'delay': 1000}, 
            {
            'pause': this.mapWrapper.interactions.showThematicPopUp.bindAsEventListener(this.mapWrapper),
            'move': function(e){
            clearTimeout(this.map.wrapper.timers.hover);
            }
            }
            ));
            */
            this.mapWrapper.setControl('click', new OpenLayers.Control.Click(
				{ 'single': false, 'double': false, 'pixelTolerance': 1, 'stopDouble': true },
				{
				//'dblclick': this.mapWrapper.interactions.showThematicPopUp.bindAsEventListener(this.mapWrapper)
}
			));


            this.mapWrapper.mapElement.fire('astun:thematicShow', {
                layer: oLayer
            });

            //this.mapWrapper.mapElement.fire('astun:saveSetting', {setting: 'visibleThematic', value: ''+oLayer.layerName});
        }
        else {
            //TODO: reset hover and drag controls to those set before thematic layer loaded.
            this.mapWrapper.removeControl('drag');

            oLayer.currentlyVisible = false;
            oLayer.group.currentLayer = null;
            oThematicMapLayer.setVisibility(false);
            this.thematicInfo.themeBeingProcessed = false;
            this.mapWrapper.mapElement.fire('astun:thematicHide', {
                layer: oLayer
            });
            //$('homeWard').innerHTML = '';
            //$('hoverWard').innerHTML ==''					
            //this.mapWrapper.mapElement.fire('astun:deleteSetting', {setting: 'visibleThematic'});			        
        }
        this.thematicInfo.themeBeingProcessed = false;
        this.mapWrapper.mapElement.fire('astun:themeLoadComplete', {});
    },


    getDateSelection: function(noMonth, prevYear, prevMonth) {
        /**
        * Function: getDateSelection 
        * builds the date selection criteria
        *
        * Parameters:
        * noMonth - {number} no of months back from 'cutOffDate'
        * prevYear - {number} year value of 'cutOffDate'
        * prevMonth - {number} month value of 'cutOffDate', javascript month is zero based
        *
        * Returns:
        * {string} Format for e.g "between '2008-11-01' and '2008-11-30'"
        */
        var oDate = new Date();
        var curYear = oDate.getFullYear();
        var curMonth = oDate.getMonth();
        var tempMonth = curMonth;
        var tempYear = curYear;
        var lastMonth = 0;
        var monthDays = 0;
        //getting dates for specific month and year
        if (prevYear) {
            tempYear = prevYear;
            tempMonth = prevMonth;
        }
        else {
            if (curMonth == 0) {
                tempMonth = 11;
                tempYear = curYear - 1
            }
            else {
                tempMonth = curMonth - 1;
                //tempMonth = tempMonth + 1 ; //calendar
            }
        }
        tempMonth = tempMonth + 1; //inclusive range
        var dd = new Date(tempYear, (tempMonth), 0);
        var monthDays = dd.getDate();
        //Calender Month, javascript is zero based
        var endDate = "'" + [tempYear, (tempMonth), monthDays].join('-') + "'";
        //find start date
        if (tempMonth < noMonth) {
            tempYear = tempYear - 1;
            tempMonth = 12 - (noMonth - tempMonth);
        }
        else {
            tempMonth = tempMonth - noMonth;
        }
        tempMonth = tempMonth + 1; //inclusive range
        var startDate = "'" + [tempYear, tempMonth, '01'].join('-') + "'";
        return 'between ' + startDate + ' and ' + endDate;
    },

    toggleHomeFeature: function(featureName) {
        /**
        * Function: toggleHomeFeature 
        * Identify the polygon feature on the thematic layer with matching name and draw a yellow
        * border to make it identifiable on map display. If feature already has yellow border, 
        * will then remove it. If no name passed, will then de-select any home feature present.
        *
        * Parameters:
        * featureName - {string } corresponding feature.attributes.name
        *
        * Returns:
        * 
        */
        var featureChanged = false;
        var oFeature = {};
        if (!this.thematicInfo.homeFeature) {
            this.mapWrapper.map.thematic.homeFeature = {};
            this.thematicInfo.homeFeature = {};
        }
        else {
            //already a home feature set, then reset it			
            $('hoverWard').innerHTML = '';
            $('homeWard').innerHTML = '';
            oFeature = this.thematicInfo.homeFeature.current;
            oFeature.style = null;
            this.thematicInfo.oThematicLayer.drawFeature(oFeature);
            //resets the pointer to current home feature
            this.mapWrapper.map.thematic.homeFeature = null;
            this.thematicInfo.homeFeature = null;
            oFeature = {};
            if (featureName === '') {
                return
            }

        }
        //determine the selected feature and keep reference for future use.
        var stylTemp = new OpenLayers.Style();

        for (i = 0; i < this.thematicInfo.oThematicLayer.features.length; i++) {
            if (this.thematicInfo.oThematicLayer.features[i].attributes.name == featureName) {
                oFeature = this.thematicInfo.oThematicLayer.features[i];
                break;
            }
        }
        this.mapWrapper.map.thematic.homeFeature = {};
        this.thematicInfo.homeFeature = {};
        this.mapWrapper.map.thematic.homeFeature.current = oFeature;
        this.thematicInfo.homeFeature.current = oFeature;

        var aRules = oFeature.layer.styleMap.styles['default'].rules;
        for (var i = 0; i < aRules.length; i++) {
            var oRule = aRules[i];
            var applies = oRule.evaluate(oFeature);
            if (applies) {
                var rangeColour = oRule.symbolizer.Polygon.fillColor;
            }
        }
        var stylTemp = new OpenLayers.Style();
        stylTemp.fillColor = rangeColour;
        stylTemp.fillOpacity = 0.6;
        // prak 17/12/08
        stylTemp.hoverFillColor = "#0000FF";
        stylTemp.hoverFillOpacity = 0.6,

		stylTemp.strokeColor = "#FFFF66";
        stylTemp.strokeOpacity = 1;
        stylTemp.strokeWidth = 6;
        oFeature.style = stylTemp;


        this.thematicInfo.oThematicLayer.drawFeature(oFeature);
        //peter plz comment
        this.mapWrapper.interactions.showHomeWardGraph(this.thematicInfo);

        //this.centreToFeature(featureName);
    },

    centreToFeature: function(sfeatureName) {
        /**
        * Function: centreToFeature 
        * Centres the map display on the polygon feature with matching feature.attributes.name
        * border to make it identifiable on map display
        *
        * Parameters:
        * featureName - {string } matching feature.attributes.name
        *
        * Returns:
        * 
        */
        var oFeature = {};
        for (i = 0; i < this.thematicInfo.oThematicLayer.features.length; i++) {
            if (this.thematicInfo.oThematicLayer.features[i].attributes.name == sfeatureName) {
                oFeature = this.thematicInfo.oThematicLayer.features[i];
                break;
            }
        }

        if (oFeature)
            this.mapWrapper.map.setCenter(oFeature.geometry.getBounds().getCenterLonLat(), 10, false, false);

    }


});   //OLLayerControl		
	
var LayerGroup = Class.create			
({
	getDisplayName: function()
	{
	/**
	* Function: getDisplayName 
	* returns the display name of current layerGroup object
	*
	* Parameters:
	*
	* Returns:
	*  {string} - the display name.
	*/
		return this.displayName ;
	},	
	
	getLayerDetails: function(lyrNo)
	{
	/**
	* Function: getLayerDetails 
	* returns the layer properties at corresponding index, concatenated in a string, for debug
	*
	* Parameters:
	* lyrNo - {number} index of layer in layers array of layer group
	* 
	* Returns:
	*  {string} - property values concatenated in a single string
	*/
		var strRet = "" ;
		var oLyr = new layer();
		oLyr = this.layers[lyrNo] ;
		strRet += " Layer No : " + lyrNo  ;
		strRet += " " +  " ,displayName: " + oLyr.displayName  ;
		strRet += " " +  " ,layerName: " + oLyr.layerName  ;
		strRet += " " +  " ,Visible: " + oLyr.initiallyVisible   ;
		strRet += " " +  " ,currentlyVisible: " + oLyr.currentlyVisible  ;
		return strRet ;
	},					
				
	initialize: function (name, guid )
	{
	/**
	* Function: initialize 
	* creates a layer group with supplied name and guuid
	*
	* Parameters:
	* name - {string} to be assigned to the layerGroup 'displayName'
	* guid - {string} to be assigned to the layerGroup 'guuid'
	* 
	* Returns:
	*  {string} - property values concatenated in a single string
	*/
	
				this.guid = guid || null;
				this.layers = $A([]);
				this.activeLayers = $A([]);
				this.thematicLayers = $A([]);
				this.activeShown = 0;
				this.displayName = name ;
				this.themeFieldsIndex = 0;
	},
	
	showLayers: function()
	{
	/**
	* Function: showLayers 
	* displays all the layers in the group on the map display
	*
	* Parameters:
	* 
	* Returns:
	* 
	*/
		this.activeShown = 0;
		for ( i=0; i< this.activeLayers.length; i++ )
		{
				this.activeShown++;	
				if ( !this.activeLayers[i].currentlyVisible )
				{
					this.mapWrapper.addISMLayer(this.activeLayers[i].layerName) ;
					this.activeLayers[i].currentlyVisible = true;
					$(this.mapWrapper.mapElement).fire("astun:layerShow", {layer: this.activeLayers[i]});
				}				
		}
		this.mapWrapper.draw();		
		
		this.mapWrapper.mapElement.fire('astun:saveSetting', {setting: 'visibleLayers', value: ''+this.mapWrapper.ismDataLayer.params.layers});
				
		this.mapWrapper.mapElement.fire("astun:showLayerGroup", {layerGroup: this});
	},
	
	hideLayers: function()
	{
	/**
	* Function: hideLayers 
	* Turn off the visibility on Map display for all layers in current group
	*
	* Parameters:
	* 
	* Returns:
	* 
	*/
	
	
		this.activeShown = 0;
		for ( i=0; i< this.activeLayers.length; i++ )
		{
				if ( this.activeLayers[i].currentlyVisible )
				{
					this.mapWrapper.removeISMLayer(this.layers[i].layerName) ;
					this.activeLayers[i].currentlyVisible = false;
					$(this.mapWrapper.mapElement).fire("astun:layerHide", {layer: this.activeLayers[i]});
				}					
		}
		this.mapWrapper.draw();	
		
		this.mapWrapper.mapElement.fire('astun:saveSetting', {setting: 'visibleLayers', value: ''+this.mapWrapper.ismDataLayer.params.layers});
					
		this.mapWrapper.mapElement.fire("astun:hideLayerGroup", {layerGroup: this});
	}
	
	
	
});

 
var layer= Class.create
({

		
		initialize: function(lyrName)
		{
		/**
		* Function: initialize 
		* Creates the layer object and initialises some property values. 
		* Called automatically upon obejct creattion ( var x = new layer(myLyr); )
		*
		* Parameters:
		* lyrName - {string} the assigned display name for the layer
		*
		* Returns:
		* 
		*/
		
				this.layerName = '' ;
				this.displayName = lyrName ;
				this.initiallyVisible = false ;
				this.currentlyVisible = false ;
				this.icon = '';
				this.infoClick =  false ;
				this.help =  '';
				this.query =  {} ;
				this.active =  true ;
				this.searchField = '' ;
				this.thematic = {};
				this.thematic.name = '';
				this.thematic.thematicType = 0 ;
				this.thematic.legendSubTitle = 0 ;
				this.thematic.themeFields = [] ;
				this.thematic.ranges = {} ;
				this.thematic.ranges.auto = true ;
				this.thematic.ranges.count = 0 ;
				this.thematic.ranges.initialised = false ;
				this.thematic.ranges.rangeNo = [] ;
				
			
		}		
 });
 
 


