/**
 *
 */
'use strict';
define(function() {
  var gclayers = function() {
    var BackGroundLayer = [];
    var OperationalLayer = new ol.Collection();
    var OperationalLayerg2c = new ol.Collection();
    var OperationalLayerESRI = new ol.Collection();
    var TechnicalLayer = [];
    var SelectFilter = [];
    var annoText = [];
    var layerHighlight = {};
    var selectSource = {};
    var drawSource = {};
    var importSource = {};
    var guideSource = {};
    var grouplayers = {};
    var layerSnap = {};
    var SnapActivate = false;
    var currentComposedLyr;
    // Technical layers
    let selectLayer = {};
    let importLayer = {};
    let guideLayer = {};
    let drawLayer = {}; // edit layer
    let measureLayer = {};
    let annotationLayer = {};


    this.$get = function(
      $rootScope,
      gcPropertiesLayers,
      gcStyleFactory,
      BaseMapFactory,
      DataStoreFactory,
      $timeout,
      gaJsUtils
    ) {
      var gcLayers = function() {
        this.addGroupLayer = function(layer) {
          var themename =
            layer.theme && layer.theme != 'undefined' ? layer.theme : 'Default';

          layer.theme = themename;

          if (angular.isUndefined(grouplayers[themename])) {
            grouplayers[themename] = [];
          }
          let toAdd = true;

          let layerFtiId = (layer.get('fti') || {}).uid;

          angular.forEach(grouplayers, function(val) {
            angular.forEach(val, function(value) {
              let valueFtiId = (value.get('fti') || {}).uid;

              if (
                valueFtiId &&
                layerFtiId &&
                valueFtiId === layerFtiId
              ) {
                toAdd = false;
              }

            });
          });

          if(grouplayers[themename].includes(layer)){
            toAdd = false;
          }

          if(toAdd){
            grouplayers[themename].push(layer);
          }
        };

        this.getGroupLayer = function() {
          return grouplayers;
        };

        this.clearLayersFromGroupLayer = function() {
          for (var group in grouplayers) {
            if (
              group !== 'Techniques' &&
                group.toUpperCase() !== 'WEBBACKGROUND'
            ) {
              delete grouplayers[group];
            }
          }
        };

        this.clearGroupLayer = function() {
          grouplayers = {};
        };

        this.deleteGroupLayer = function(layer) {
          if (grouplayers.hasOwnProperty(layer.theme)) {
            let deleteIndex = grouplayers[layer.theme]
              .map(function(x) {
                return x.name;
              })
              .indexOf(layer.name);
            grouplayers[layer.theme].splice(deleteIndex, 1);
          }
        };

        /**
           * [addBackGroundLayer description]
           * @param {[type]} layer [description]
           */
        this.addBackGroundLayer = function(layer) {
          gcPropertiesLayers(layer);
          BackGroundLayer.push(layer);
          if (BackGroundLayer.length > 1){
            $rootScope.$broadcast('gcBackGroundLayerChange', BackGroundLayer);
          }
          else {
            $rootScope.$broadcast('gcBackGroundLayerChange', layer);
          }
        };

        this.forceClearBackgroundLayer = () => {
          BackGroundLayer.length = 0;
        };

        this.clearBackgroundLayer = function(map, callOrigin) {
          if (currentComposedLyr) {
            this.removeBackGroundLayer(currentComposedLyr, map, callOrigin);
            BackGroundLayer.splice(0, BackGroundLayer.length);
          }
        };

        this.resetBackgroundLayer = () => {
          BackGroundLayer = [];
        };

        this.rmOnScaleChangeCb = function() {
          if (this.onScaleChangeCb != undefined) {
            ol.Observable.unByKey(this.onScaleChangeCb);
            this.onScaleChangeCb = undefined;
          }
        };

        this.removeBackGroundLayer = function(layer, map, callOrigin) {
          var bgs;

          if (layer.type == 'COMPOSED') {
            if (callOrigin != 'fromZoom') this.rmOnScaleChangeCb();
            bgs = layer.backgrounds;
            for (var iLyr = 0; iLyr < bgs.length; iLyr++) {
              if (bgs[iLyr].layer != undefined) {
                this.removeBackGroundLayer(bgs[iLyr].layer, map);
                bgs[iLyr].layer = undefined;
              }
            }
          } else {
            var idx = BackGroundLayer.map(function(x) {
              return x.name;
            }).indexOf(layer.name);
            if (~idx) {
              BackGroundLayer.splice(idx, 1);
              this.deleteGroupLayer(layer);
              if (map) map.removeLayer(layer);
              $rootScope.$broadcast('gcBackGroundLayerRemoved', layer);
            }
          }
        };

        /**
         *     Ajout d'une couche WMS Tuilée en fond de plan
         * de la carte KIS.
         *
         * @param {[[type]]} baselayer [[Description du layer WMS Tuilé]]
         */
        this.addWmtsBackgroundLayer = function(baselayer, opacity, zIndex) {
          //-- Etre sût que le système de projection
          //-- de la couche est disponible.
          proj4.defs(baselayer.projection, baselayer.projectiondesc);
          //-- Définition aussi en upper/lowercase si le epsg
          //-- est en minuscule dans le getcapabilities par ex.
          proj4.defs(
            baselayer.projection.toLowerCase(),
            baselayer.projectiondesc
          );
          proj4.defs(
            baselayer.projection.toUpperCase(),
            baselayer.projectiondesc
          );

          var projection = ol.proj.get(baselayer.projection);
          if (projection.getExtent() == null)
            projection.setExtent(baselayer.projectionExtent);

          //-- Récupération de la description
          //-- de la source depuis les capabilities.
          var opts = ol.source.WMTS.optionsFromCapabilities(
            baselayer.capabilities,
            {
              layer: baselayer.layer,
              matrixSet: baselayer.TileMatrixSet,
            }
          );

          var wms = new ol.layer.Tile({
            name: baselayer.name,
            //                            label : baselayer.name,
            theme: 'WebBackGround',
            type: 'WMTS',
            //                            gctype : "g2c",
            //                            KEY : baselayer.key,
            opacity: opacity,
            zIndex: zIndex,
            source: new ol.source.WMTS(opts), //,
            capabilities: baselayer.capabilities,
          });

          this.addBackGroundLayer(wms);
        };

        /**
         *     Ajout de chaque layer du groupe de layer
         *  à la carte, à condition que la plage d'échelle corresponde.
         */
        this.addGroupedBackgroundLayerStep2 = function(
          baselayer,
          map,
          callOrigin
        ) {
          var bgs,
            bls,
            thisGcLayers = this;

          this.clearBackgroundLayer(map, callOrigin);
          bgs = baselayer.backgrounds;
          bls = BaseMapFactory.resources.basemaps;
          var mapResolution = map.getView().getResolution();
          let mapScale = gaJsUtils.resolutionToScale(mapResolution, 'm')
          this.addingLayersOfGroup = true;
          for (var iLyr = 0; iLyr < bgs.length; iLyr++) {
            if (bgs[iLyr].minscale == null) bgs[iLyr].minscale = 0;
            if (
              bgs[iLyr].layer != undefined &&
              (bgs[iLyr].maxscale <= mapScale ||
                bgs[iLyr].minscale > mapScale)
            ) {
              this.removeBackGroundLayer(bgs[iLyr].layer);
              bgs[iLyr].layer = undefined;
            }
            bls.forEach(function(bl) {
              if (
                bgs[iLyr].name == bl.name &&
                (bgs[iLyr].maxscale == undefined ||
                  bgs[iLyr].maxscale > mapScale) &&
                (bgs[iLyr].minscale <= mapScale ||
                  bgs[iLyr].minscale == null)
              ) {
                if (bgs[iLyr].layer == undefined) {
                  bgs[iLyr].zIndex = bgs.length - 1 - iLyr;
                  bgs[iLyr].layer = thisGcLayers.addBackGroundLayerByDesc(
                    bl,
                    map,
                    callOrigin,
                    bgs[iLyr]
                  );
                }
              }
            });
          }
          this.addingLayersOfGroup = false;

          if (callOrigin != 'fromZoom') {
            const RESOLUTION_PROPERTY = 'resolution';
            const TIME_INTERVAL = 40;
            this.onScaleChangeCb = map
              .getView()
              .on('propertychange', function(e) {
                if (e.key === RESOLUTION_PROPERTY) {
                  // Annuler les appels précédents en attente
                  if (thisGcLayers.addGroupedBackgroundLayerStep2Timeout) {
                    clearTimeout(thisGcLayers.addGroupedBackgroundLayerStep2Timeout);
                  }

                  // Définir un nouveau délai pour la fonction addGroupedBackgroundLayerStep2
                  thisGcLayers.addGroupedBackgroundLayerStep2Timeout = setTimeout(function() {
                    thisGcLayers.addGroupedBackgroundLayerStep2(
                      baselayer,
                      map,
                      'fromZoom'
                    );
                  }, TIME_INTERVAL);
                }
            });
          }
        };

        this.addGroupedBackgroundLayer = function(baselayer, map) {
          currentComposedLyr = baselayer;
          if (BaseMapFactory.resources.basemaps.length == 0) {
            BaseMapFactory.get().then(function() {
              this.addGroupedBackgroundLayerStep2(baselayer, map);
            });
          } else {
            this.addGroupedBackgroundLayerStep2(baselayer, map);
          }
        };

        this.addBackGroundLayerByDesc = function(
          baselayer,
          map,
          callOrigin,
          lyrComp
        ) {
          var addedLayer,
            wms,
            opacity = 1.0,
            pref = '',
            zIndex = 0;

          if (!this.addingLayersOfGroup){
            this.clearBackgroundLayer(map, callOrigin);
          }
          if (callOrigin != 'fromZoom'){
            this.rmOnScaleChangeCb();
          }
          if (lyrComp) {
            //-- Permet de ne pas sélectionner les layers
            //-- qui font partie d'un layer composé
            //-- dans la liste des layers quand le layer
            //-- en cours d'affichage est un layer composé.
            pref = '_';
            if (lyrComp.opacity != undefined) opacity = lyrComp.opacity / 100.0;
            zIndex = lyrComp.zIndex;
          } else if (baselayer.opacity != undefined){
            opacity = baselayer.opacity / 100.0;
          }

          if (baselayer.type == 'OSM') {
            //http://wxs.ign.fr/e4i6cff4ot440vro0byfkciw/geoportail/wmts?Layer=GEOGRAPHICALGRIDSYSTEMS.MAPS&style=normal&Style=normal&TileMatrixSet=PM&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image%2Fjpeg&TileMatrix=8&TileCol=130&TileRow=90
            var nuages = new ol.layer.Tile({
              opacity: opacity,
              zIndex: zIndex,
              name: pref + baselayer.name,
              label: baselayer.name,
              source: new ol.source.OSM(),
              theme: 'WebBackGround',
              visible: true,
              type: 'OSM',
              gctype: 'g2c',
            });

            this.addBackGroundLayer(nuages);
            addedLayer = nuages;

            //scope.map.addLayer(nuages)
          } else if (baselayer.type == 'BING') {
            //key Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3
            //Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3http://wxs.ign.fr/e4i6cff4ot440vro0byfkciw/geoportail/wmts?Layer=GEOGRAPHICALGRIDSYSTEMS.MAPS&style=normal&Style=normal&TileMatrixSet=PM&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image%2Fjpeg&TileMatrix=8&TileCol=130&TileRow=90
            nuages = new ol.layer.Tile({
              name: baselayer.name,
              label: baselayer.name,
              visible: true,
              preload: Infinity,
              theme: 'WebBackGround',
              type: 'BING',
              gctype: 'g2c',
              source: new ol.source.BingMaps({
                key: baselayer.key,
                imagerySet: baselayer.layer,
              }),
            });
            this.addBackGroundLayer(nuages);
            // $rootScope.$broadcast('gcBackGroundLayerChange',nuages);
            addedLayer = nuages;
          } else if (baselayer.type == 'WMS') {
            var verionWMS =
              baselayer.version == undefined ? '1.1.1' : baselayer.version;
            if (baselayer.version == undefined) {
              wms = new ol.layer.Tile({
                opacity: opacity,
                zIndex: zIndex,
                name: pref + baselayer.name,
                label: baselayer.name,
                theme: 'WebBackGround',
                type: 'WMSBACK',
                gctype: 'g2c',
                source: new ol.source.TileWMS({
                  version: verionWMS,
                  url: baselayer.url,
                  params: {
                    LAYERS: baselayer.layer,
                    TILED: true,
                    VERSION: verionWMS,
                    FORMAT: 'image/png',
                    TRANSPARENT: true,
                    bgcolor: '0xFFFFFF',
                  },
                  serverType: 'geoserver',
                  crossOrigin: 'anonymous',
                }),
              });
            } else {
              wms = new ol.layer.Tile({
                opacity: opacity,
                zIndex: zIndex,
                name: pref + baselayer.name,
                label: baselayer.name,
                theme: 'WebBackGround',
                type: 'WMSBACK',
                gctype: 'g2c',
                source: new ol.source.TileWMS({
                  version: verionWMS,
                  url: baselayer.url,
                  params: {
                    LAYERS: baselayer.layer,
                    TILED: true,
                    VERSION: verionWMS,
                    SRS: 'EPSG:3857',
                    TRANSPARENT: true,
                    FORMAT: 'image/png'
                  },
                  serverType: 'geoserver',
                  crossOrigin: 'anonymous',
                }),
              });
            }

            this.addBackGroundLayer(wms);
            addedLayer = wms;
          } else if (baselayer.type == 'GEOWEBCACHE') {
            var verionWMS =
              baselayer.version == undefined ? '1.1.1' : baselayer.version;

            if (baselayer.version == undefined) {
              wms = new ol.layer.Tile({
                opacity: opacity,
                zIndex: zIndex,
                name: pref + baselayer.name,
                label: baselayer.name,
                theme: 'WebBackGround',
                type: 'GEOWEBCACHE',
                gctype: 'g2c',
                source: new ol.source.TileWMS({
                  version: verionWMS,
                  url: baselayer.url,
                  params: {
                    LAYERS: baselayer.layer,
                    TILED: false,
                    VERSION: verionWMS,
                    FORMAT: 'image/jpeg',
                  },
                  serverType: 'geoserver',
                }),
              });
            } else {
              wms = new ol.layer.Tile({
                opacity: opacity,
                zIndex: zIndex,
                name: pref + baselayer.name,
                label: baselayer.name,
                theme: 'WebBackGround',
                type: 'GEOWEBCACHE',
                gctype: 'g2c',
                source: new ol.source.TileWMS({
                  version: verionWMS,
                  url: baselayer.url,
                  params: {
                    LAYERS: baselayer.layer,
                    TILED: true,
                    VERSION: verionWMS,
                    FORMAT: 'image/jpeg',
                    SRS: 'EPSG:3857',
                  },
                  serverType: 'geoserver',
                }),
              });
            }

            this.addBackGroundLayer(wms);
            addedLayer = wms;
          } else if (baselayer.type == 'IGN') {
            var resolutions = [];
            var matrixIds = [];
            var proj3857 = ol.proj.get('EPSG:3857');
            var maxResolution = ol.extent.getWidth(proj3857.getExtent()) / 256;

            for (var i = 0; i < 18; i++) {
              matrixIds[i] = i.toString();
              resolutions[i] = maxResolution / Math.pow(2, i);
            }

            var tileGrid = new ol.tilegrid.WMTS({
              origin: [-20037508, 20037508],
              resolutions: resolutions,
              matrixIds: matrixIds,
            });
            //http://wxs.ign.fr/e4i6cff4ot440vro0byfkciw/geoportail/wmts?Layer=GEOGRAPHICALGRIDSYSTEMS.MAPS&style=normal&Style=normal&TileMatrixSet=PM&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image%2Fjpeg&TileMatrix=8&TileCol=130&TileRow=90
            // API key valid for 'openlayers.org' and 'localhost'.
            // Expiration date is 06/29/2018.
            var key = '8x5snhe1rmiixxbsyz4uh418';

            var format_k = 'image/jpeg';
            if (baselayer.layer == 'CADASTRALPARCELS.PARCELS') {
              format_k = 'image/png';
            }

            var ign_source = new ol.source.WMTS({
              url: 'http://wxs.ign.fr/' + baselayer.key + '/wmts',
              layer: baselayer.layer /*'GEOGRAPHICALGRIDSYSTEMS.MAPS'*/,
              matrixSet: 'PM',
              format: format_k,
              projection: 'EPSG:3857',
              tileGrid: tileGrid,
              type: 'WMTS',
              style: 'normal',
              attributions: [
                new ol.Attribution({
                  html:
                    '<a href="http://www.geoportail.fr/" target="_blank">' +
                    '<img src="http://api.ign.fr/geoportail/api/js/latest/' +
                    'theme/geoportal/img/logo_gp.gif"></a>',
                }),
              ],
            });

            var ign = new ol.layer.Tile({
              opacity: opacity,
              zIndex: zIndex,
              name: pref + baselayer.name,
              label: baselayer.name,
              theme: 'WebBackGround',
              type: 'IGN',
              gctype: 'g2c',
              source: ign_source,
            });
            this.addBackGroundLayer(ign);
            addedLayer = ign;
            //$rootScope.$broadcast('gcBackGroundLayerChange',nuages);
            //gclayers.addBackGroundLayerByLabel("Bing Aerial et Label");
          } else if (baselayer.type == 'WMTS') {
            this.addWmtsBackgroundLayer(baselayer, opacity, zIndex);
            addedLayer = baselayer;
          } else if (baselayer.type == 'COMPOSED') {
            this.addGroupedBackgroundLayer(baselayer, map);
          } else if (baselayer.type === 'ARCGIS') {

            // Récupération des Ids des couches
            const layersId = [];
            for(let layer of baselayer.layers) {
              layersId.push(layer.id);
            }

            // Construction du paramètre dynamicLayers contenant
            // les couches qui feront partie du FDP
            let dynamicLayers = [];
            for (const layerId of layersId) {
              let dynamicLayerEntry = {
                id: layerId,
                source: {
                  type: 'mapLayer',
                  mapLayerId: layerId,
                },
              };
              dynamicLayers.push(dynamicLayerEntry);
            }

            const params = {
              dynamicLayers: angular.toJson(dynamicLayers),
              tiled: true,
              tilesorigin: '-20037508.342789236,-20037508.342789236',
              type: 'esri',
              bboxSR: map.getView().getProjection().getCode().split(':')[1]
            };

            const datastore = DataStoreFactory.getDataStoreByName(baselayer.esriDatastore);
            const source = new olkis.KISTileArcGISRest({
              params: params,
              url: (datastore.useproxy && datastore.proxykey) ?
                '../services/esriproxy/' + datastore.proxykey + '/MapServer' :
                datastore.url,
              crossOrigin: 'anonymous',
            });

            const layer = new ol.layer.Tile({
              opacity: opacity,
              zIndex: zIndex,
              name: pref + baselayer.name,
              label: baselayer.name,
              theme: 'WebBackGround',
              type: 'IGN',
              gctype: 'g2c',
              source: source,
            });
            this.addBackGroundLayer(layer);
            addedLayer = layer;
          }
          return addedLayer;
        };

        this.getEditableLayer = function() {
          return OperationalLayer;
        };

        /**
         * [addToSelectFilter Add layer to selection filter ]
         * @param {[type]} layer [description]
         */
        this.addToSelectFilter = function(layer) {
          SelectFilter.push(layer);
        };

        this.removeSelectFilter = function(layer) {
          //console.log(layer.name);
          var deleteIndex = SelectFilter.map(function(x) {
            return x.name;
          }).indexOf(layer.name);
          if (deleteIndex != -1) {
            SelectFilter.splice(deleteIndex, 1);
          }
        };

        this.clearListOfSelectableLayers = function () {
          SelectFilter.splice(0, SelectFilter.length);
        };

        function fTypeMatchMapResolution(fType, map) {
          if (fType.maxScale == undefined && fType.minScale == undefined){
            return true;
          }

          var mapResolution = map.getView().getResolution();
          var dpi = 25.4 / 0.28;

          if (fType.maxScale != undefined) {
            var ftiMaxResultion = parseFloat(fType.maxScale) / (39.37 * dpi);
            if (mapResolution > ftiMaxResultion) return false;
          }
          if (fType.minScale != undefined) {
            var ftiMinResultion = fType.minScale / (39.37 * dpi);
            if (mapResolution < ftiMinResultion) return false;
          }
          return true;
        }

        this.getSelectFilterasStringuid = function(map) {
          var res = '';
          SelectFilter.forEach(function(fType) {
            try {
              if (fTypeMatchMapResolution(fType, map))
                res = res + fType['fti'].uid + ',';
            } catch (e) {
              e.stack;
            }
          });
          res = res.substr(0, res.length - 1);
          return res;
        };

        /**
         * [createselectlayer Creation de la couche de selection]
         * @return {[type]} [description]
         */
        this.createselectlayer = function() {
          selectSource = new ol.source.Vector({
            format: new ol.format.GeoJSON(),
            attributions: [
              new ol.Attribution({
                html: '<i class="g2c-name"></i> ',
              }),
            ],
          });

          selectLayer = new ol.layer.Vector({
            source: selectSource,
            style: gcStyleFactory.getStyle('select'),
            label: 'Selection',
            name: 'Selection',
            theme: 'Techniques',
            gctype: 'g2c',
          });
          selectLayer.setZIndex(9999);

          TechnicalLayer.push(selectLayer);
          gcPropertiesLayers(selectLayer);
          $rootScope.$broadcast('gcTechnicalLayerChange', selectLayer);
        };

        this.createmeasurelayer = function() {
          var measureSource;
          measureSource = new ol.source.Vector({
            format: new ol.format.GeoJSON(),
            attributions: [],
          });

          var wfs_layer = new ol.layer.Vector({
            source: measureSource,
            style: gcStyleFactory.getStyle('measure'),
            label: 'Mesure',
            name: 'Mesure',
            theme: 'Techniques',
            gctype: 'g2c',
          });

          wfs_layer.setZIndex(9999);
          measureLayer = wfs_layer;

          TechnicalLayer.push(wfs_layer);
          gcPropertiesLayers(wfs_layer);
          $rootScope.$broadcast('gcTechnicalLayerChange', wfs_layer);
        };

        this.createAnnotationLayer = function() {
          var annotSource;
          annotSource = new ol.source.Vector({
            format: new ol.format.GeoJSON(),
            attributions: [],
          });

          var wfs_layer = new ol.layer.Vector({
            source: annotSource,
            style: gcStyleFactory.getStyle('measure'),
            label: 'Annotations',
            name: 'Annotations',
            theme: 'Techniques',
            gctype: 'g2c',
          });

          wfs_layer.setZIndex(9999);
          annotationLayer = wfs_layer;

          TechnicalLayer.push(wfs_layer);
          gcPropertiesLayers(wfs_layer);
          $rootScope.$broadcast('gcTechnicalLayerChange', wfs_layer);
        };

        this.ActivateSnapLayer = function() {
          SnapActivate = true;
        };

        this.DeactivateSnapLayer = function() {
          SnapActivate = false;
        };

        this.getSnapLayerActivation = function() {
          return SnapActivate;
        };

        this.getSnapLayer = function() {
          return layerSnap;
        };

        this.addSnapLayer = function(layer) {
          layerSnap = layer;
        };

        this.deleteSnapLayer = function() {
          try {
            layerSnap.getSource().clear();
          } catch (e) {
            console.error(e.stack);
          }
        };

        this.createimportlayer = function() {
          importSource = new ol.source.Vector({
            format: new ol.format.GeoJSON(),
          });

          var wfs_layer = new ol.layer.Vector({
            source: importSource,
            style: gcStyleFactory.getStyle('import'),
            label: 'Import',
            name: 'Import',
            theme: 'Techniques',
            gctype: 'g2c',
          });

          importLayer = wfs_layer;

          TechnicalLayer.push(wfs_layer);
          gcPropertiesLayers(wfs_layer);
          $rootScope.$broadcast('gcTechnicalLayerChange', wfs_layer);
        };

        this.createguidelayer = function() {
          guideSource = new ol.source.Vector({
            format: new ol.format.GeoJSON(),
          });

          var wfs_layer = new ol.layer.Vector({
            source: guideSource,
            style: gcStyleFactory.getStyle('import'),
            label: 'Guide',
            name: 'Guide',
            theme: 'Techniques',
            gctype: 'g2c',
          });

          guideLayer = wfs_layer;

          TechnicalLayer.push(wfs_layer);
          gcPropertiesLayers(wfs_layer);
          $rootScope.$broadcast('gcTechnicalLayerChange', wfs_layer);
        };

        this.createhighlightlayer = function(map) {
          var collection = new ol.Collection();
          var featureOverlay = new ol.layer.Vector({
            map: map,
            source: new ol.source.Vector({
              features: collection,
              useSpatialIndex: false, // optional, might improve performance
            }),
            style: gcStyleFactory.getStyle('highlight'),
            zIndex: 10001,
            updateWhileAnimating: true, // optional, for instant visual feedback
            updateWhileInteracting: true, // optional, for instant visual feedback
          });
          layerHighlight = featureOverlay;
          layerHighlight.setMap(map);
        };

        this.createeditlayer = function() {
          drawSource = new ol.source.Vector({
            format: new ol.format.GeoJSON(),
          });
          var wfs2_layer = new ol.layer.Vector({
            source: drawSource,
            style: gcStyleFactory.getStyle('edit'),
            label: 'Edition',
            name: 'Edition',
            theme: 'Techniques',
            gctype: 'g2c',
            zIndex: 10000
          });

          drawLayer = wfs2_layer;

          TechnicalLayer.push(wfs2_layer);
          gcPropertiesLayers(wfs2_layer);
          $rootScope.$broadcast('gcTechnicalLayerChange', wfs2_layer);
        };

        this.getBackGroundLayer = function() {
          return BackGroundLayer;
        };

        /**
         * clearOperationalLayer
         */
        this.clearOperationalLayer = function() {
          grouplayers = {};
          OperationalLayer.clear();
          $rootScope.$broadcast('gcOperationalLayerChange', '', 'applyall');
        };

        /**
         * Delete all technical layers from this object and from the map.
         */
        this.clearTechnicalLayers = function (map) {
          //-- Enlever les couches de la carte OpenLayers.
          for (const layer of TechnicalLayer) {
            map.removeLayer(layer);
          }
          //-- Vider la liste des couhces techniques.
          TechnicalLayer.splice(0, TechnicalLayer.length);
          //-- Réinitialiser chaquevariable qui point sur une couche technique.
          selectLayer = {};
          importLayer = {};
          guideLayer = {};
          drawLayer = {}; // edit layer
          measureLayer = {};
          annotationLayer = {};
        };

        this.getOperationalLayer = function() {
          return OperationalLayer.getArray();
        };

        this.getOperationalLayerCollection = function() {
          return OperationalLayer;
        };

        this.refreshlayerByid = function(id) {
          var layers = OperationalLayer.getArray();
          var l = null;
          layers.forEach(function(ft) {
            if (!angular.isUndefined(ft.fti)) {
              if (ft.fti.uid == id) {
                l = ft;
              }
            }
          });
          if (l != null) {
            $rootScope.$broadcast(
              'gcOperationalLayerChange',
              l,
              'refreshlayer'
            );
          }
        };

        this.getOperationalLayerById = function(id) {
          var res = null;
          OperationalLayer.forEach(function(ft) {
            if (ft.fti.uid == id) {
              res = ft.fti;
              //break;
            }
          });
          return res;
        };

        this.getOperationalLayerByLayerName = function(name) {
          var res = null;
          OperationalLayer.forEach(function(ft) {
            if (ft.fti.name == name) {
              res = ft;
            }
          });
          return res;
        };

        this.getOperationalLayerByName = function(name) {
          var res = null;
          OperationalLayer.forEach(function(ft) {
            if (ft.fti.name == name) {
              res = ft.fti;
            }
          });
          return res;
        };

        /** type different of ESRI **/
        /**
         * [addOperationalLayer description]
         * @param {*} layer [description]
         */
        this.addOperationalLayerg2c = function(layer) {
          const layerFtiId = (layer.get('fti') || {}).uid;

          // méthode interne
          // Si "layer" est déjà présente dans la collection => return true, sinon return false
          // si la propriété est différente alors la couche existante récupère la valeur de la propriété de "layer"
          const replaceExistingLayerProperties = (collection) => {
            const collLayer = collection.getArray().find(val => {
              const ftiUid = (val.get('fti') || {}).uid;
              return typeof ftiUid === 'string' && ftiUid === layerFtiId;
            });

            const layerProperties = Object.keys(layer.getProperties());

            for (const layerPropKey of layerProperties) {
              if (collLayer && collLayer.get(layerPropKey) !== layer.get(layerPropKey)) {
                collLayer.set(layerPropKey, layer.get(layerPropKey));
              }
            }

            return !!collLayer;
          };

          const toAddG2c = !replaceExistingLayerProperties(OperationalLayerg2c);
          const toAdd = !replaceExistingLayerProperties(OperationalLayer);

          if (toAddG2c || toAdd) {
            if (toAddG2c) {
              OperationalLayerg2c.insertAt(layer.get('index'), layer);
              if (layer.selected) {
                this.addToSelectFilter(layer);
              }
            }

            if (toAdd) {
              OperationalLayer.insertAt(layer.get('index'), layer);
            }

            $rootScope.$broadcast('gcOperationalLayerChange', layer, 'add');
          }
        };

        /**
         * clearOperationalLayer
         */
        this.clearOperationalLayerg2c = function(b) {
          if (b) {
            this.clearLayersFromGroupLayer();
          } else {
            this.clearGroupLayer();
          }
          //-- Il faut enlever les couches G2C de l'ensemble des couches.
          //-- Sinon on cumule dans la liste "OperationalLayer" tous les layers
          //-- à chaque changement de modèle de carte ce qui peut aboutir
          //-- à des incohérences à l'impression par exemple (visibilité).
          var iLyr1, iLyr2;
          for (iLyr1 = 0; iLyr1 < OperationalLayerg2c.getLength(); iLyr1++)
            for (iLyr2 = 0; iLyr2 < OperationalLayer.getLength(); iLyr2++)
              if (
                OperationalLayerg2c.item(iLyr1) == OperationalLayer.item(iLyr2)
              )
                OperationalLayer.remove(OperationalLayer.item(iLyr2));
          OperationalLayerg2c.clear();
          $rootScope.$broadcast('gcOperationalLayerChange', '', 'applyall');
        };

        this.getOperationalLayerg2c = function() {
          return OperationalLayerg2c.getArray();
        };

        this.getOperationalLayerg2cCollection = function() {
          return OperationalLayerg2c;
        };

        /**
         * ESRI layers
         */
        this.addOperationalLayerESRI = function(layer) {
          OperationalLayerESRI.insertAt(layer.get('index'), layer);
          if (layer.selected) {
            this.addToSelectFilter(layer);
          }
          OperationalLayer.insertAt(layer.get('index'), layer);
          $rootScope.$broadcast('gcOperationalLayerChange', layer, 'add');
        };

        /**
         * clearOperationalLayerESRI
         */
        this.clearOperationalLayerESRI = function(b) {
          if (b) {
            this.clearLayersFromGroupLayer();
          } else {
            this.clearGroupLayer();
          }
          OperationalLayerESRI.clear();
          $rootScope.$broadcast('gcOperationalLayerChange', '', 'applyall');
        };

        this.getOperationalLayerESRI = function() {
          return OperationalLayerESRI.getArray();
        };

        this.getOperationalLayerESRICollection = function() {
          return OperationalLayerESRI;
          //selectFeatures = features;
          //$rootScope.$broadcast('gcSelectChange');
        };

        /**
         * [getHighLightLayer description]
         * @return {[type]} [description]
         */
        this.getHighLightLayer = function() {
          return layerHighlight;
        };

        this.getDrawLayer = function() {
          return drawLayer;
        };

        this.getMeasureLayer = function() {
          return measureLayer;
        };

        this.getAnnotationLayer = function() {
          return annotationLayer;
        };

        this.clearEditLayer = function() {
          drawSource.clear();
        };

        this.addhighLightFeatures = function(features) {
          var f = selectSource.getFeatureById(features);
          if (f){
            layerHighlight.getSource().addFeature(f);
          }
        };

        this.addhighLightFeature = function(f, clearStyleBefore = true) {
          const isPresent = layerHighlight.getSource().getFeatures().some(
            feat => feat.get('name') === f.get('name'));
          if (!isPresent){
            // KIS-2881: reset du style de la feature
            // KIS-2960: Régression sur le visuel du résultat de la fonction rechercheAmontAval
            if (clearStyleBefore && typeof f.setStyle === 'function') {
              f.setStyle(null);
            }
            layerHighlight.getSource().addFeature(f);
          }
        };

        function clearFeaturesOfLayer(theLayer) {
          let done = false;
          if (typeof theLayer.getSource === 'function') {
            // TODO améliorer ce code!
            // ce code est bizarre, je vois l’intérêt de mettre le done
            // à part de dire que theLayer.getSource().getFeatures() est vide!!
            angular.forEach(theLayer.getSource().getFeatures(), function(feat) {
              theLayer.getSource().removeFeature(feat);
              done = true;
            });
          }
          return done;
        }

        this.removehighLightFeatures = function(features) {
          angular.forEach(layerHighlight.getSource().getFeatures(), function(feat) {
            if (feat.getId() == features.getId()) {
              layerHighlight.getSource().removeFeature(feat);
            }
          });
          if (layerHighlight.getSource().getFeatures().length > 0)
            return layerHighlight.getSource().removeFeature(features);
        };

        this.clearhighLightFeatures = function () {
          let more = true;
          while (more) {
            more = clearFeaturesOfLayer(layerHighlight);
          }
        };

        this.getselectSource = function() {
          return selectSource;
        };

        this.clearSelectLayer = function() {
          selectSource.clear();
        };

        this.getimportsource = function() {
          return importSource;
        };

        this.getImportLayer = function() {
          return importLayer;
        };

        this.getGuideLayer = function() {
          return guideLayer;
        };

        this.getAnnoText = function() {
          return annoText;
        };

        // réception de l'évènement envoyé par jsUtils.customLoader
        // à la récupération d'une erreur de geoserver concernant un composant absent
        // La couche du composant doit être rendue non-visible/non-sélectionnable
        $rootScope.$on('lockGeocatalogLayer', (event, componentName) => {
          const layer = Object.values(grouplayers).reduce((trouve, layers) => {
            return trouve || layers.find(layer => layer.get('name') === componentName);
          }, null);
          if (layer) {
            layer.visible = false;
            layer.selected = false;
            layer.locked = true;
            $rootScope.$broadcast('gcOperationalLayerChange', layer, 'visibility');
          }
        });
      };
      return new gcLayers();
    };

    this.$get.$inject = [
      '$rootScope',
      'gcPropertiesLayers',
      'gcStyleFactory',
      'BaseMapFactory',
      'DataStoreFactory',
      '$timeout',
      'gaJsUtils'
    ];
  };
  return gclayers;
});
