var WMS_LAYER_TYPE = 1;
var GMAP2_LAYER_TYPE = 2;
var VMAP_LAYER_TYPE = 3;
var YMAP_LAYER_TYPE = 4;
var OSM_LAYER_TYPE = 5;
var TMS_LAYER_TYPE = 6;
var GMAP3_LAYER_TYPE = 7;
var BING_LAYER_TYPE = 8;

(function($) {


    $.widget("gcComponent.gcLayersManager", $.ui.gcComponent, {

        widgetEventPrefix: "gcLayersManager",

        options: {
            layerTree: 'treeList',
            layerSwitcher: false,
            referenceMap: 'refMapContainer',
            legend: 'legendList',
            editingLayer: true,
            selectionLayer: true,
			defaultFeatureFilters: null
        },
		
        internalVars: {
            themes: {
            //theme {title:'Theme title',layers:{}}
            },
            editingLayer: null,
            selectionLayer: null,
            highlightLayer: null,
            referenceMapLayers: [],
            reversedThemes: [],
            reversedLayers: {},
            queryableLayers: {}
        },

        _create: function() {
            var self = this;
            $.ui.gcComponent.prototype._create.apply(self, arguments);
			
            var mapsetData = gisclient.options.mapsetData;

            $.each(mapsetData.theme, function(themeId, theme) {
                self.internalVars.reversedThemes.push(themeId);
                self.internalVars.reversedLayers[themeId] = [];
				
                self.internalVars.themes[themeId] = {
                    title: null,
                    radio: false,
                    layers: {},
					id: themeId
                };
				
                $.each(theme, function(propertyKey, propertyValue) {
                    if(propertyKey == 'title') {
                        self.internalVars.themes[themeId].title = propertyValue;
                        return;
                    }
                    if(propertyKey == 'radio') {
                        if(propertyValue == 1) self.internalVars.themes[themeId].radio = true;
                        return;						
                    }
                    if(typeof(propertyValue) != 'object') {
                        gisclient.log('Unexpected theme content in init JSON string');
                        return;
                    }
                    var layer = propertyValue;
                    var layerId = propertyKey;
					layer.id = layerId;
					
                    self.internalVars.reversedLayers[themeId].push(layerId);
					
                    layer.userHasActivated = false;
                    self.internalVars.themes[themeId].layers[propertyKey] = layer;
                    if(self._layerIsQueryable(layer)) {
                        $.each(layer.options.featureTypes, function(featureId, feature) {
                            if(typeof(feature.items) != 'object') return;
                            var showGeometry = (typeof(feature.hidden) == 'undefined' || feature.hidden != 1);
                            var fields = {};
                            var count = 0;
                            $.each(feature.items, function(key, val) {
                                if(val.resultType != 4) {
                                    count += 1;
                                    fields[key] = val;
                                }
                            });
							if(self.options.defaultFeatureFilters != null) {
								var defaultFilters = (typeof(self.options.defaultFeatureFilters[featureId]) == 'undefined') ? null : self.options.defaultFeatureFilters[featureId];
							} else {
								var defaultFilters = null;
							}
                            var queryLayer = {
                                showGeometry: showGeometry,
                                themeId: themeId,
                                layerId: layerId,
                                featureId: featureId,
                                fields: fields,
                                fieldsCount: count,
                                title: feature.title,
                                layer: layer,
                                groupTitle: layer.title,
								defaultFilters: defaultFilters
                            };
                            self.internalVars.queryableLayers[featureId] = queryLayer;
                        });
                    }
                });
                self.internalVars.reversedLayers[themeId].reverse();
            });
            self.internalVars.reversedThemes.reverse();
			
            var radioThemes = self._getRadioThemes(); // set radio to false if there's only one radio theme
            if(radioThemes.length == 1) {
                self.internalVars.themes[radioThemes[0]].radio = false;
            }
			
            self._initLayers();
			
            gisclient.map.zoomToMaxExtent({
                restricted:true
            });

            if(self.options.layerTree) self._initTree();
            else if(self.options.layerSwitcher) self._initLayerSwitcher();
			
            if(self.options.legend) self._initLegend();
            if(self.options.referenceMap) self._initReferenceMap();
            if(self.options.editingLayer) self._initEditingLayer();
            if(self.options.selectionLayer) self._initSelectionLayer();
			
            if(self.options.layerTree) gisclient.map.events.register('zoomend',self,self._checkLayersVisibility);
        },
		
        _initLayers: function() {
            var self = this;

            var baseLayer = new OpenLayers.Layer.Image('BASE_LAYER', gisclient.options.mapsetURL +'images/pixel.png', gisclient.map.maxExtent, new OpenLayers.Size(1,1), {
                isBaseLayer: true,
                resolutions: gisclient.options.mapsetData.resolutions,
                displayInLayerSwitcher: false
            });
            gisclient.map.addLayer(baseLayer);
			
            $.each(self.internalVars.themes, function(themeId, theme) {
                $.each(theme.layers, function(layerId, layerData) {
                    self.internalVars.themes[themeId].layers[layerId].olLayer = self._createLayer(layerData);
                });
            });
        },
		
        _createLayer: function(layerData) {
            var self = this;
            var olLayer;
			
            switch(layerData.type) {
                case WMS_LAYER_TYPE:
                    if(typeof(GISCLIENT_DEBUG) == 'undefined') {
                        layerData.parameters.exceptions = 'BLANK';
                    }
					layerData.parameters.lang = OpenLayers.Lang.getCode();
                    olLayer = new OpenLayers.Layer.WMS(layerData.title,layerData.url,layerData.parameters,layerData.options);
                    break;
                default:
                    gisclient.log(layerData);
                    alert('NOT IMPLEMENTED LAYER TYPE FOR '+layerData.title);
                    break;
            }
			
            if(layerData.overview == 1) self.internalVars.referenceMapLayers.push(olLayer.clone());
            return olLayer;
        },
		
        loadLayers: function() {
            var self = this;

            $.each(self.internalVars.reversedThemes, function(e, themeId) {
                $.each(self.internalVars.reversedLayers[themeId], function(i, layerId) {
                    var layer = self.internalVars.themes[themeId].layers[layerId];
                    gisclient.map.addLayer(layer.olLayer);
                });
            });
            self._checkLayersVisibility();
			
            if(self.options.editingLayer) gisclient.map.addLayer(self.internalVars.editingLayer);
            if(self.options.selectionLayer) gisclient.map.addLayer(self.internalVars.selectionLayer);
        },
		
        _initReferenceMap: function() {
            var self = this;
			
            $('#'+self.options.referenceMap).referenceMap({
                layers: self.internalVars.referenceMapLayers.reverse(),
                mapOptions: {
                    units: "m",
                    projection: new OpenLayers.Projection(gisclient.getProjection()),
                    maxExtent: new OpenLayers.Bounds.fromArray(gisclient.options.mapsetData.restrictedExtent),
                    resolutions: gisclient.options.mapsetData.resolutions,
                    size: new OpenLayers.Size(250, 150)
                }
            });
        },
		
        _initTree: function() {
            var self = this;
			
            $('#'+self.options.layerTree).gcLayerTree({
                change: function(event, ui) {
                    self._treeClick(event, ui);
                }
            });
            var gcLayerTree = gisclient.componentObjects.gcLayerTree;
			
            $.each(self.internalVars.themes, function(themeId, theme) {
                self.internalVars.themes[themeId].treeId = gcLayerTree.addThemeNode(themeId, theme.title, theme.radio);
				var hasLayergroups = false;
                $.each(theme.layers, function(layerId, layerData) {
					if(typeof(layerData.hide) != 'undefined' && layerData.hide == 1) return;
					hasLayergroups = true;
                    self.internalVars.themes[themeId].layers[layerId].treeId = gcLayerTree.addLayerNode(themeId, layerId, layerData.title);
                });
				if(!hasLayergroups) gcLayerTree.removeThemeNode(themeId);
            });
            gcLayerTree.startJsTree();
			
            $.each(self.internalVars.themes, function(themeId, theme) {
                var themeIsActive = false;
                $.each(theme.layers, function(layerId, layerData) {
					if(typeof(layerData.hide) != 'undefined' && layerData.hide == 1) return;
                    self.internalVars.themes[themeId].layers[layerId].userHasActivated = true;
                    if(self.layerIsActive(themeId, layerId)) {
                        self._checkLayer(layerData);
                        themeIsActive = true;
                    }
                });
                if(themeIsActive) self._checkTheme(theme);
            });
        },
		
        _initLayerSwitcher: function() {
        },
		
        _initLegend: function() {
            var self = this;
			
            $('#'+self.options.legend).gcLegendTree();
            var gcLegendTree = gisclient.componentObjects.gcLegendTree;

            $.each(self.internalVars.themes, function(themeId, theme) {
                self.internalVars.themes[themeId].legendId = gcLegendTree.addThemeNode(themeId, theme.title);
				var hasLayergroups = false;
                $.each(theme.layers, function(layerId, layer) {
					if(typeof(layer.hide) != 'undefined' && layer.hide == 1) return;
					hasLayergroups = true;
                    self.internalVars.themes[themeId].layers[layerId].legendId = gcLegendTree.addLayerNode(themeId, layerId, layer);
                    var visibleLegend = [];
                    $.each(layer.legend, function(e, classData) {
                        if(classData.legendtype_id != '0') {
                            gcLegendTree.addClassNode(themeId, layerId, classData);
                            visibleLegend.push(classData);
                        }
                    });
                    self.internalVars.themes[themeId].layers[layerId].legend = visibleLegend;
                });
				if(!hasLayergroups) gcLegendTree.removeThemeNode(themeId);
            });
            gcLegendTree.startLegendTree();
			
            $.each(self.internalVars.themes, function(themeId, theme) {
                var allHidden = true;
                $.each(theme.layers, function(layerId, layer) {
                    if(!self.layerIsActive(themeId, layerId)) {
                        self._hideLegendLayer(layer);
                    } else allHidden = false;
                });
                if(allHidden) self._hideLegendTheme(theme);
            });
			
        },
		
        getEditingLayer: function() {
            var self = this;
			
            if(self.internalVars.editingLayer == null) self._initEditingLayer();
            return self.internalVars.editingLayer;
        },
		
        getSelectionLayer: function() {
            var self = this;
            if(self.internalVars.selectionLayer == null) self._initSelectionLayer();
            return self.internalVars.selectionLayer;
        },
		
        getHighlightLayer: function() {
            var self = this;
            if(self.internalVars.highlightLayer == null) self._initHighlightLayer();
            return self.internalVars.highlightLayer;
        },
		
        _treeClick: function(event, ui) {
            var self = this;
            if(ui.role == 'theme') {
                if(ui.checked) self.activateTheme(ui.id);
                else self.deactivateTheme(ui.id);
            } else if(ui.role == 'layer') {
                if(ui.checked) {
                    self.internalVars.themes[ui.parent].layers[ui.id].userHasActivated = true;
                    self.activateLayer(ui.parent, ui.id);
                } else {
                    self.internalVars.themes[ui.parent].layers[ui.id].userHasActivated = false;
                    self.deactivateLayer(ui.parent, ui.id);
                }
            }
			
        },
		
        _checkLayersVisibility: function() {
            var self = this;
			
            $.each(self.internalVars.themes, function(themeId, theme) {
                var allDisabled = true;
                $.each(theme.layers, function(layerId, layer) {
                    if(!layer.olLayer.calculateInRange()) self._disableLayer(layer);
                    else {
                        allDisabled = false;
                        self._enableLayer(layer);
                    }
                });
                if(allDisabled) self._disableTheme(theme);
                else self._enableTheme(theme);
            });
        },
		
        _getRadioThemes: function() {
            var self = this;
			
            var radioThemes = [];
            $.each(self.internalVars.themes, function(themeId, theme) {
                if(theme.radio) radioThemes.push(themeId);
            });
            return radioThemes;
        },
		
        themeIsActive: function(themeId) {
            var self = this;
            var isActive = false;
            $.each(self.internalVars.themes[themeId].layers, function(layerId, layer) {
                if(layer.olLayer.getVisibility()) isActive = true;
            });
            return isActive;
        },
		
        activateTheme: function(themeId) {
            var self = this;
			
            $.each(self.internalVars.themes[themeId].layers, function(layerId, layer) {
                if(layer.userHasActivated && layer.olLayer.calculateInRange()) {
                    self.activateLayer(themeId, layerId, false);
                }
            });
            if(self.options.layerTree) self._checkTheme(self.internalVars.themes[themeId]);
            if(self.options.legend) self._showLegendTheme(self.internalVars.themes[themeId]);
			
            if(self.internalVars.themes[themeId].radio) {
                $.each(self._getRadioThemes(), function(e, radioThemeId) {
                    if(themeId != radioThemeId) self.deactivateTheme(radioThemeId);
                });
            }
        },
		
        deactivateTheme: function(themeId) {
            var self = this;
			
            $.each(self.internalVars.themes[themeId].layers, function(layerId, layer) {
                layer.olLayer.setVisibility(false);
                if(self.options.layerTree) self._unCheckLayer(layer);
                if(self.options.legend) self._hideLegendLayer(layer);
            });
            if(self.options.layerTree) self._unCheckTheme(self.internalVars.themes[themeId]);
            if(self.options.legend) self._hideLegendTheme(self.internalVars.themes[themeId]);
        },
		
        layerIsActive: function(themeId, layerId) {
            var self = this;
			if(typeof(self.internalVars.themes[themeId]) == 'undefined') return false;
			if(typeof(self.internalVars.themes[themeId].layers[layerId]) == 'undefined') return false;
            return self.internalVars.themes[themeId].layers[layerId].olLayer.getVisibility();
        },
		
        activateLayer: function(themeId, layerId, checkTheme) {
            if(typeof(checkTheme) == 'undefined') checkTheme = true;
            var self = this;
			
            var layer = self.internalVars.themes[themeId].layers[layerId];
            layer.olLayer.setVisibility(true);
            if(self.options.layerTree) self._checkLayer(layer);
            if(self.options.legend) self._showLegendLayer(layer);
			
            if(self.options.layerTree && checkTheme) {
                self._checkTheme(self.internalVars.themes[themeId]);
            }
        },
		
        deactivateLayer: function(themeId, layerId) {
            var self = this;
			
            var layer = self.internalVars.themes[themeId].layers[layerId];
            layer.olLayer.setVisibility(false);
            if(self.options.layerTree) self._unCheckLayer(layer);
            if(self.options.legend) self._hideLegendLayer(layer);
			
            var allDeactivated = true;
            $.each(self.internalVars.themes[themeId].layers, function(layerId, layer) {
                if(self.layerIsActive(themeId, layerId)) allDeactivated = false;
            });
            if(allDeactivated) self.deactivateTheme(themeId);
        },
		
        getQueryableLayers: function(selectActive, includeHidden) {
            var self = this;
            if(typeof(selectActive) == 'undefined') selectActive = false;
            if(typeof(includeHidden) == 'undefined') includeHidden = false;
			
            if(!selectActive && includeHidden) return self.internalVars.queryableLayers;
			
            var queryableLayers = {};
            $.each(self.internalVars.queryableLayers, function(featureId, feature) {
                if(selectActive && !self.layerIsActive(feature.themeId, feature.layerId)) return;
                if(!includeHidden && feature.fieldsCount == 0) return;
                queryableLayers[featureId] = feature;
            });
            return queryableLayers;
        },
		
        _hideLegendTheme: function(theme) {
            var self = this;
            $('#'+theme.legendId).hide();
        },
		
        _hideLegendLayer: function(layer) {
            var self = this;
            $('#'+layer.legendId).hide();
        },
		
        _showLegendTheme: function(theme) {
            var self = this;
            $('#'+theme.legendId).show();
        },
		
        _showLegendLayer: function(layer) {
            var self = this;
            $('#'+layer.legendId).show();
        },
		
        _checkLayer: function(layer) {
            var self = this;
            $('#'+layer.treeId).attr('checked', 'checked');
        },
		
        _checkTheme: function(theme) {
            var self = this;
            $('#'+theme.treeId).attr('checked', 'checked');
        },
		
        _unCheckLayer: function(layer) {
            var self = this;
            $('#'+layer.treeId).attr('checked', false);
        },
		
        _unCheckTheme: function(theme) {
            var self = this;
            $('#'+theme.treeId).attr('checked', false);
        },
		
        _enableLayer: function(layer) {
            var self = this;
            $('#'+layer.treeId).removeAttr('disabled');
            if(self.options.legend && self.layerIsActive(layer.id)) self._showLegendLayer(layer);
        },
		
        _enableTheme: function(theme) {
            var self = this;
            $('#'+theme.treeId).removeAttr('disabled');
            if(self.options.legend && self.themeIsActive(theme.id)) self._showLegendTheme(theme);
        },
		
        _disableLayer: function(layer) {
            var self = this;
            $('#'+layer.treeId).attr('disabled', true);
            if(self.options.legend) self._hideLegendLayer(layer);
        },
		
        _disableTheme: function(theme) {
            var self = this;
            $('#'+theme.treeId).attr('disabled', true);
            if(self.options.legend) self._hideLegendTheme(theme);
        },
		
        _layerIsQueryable: function(layer) {
            var self = this;
			
            if(typeof(layer.options.featureTypes) != 'object') return false;
            return true;
        },
		
        _initEditingLayer: function() {
            var self = this;

            // cursors for the transformfeature control
            var cursors = ["sw-resize", "s-resize", "se-resize", "e-resize", "ne-resize", "n-resize", "nw-resize", "w-resize"];
            var context = {
                getCursor: function(feature){
                    var controls = gisclient.map.getControlsByClass('OpenLayers.Control.TransformFeature');
                    var i = OpenLayers.Util.indexOf(controls[0].handles, feature);
                    var cursor = "inherit";
                    if(i !== -1) {
                        i = (i + 8 + Math.round(controls[0].rotation / 90) * 2) % 8;
                        cursor = cursors[i];
                    }
                    return cursor;
                }
            };

            // styles for the vector layer
            var styles = new OpenLayers.StyleMap({
                "default": new OpenLayers.Style(null, {
                    rules: [
                    new OpenLayers.Rule({
                        symbolizer: {
                            "Point": {
                                pointRadius: 5,
                                graphicName: "square",
                                fillColor: "white",
                                fillOpacity: 0.25,
                                strokeWidth: 1,
                                strokeOpacity: 1,
                                strokeColor: "#3333aa",
                                fontSize: "14px"
                            },
                            "Line": {
                                strokeWidth: 3,
                                strokeOpacity: 1,
                                strokeColor: "#6666aa"
                            },
                            "Polygon": {
                                strokeWidth: 1,
                                strokeOpacity: 1,
                                fillColor: "#9999aa",
                                strokeColor: "#6666aa"
                            }
                        }
                    })
                    ]
                }),
                "select": new OpenLayers.Style(null, {
                    rules: [
                    new OpenLayers.Rule({
                        symbolizer: {
                            "Point": {
                                pointRadius: 5,
                                graphicName: "square",
                                fillColor: "white",
                                fillOpacity: 0.25,
                                strokeWidth: 2,
                                strokeOpacity: 1,
                                strokeColor: "#0000ff"
                            },
                            "Line": {
                                strokeWidth: 3,
                                strokeOpacity: 1,
                                strokeColor: "#0000ff"
                            },
                            "Polygon": {
                                strokeWidth: 2,
                                strokeOpacity: 1,
                                fillColor: "#0000ff",
                                strokeColor: "#0000ff"
                            }
                        }
                    })
                    ]
                }),
                "temporary": new OpenLayers.Style(null, {
                    rules: [
                    new OpenLayers.Rule({
                        symbolizer: {
                            "Point": {
                                graphicName: "square",
                                pointRadius: 5,
                                fillColor: "white",
                                fillOpacity: 0.25,
                                strokeWidth: 2,
                                strokeColor: "#0000ff"
                            },
                            "Line": {
                                strokeWidth: 3,
                                strokeOpacity: 1,
                                strokeColor: "#0000ff"
                            },
                            "Polygon": {
                                strokeWidth: 2,
                                strokeOpacity: 1,
                                strokeColor: "#0000ff",
                                fillColor: "#0000ff"
                            }
                        }
                    })
                    ]
                }),
                "transform": new OpenLayers.Style({
                    cursor: "${getCursor}",
                    pointRadius: 5,
                    fillColor: "white",
                    fillOpacity: 1,
                    strokeColor: "black"
                }, {
                    context: context
                })
            });
				
            var editingLayer = new OpenLayers.Layer.Vector('GisClientVector', {
                styleMap: styles
            });
            // HACK: give attributes.text an empty string to avoid openlayers to write undefined when a feature is added (used by redline text)
            editingLayer.events.register('featureadded',self,function(event) {
                event.feature.attributes.text = '';
            });
            //gisclient.map.addLayer(editingLayer);
            self.internalVars.editingLayer = editingLayer;
        },
		
        _initSelectionLayer: function() {
            var self = this;

            // functions to customize vector styles
            var styleFunctions = {
                context: {
                    getStrokeWidth: function(feature) {
                        if(feature.geometry != null && typeof(feature.geometry.CLASS_NAME) != 'undefined') {
                            switch(feature.geometry.CLASS_NAME) {
                                case 'OpenLayers.Geometry.LineString':
                                    return 10;
                            }
                        }
                        return 1;
                    }
                }
            };
			

            // vector stylemap
            var styleMap = new OpenLayers.StyleMap({
                "default": new OpenLayers.Style({
                    pointRadius: 12,
                    strokeWidth: "${getStrokeWidth}",
                    strokeColor: "#00B4B4",
                    fillColor: "#00B4B4",
                    fillOpacity: 0.2,
                    strokeOpacity:0.6,
                    strokeWidth: 2 // TODO: this is a double entry
                }, styleFunctions),
                "select": new OpenLayers.Style({
                    pointRadius: 14,
                    strokeWidth: "${getStrokeWidth}",
                    strokeColor: "yellow",
                    fillColor: "yellow",
                    fillOpacity: 0.6
                }, styleFunctions)
            });
			
            var layer = new OpenLayers.Layer.Vector('GisClientSelection', {
                styleMap:styleMap
            });
            //gisclient.map.addLayer(layer);
            self.internalVars.selectionLayer = layer;
        },
		
        _initHighlightLayer: function() {
            var self = this;
			
            var styleMap = new OpenLayers.StyleMap({
                "default": new OpenLayers.Style({
                    fillColor: "FF9900",
                    strokeColor: "black",
                    fillOpacity: 0.2,
                    pointRadius: 10
                })
            });
			
            self.internalVars.highlightLayer = new OpenLayers.Layer.Vector('HighlightLayer', {
                styleMap:styleMap
            });
            gisclient.map.addLayer(self.internalVars.highlightLayer); // si puo unire con quello del taglio
        },
		
        reloadLayer: function(themeId, layerId) {
            var self = this;
			
            self.internalVars.themes[themeId].layers[layerId].olLayer.redraw(true);
        },
		
        reloadTheme: function(themeId) {
            var self = this;
			
            $.each(self.internalVars.themes[themeId].layers, function(layerId, layer) {
                self.reloadLayer(themeId, layerId);
            });
        },
		
        reloadThemes: function() {
            var self = this;
			
            $.each(self.internalVars.themes, function(themeId, theme) {
                self.reloadTheme(themeId);
            });
        },
		
        getLayer: function(themeId, layerId) {
            var self = this;
            
            if (typeof self.internalVars.themes[themeId] != 'undefined') {
                return self.internalVars.themes[themeId].layers[layerId];
            }
            return null;
        },
		
        getThemes: function() {
            var self = this;
			
            var themesWOLayers = {};
            $.each(self.internalVars.themes, function(themeId, theme) {
                themesWOLayers[themeId] = {
                    title: theme.title
                };
            });
            return themesWOLayers;
        },
		
        getLayers: function(themeId) {
            var self = this;
			
            return self.internalVars.themes[themeId].layers;
        }
		
    });

    $.extend($.gcComponent.gcLayersManager, {
        version: "3.0.0"
    });
})(jQuery);
