'use strict';
define( function() {
  var defaultfilterswidget = function(
    ParametersFactory,
    $filter,
    extendedNgDialog,
    gclayers,
    $rootScope,
    FeatureTypeFactory,
    defaultFiltersFactory,
    gaJsUtils,
    ConfigFactory,
    gaDomUtils,
    $timeout
  ) {
    return {
      templateUrl:
        'js/XG/widgets/mapapp/defaultfilters/views/defaultfilterswidget.html',
      restrict: 'A',
      link: function(scope) {

        /**
         * Rassemble les variables à initialiser et les méthodes à exécuter
         * au démarrage de la directive
         */
        const onInitWidget = () => {
          // df est la seule variable héritée de l'ancien widget
          // filters est le tableau de filtres du widget récupéré par getFilters
          // current est le filtre courant
          scope.df = {
            filters: [],
            current: false,
          };

          // incrémente le compteur des visites du widget
          defaultFiltersFactory.incrLaunchWidgetCount();

          //récupere les filtres à l'initialisation de la directive
          getFilters();
        };

        /**
         * Retrieve the filters from the ParametersFactory
         */
        const getFilters = (activeFilters) => {
          ParametersFactory.getbytype('defaultfilter').then(
              (res) => {
                if (Array.isArray(res.data)) {
                  scope.df.filters = res.data;
                  scope.currentResources = res.data;
                  // créé df.filters[n].theme
                  createFilterThemes(scope.currentResources);

                  // récupère les filtres actifs pour la construction de l'arborescence des filtres
                  // les filtres actifs sont créés à chaque cochage/décochage
                  if (!activeFilters){
                    if (Array.isArray(defaultFiltersFactory.activeFilters)) {
                      activeFilters = defaultFiltersFactory.activeFilters;
                    } else {
                      activeFilters = defaultFiltersFactory.getActiveFiltersOnStartup(scope.df.filters);
                    }
                  }
                  // construit l'arborescence des filtres dans le panel
                  getFilterGroups(activeFilters);
                }
                // récupère les filtres actifs du service à l'initialisation du widget (sans recharger la page)
                // pour rétablir la visibilité des filtres (ceux du scope, pas les resources)
                if (defaultFiltersFactory.getLaunchWidgetCount() > 0) {
                  setActiveFiltersAfterNavigation();
                }
              });
        };

        // au démarrage de la directive
        onInitWidget();

        /**
         * Ouvre la liste des filtres au clic sur le bouton de configuration violet
         */
        scope.openConfigFilterDialog = () => {
          if (!scope.configDialog){
            scope.getConfig();
            scope.configDialog = extendedNgDialog.open({
              template:
                  'js/XG/widgets/mapapp/defaultfilters/views/modal/modal.defaultfilterswidget.config.html',
              className:
                  'ngdialog-theme-plain overflowY width800 nopadding miniclose filter-config-dialog',
              closeByDocument: false,
              scope: scope,
              draggable: true,
              resizable: true,
              title: $filter('translate')('default_filters.config.title'),
              preCloseCallback: resetFilterDialog
            });
            // limite la taille des popups redimensionnables
            $timeout(()=> {
              const popupPanel = $('.filter-config-dialog').next().children()[0];
              $(popupPanel).css('min-width', '620px');
              refreshFilterListPanelMinHeight();
            },500);
          }
        };

        /**
         * Rafraîchit la propriété css min-height du panel redimensionnable de la popup de la liste des filtres
         * Ajoute 100 à la hauteur de la liste des filtres
         */
        const refreshFilterListPanelMinHeight = () => {
          const popupPanel = $('.filter-config-dialog').next().children()[0];
          $(popupPanel).css('min-height', scope.adjustFilterListHeight(100));
        };

        /**
         *
         * @return {string}
         */
        scope.adjustFilterListHeight = (constant) => {
          const filterHeight = 46;
          const buttonsHeight = 108;
          if (!constant){
            constant = 0;
          }
          if (scope.df.filters){
            return filterHeight * scope.df.filters.length + buttonsHeight + constant + 'px';
          }else{
            return 'auto';
          }
        }
        /**
         * Supprime la popup à la fermeture de la popup de configuration
         */
        const resetFilterDialog = () => {
          scope.configDialog = undefined;
        };

        /**
         * Récupèration de la configuration du widget et la charge dans le scope courant.
         */
        scope.getConfig = () => {
          ConfigFactory.get('widgets', scope.ConfigName).then(
              (res) => {
                if (res.data) {
                  scope.config = res.data;
                  if (!res.data.categories) {
                    scope.config.categories = [];
                  }
                } else {
                  scope.config = {
                    categories: []
                  }
                }
              },
              () => {
                require('toastr').error($filter('translate')(
                    'default_filters.config.getError'
                ));
                scope.config = {
                  categories: []
                }
              });
        };

        /**
         * Au clic sur le bouton "Ajouter" de la table
         */
        const openAddFiltreDialog = () => {
          openAddEditFiltreDialog(true);
        };

        /**
         * Au clic sur le bouton "éditer" d'un filtre"
         */
        const openEditFiltreDialog = () => {
          openAddEditFiltreDialog(false);
        };

        /**
         * Initialise les models du filtre
         * Complète la configuration du widget à transmettre à l'enfant defaultfilterEdition
         * Ouvre la popup d'ajout/édition d'un filtre
         */
        const openAddEditFiltreDialog = (isNew) => {
          let title;
          let nbClauses = 0;
          if (!scope.addEditFilterDialog){
            if (!isNew){
              // edition d'un filtre
              title = 'model.default_filters.editFilter';
              if (scope.edit_resource.theme || (scope.edit_resource.data && scope.edit_resource.data.theme)){
                scope.edit_resource.theme = {
                  name:  scope.edit_resource.theme || scope.edit_resource.data.theme
                }
              }
              scope.df.current = scope.edit_resource;
            }else{
              // nouveau filtre
              title = 'model.default_filters.newFilter';
              addNewFilter();
              scope.edit_resource = scope.df.current;
            }
            if (!scope.config){
              scope.config = {}
            }
            // construction des filterNames et fitleTitles
            // pour la détection de doublons dans les inputs d'ajout de catégorie et de l'ajout/édition d'un filtre
            if (scope.df.filters && scope.df.filters.length > 0){
              scope.config.filterNames = scope.df.filters.filter(f => f.name && f.name !== scope.edit_resource.name).map(f => f.name);
              scope.config.filterTitles = scope.df.filters.filter(f => f.data && f.data.title && f.data.title !== scope.edit_resource.title).map(f => f.data.title);
            }
            scope.config.ConfigName = scope.ConfigName;
            defaultFiltersFactory.getCategories(scope.ConfigName, scope.df.filters).then(
                res => {
                  scope.config.categories = res;
                  const filtersCategories = scope.df.filters.filter(f => f.data && f.data.theme).map(f => {
                   if (typeof f.data.theme === 'string'){
                     f.data.theme = {name:f.data.theme};
                   }
                   return f.data.theme;
                  });
                  for (const category of filtersCategories){
                    if (scope.config.categories.findIndex(c => c.name === category.name) === -1){
                      scope.config.categories.push(category);
                    }
                  }
                  if (scope.groupfilters) {
                    for (const [key, value] of Object.entries(scope.groupfilters)){
                      if (key !== 'default'){
                        const categoryIndex = scope.config.categories.findIndex(c => c.name === key);
                        scope.config.categories[categoryIndex].filters = value.map(f => f.name);
                      }
                    }
                  }
                  scope.addEditFilterDialog = extendedNgDialog.open({
                    template:
                        'js/XG/widgets/mapapp/defaultfilters/views/modal/modal.defaultfilters.filtre.addEdit.html',
                    className:
                        'ngdialog-theme-plain width800 nopadding miniclose addEditFiltre',
                    closeByDocument: false,
                    scope: scope,
                    draggable: true,
                    resizable: true,
                    title: $filter('translate')(title),
                    preCloseCallback: resetEditResource
                  });
                  // limite la taille des popups redimensionnables
                  $timeout(()=> {
                    const popupPanel = $('.addEditFiltre').next().children()[0];
                    $(popupPanel).css('min-width', '585px');

                    refreshFilterPopupPanelMinHeight();
                  },500);
                },
                err => {
                  console.log(err);
                }
            );
          }
        };

        /**
         * Limite le redimensionnement vertical de la popup d'ajout/édition de filtre
         */
        const refreshFilterPopupPanelMinHeight = () => {
          const popupPanel = $('.addEditFiltre').next().children()[0];
          $(popupPanel).css('min-height', calculeFilterPanelHeightByClausesLength());
        };

        /**
         * Calcule la valeur de hauteur minimale à donner à la popup d'ajout/édition de filtre
         * 50px barre de titre, 330px le header, 20px les boutons
         * @return {string} valeur numérique suffixée par 'px'
         */
        const calculeFilterPanelHeightByClausesLength = () => {
          let listHeight  = 50 + 330 + 20;
          if (scope.df.current){
            if (scope.df.current.data && scope.df.current.data.where_clauses) {
              listHeight = 47 * scope.df.current.data.where_clauses.length + listHeight;
            }
          }
          listHeight = listHeight > 800 ? 800 : listHeight;
          return listHeight + 'px';
        };

        /**
         * Supprime la resource éditée
         * afin qu'{@link openEditClause} ouvre un formulaire vierge au clic sur le bouton "Ajouter".<br>
         * En cause, la datatable <code>editList</code> qui ne gère pas la variable <code>edit_resource</code>:
         * on est obligé de la gérer manuellement par un output ($emit) depuis editList
         * @see editList.js (scope.selectResource ligne 577)
         */
        const resetEditResource = () => {
          if (scope.edit_resource){
            scope.edit_resource = undefined;

          }
          if (scope.addEditFilterDialog){
            scope.addEditFilterDialog = undefined;
          }
        };

        /**
         * Initialise un nouveau filtre vierge
         */
        const addNewFilter = () => {
            scope.df.current = {
              id: false,
              name: '',
              data: {
                title: '',
                where_clauses: [],
              },
            };
        };

        /**
         * Supprime le filtre selectionné
         * Exécute un rafraîchissement de l'objet et des tableaux
         * Applique les filtres courant.
         * @see refreshAfterUpdateOrDelete
         * @see applyFilters
         */
        scope.deleteFilter = () => {
          const index = scope.df.filters.findIndex(f => f.name === scope.edit_resource.name);
          // supprime le paramètre du repo (PS: un filtre est un paramètre stocké dans le repo)
          ParametersFactory.remove(scope.df.filters[index].id).then(() => {
            // supprime le filtre de la liste de filtres
            scope.df.filters.splice(index, 1);
            // créé un objet contenant les filtres actifs afin de les restituer à la re-création de l'arborescence des filtres
            let activeFilters = [];
            for (const filters of Object.values(scope.groupfilters)){
              for (const filter of filters){
                if (filter.visible){
                  activeFilters.push(filter.name);
                }
              }
            }
            // re-création de l'arborescence des filtres
            getFilterGroups(activeFilters);
            // applique les filtres
            defaultFiltersFactory.applyFilters(scope.df.filters, activeFilters);

            // rafraîchit la hauteur minimale de la popup resizable de la liste des filtres
            refreshFilterListPanelMinHeight();

            require('toastr').success($filter('translate')('common.ok'));
          });
        };

        /**
         * Au clic sur le bouton 'Annuler' de la popup d'édition d'un filtre
         * Réinitialisation de l'objet <code>df.current</code> à partir de la variable <code>editedFilter</code>
         */
        scope.closeEditForm = () => {
          scope.df.current = false;
          if (scope.addEditFilterDialog){
            scope.addEditFilterDialog.close();
          }
        };

        /**
         * Affiche une nouvelle ligne de clause<br>
         * si 1ère clause, le fti est null<br>
         * sinon on reprend le fti de la clause précédente
         */
        scope.addWhereToCurrentFilter = () => {
          const clause = {
            where: '',
            fti: ''
          }

          // si 1ère clause, le fti est null
          // sinon on reprend le fti de la clause précédente
          if (scope.df.current.data.where_clauses && scope.df.current.data.where_clauses.length > 0){
            const index = scope.df.current.data.where_clauses.length-1;
            clause.fti = scope.df.current.data.where_clauses[index].fti;
          }
          scope.df.current.data.where_clauses.push(clause);
          scope.isNewClause = true;
        };

        /**
         * Sauvegarde le filtre dans les paramètres du repo.<br>
         * Ajoute le filtre ou met à jour si le filtre est existant (= possède un id)
         * @see ParametersFactory.add
         * @see ParametersFactory.update
         */
         scope.saveFilter = () => {
          let activeFilters = [];
          
          if (scope.df.current.hasOwnProperty('visible')){
            delete scope.df.current.visible;
          }
          if (scope.df.current.hasOwnProperty('theme')){
            delete scope.df.current.theme;
          }
          if (!scope.df.current.id) {
            ParametersFactory.add(
                scope.df.current.data,
                'defaultfilter',
                scope.df.current.name
            ).then(() => {
              require('toastr').success($filter('translate')('common.saved'));
              getFilters(activeFilters);
              // rafraîchit la hauteur minimale de la popup resizable d'édition/ajout d'un filtre
              refreshFilterListPanelMinHeight();
              scope.closeEditForm();
            });
          } else {
            ParametersFactory.update(
                scope.df.current,
                scope.df.current.id
            ).then(() => {
              require('toastr').success($filter('translate')('common.updated'));
              getFilters(activeFilters);
              // rafraîchit la hauteur minimale de la popup resizable d'édition/ajout d'un filtre
              refreshFilterListPanelMinHeight();
              scope.closeEditForm();
            });
          }
        };

        /**
         * Ajout/mise à jour d'un tableau de paramètres de type 'defaultfilter'
         */
        scope.saveFilters = () => {
           defaultFiltersFactory.saveFilters(scope.df.filters).then(
               () => {

               },
               err => {
                 require('toastr').error($filter('translate')('default_filters.config.saveError'));
               }
           )
         };

        /**
         * Enlève une clause du filtre courant
         * @param index rang de la clause dans le tableau where_clauses du filtre.
         */
        scope.removeWhereClause = (index) => {
          scope.df.current.data.where_clauses.splice(index, 1);
        };

        // tableau des couches actuellement filtrées dans l'application
        let filteredLayers = defaultFiltersFactory.filteredLayers;

        /**
         * Applique les filtres actifs
         * Insère les clauses where de chaque filtre actif
         * dans la propriété cql_filter de la layer openlayers du composant concerné
         */
        scope.applyFilters = () => {
          const activeFilters = [];
          for (const groupfilters of Object.values(scope.groupfilters)) {
            for (const filter of groupfilters) {
              if (filter.visible) {
                activeFilters.push(filter.name);
              }
            }
            defaultFiltersFactory.applyFilters(scope.df.filters, activeFilters);
          }
        };

        /**
         * Enregistre le nouvel état de la propriété svisible
         * Visibilité du filtre au démarrage
         */
        const updateStartupVisible = () => {
          const index = scope.df.filters.findIndex(r => r.name === scope.edit_resource.name);
          const filter = scope.df.filters[index];
          if (filter){
            scope.df.filters[index].data.svisible = !scope.df.filters[index].data.svisible;
          }
          let visibility = false;
          if (scope.df.filters[index].hasOwnProperty('visible')){
            visibility = scope.df.filters[index].visible;
            delete scope.df.filters[index].visible;
          }
          let theme = null;
          if (scope.df.filters[index].hasOwnProperty('theme')){
            theme = scope.df.filters[index].theme;
          }
          ParametersFactory.update(
            scope.df.filters[index],
            scope.df.filters[index].id
          ).then((res) => {
            require('toastr').success($filter('translate')('common.updated'));
            scope.df.filters[index] = res.data;
            scope.df.filters[index].visible = visibility;
            if (theme){
              scope.df.filters[index].theme = theme;
            }
            refreshAfterUpdateOrDelete(scope.df.filters[index]);
          },() => {
            require('toastr').error($filter('translate')('default_filters.config.init_conf_error'));
          });
        };

        /**
         * Exécute l'initialisation des filtres au démarrage
         * Fonctionne uniquement à la 1ère ouverture de la catégorie contenant le widget
         */
        scope.$watch('df.filters', () => {
          if (scope.df.filters && scope.df.filters.length > 0
              && !scope.isStarted
              && defaultFiltersFactory.getLaunchWidgetCount() < 1) {
            // Active les filtres au démarrage de l'application depuis le watcher sur la variable <code>df.filters</code>.<br>
            // Défini les filtres actifs dans le service.<br>
            //Lors d'une réinitialisation du widget, c'est {@link setActiveFiltersAfterNavigation} qui est exécutée à la place.
            defaultFiltersFactory.getActiveFiltersOnStartup(scope.df.filters);
            scope.isStarted = true;
          }
        }, 1);

        /**
         * Est exécutée à l'initialisation du widget à partir de la 2nde ouverture du widget.<br>
         * Le compteur {@link defaultFiltersFactory.getLaunchWidgetCount} stocke le nombre de visites.<br>
         * Redéfinie la visibilité des filtres d'après l'objet des filtres actifs sauvegardé dans le service: <code>activeFilters</code><br>
         * UC: retour dans le widget après navigation dans les rubriques de l'application
         */
        const setActiveFiltersAfterNavigation = () => {
          /*console.log(
            'widget "Filtres de données": nombre de rechargement du widget = ' +
              defaultFiltersFactory.getLaunchWidgetCount()
          );*/
          if (!defaultFiltersFactory.activeFilters) {
            defaultFiltersFactory.activeFilters = defaultFiltersFactory.getActiveFiltersOnStartup(scope.df.filters);
          }
          for (const activeFilterName of defaultFiltersFactory.activeFilters) {
            const index = scope.df.filters.findIndex(f => f.name === activeFilterName);
            scope.df.filters[index].visible = true;
            scope.df.filters[index].data.disabled = false;
          }
        };

        /**
         * A la réactivation d'un filtre désactivé (à la fermeture d'un form portant la fonction deactivatedFilter),
         * coche les cases des filtres actifs à l'initialisation de la carte
         * @see defaultFiltersFactory.restoreFilters
         */
        $rootScope.$on('defaultfilterchange', (event, filter) => {
          for (let i=0; i<scope.df.filters.length; i++){
            if (scope.df.filters[i].id === filter.id){
              scope.df.filters[i].visible = filter.data.svisible;
            }
          }
        });

        /**
         * Lorsqu'un formulaire contient une fonction deactivatedFilter,
         * alors on décoche la case du filtre concerné (res.filter)
         * @see defaultFiltersFactory.removeFilter
         */
        scope.$on('resetActiveFilters', (event, res) => {
          const index = scope.df.filters.findIndex(f => f.name === res.filter.name);
          if (index >= 0){
            scope.df.filters[index].visible = false;
          }
        });

        // variables pour l'ouverture/fermeture des groupes de filtres
        scope.hiddenPanels = {};
        scope.allPanelHidden = false;
        scope.allFiltersVisible = false;


        // groupes de filtres de sauvegarde (restauré après chaque filtrage-texte)
        scope.groupFiltersArray = [];
        /**
         * Construit le tableau des groupes de filtres d'après l'objet des groupes de filtres<br>
         * Exécutée après ouverture de la rubrique du widget et la construction des groupes de filtres<br>
         * Créé le tableau <code>displayGroupFiltersArray</code> qui est un doublon du tableau de groupe nécessaire à l'input de filtrage
         * @see setGroupFilters
         */
        const groupFiltersToArray = () => {
          scope.groupFiltersArray = [];
          for (const [key, value] of Object.entries(scope.groupfilters)){
            scope.groupFiltersArray.push({title: key, filters: value});
          }
          scope.displayGroupFiltersArray = scope.groupFiltersArray;
        };

        /**
         * Rassemble les deux méthodes nécessaires à la construction de l'arborescence de filtres du panel
         * Active les filtres dans l'arborescence si
         * Exécutée à l'ouverture de la rubrique du widget<br>
         * Pour créer un géocatalogue, veuillez vous appuyer sur mapcatalog ou geocatalogfilter
         * @see getFilters
         * @see deleteFilter
         */
        const getFilterGroups = (activeFilters) => {
          if (!activeFilters){
            activeFilters = [];
            for (const groupfilters of Object.values(scope.groupfilters)) {
              for (const filter of groupfilters) {
                if (filter.visible) {
                  activeFilters.push(filter.name);
                }
              }
            }
          }
          setGroupFilters();
          if (Array.isArray(activeFilters) && activeFilters.length > 0 ) {
            for (const groupfilters of Object.values(scope.groupfilters)){
              for (const filter of groupfilters){
                if (activeFilters.includes(filter.name)){
                  filter.visible = true;
                }
              }
            }
          }
          groupFiltersToArray();
        };

        /**
         * Répartie les filtres dans des objets 'groupfilters' <code>{catégorie -> tableau de filtres}</code>
         * suivant la catégorie du filtre (filtre.theme).<br>
         * Trie les filtres de chaque catégorie par ordre alphabétique (avec accents)
         */
        const setGroupFilters  = () => {
          scope.groupfilters = {};
          for (const filter of scope.df.filters) {
            const theme = filter.theme;
            if (theme && theme.length > 0){
              // le filtre possède une catégorie
              if (scope.groupfilters.hasOwnProperty(theme)){
                // la catégorie existe déjà
                scope.groupfilters[theme].push(filter);
              }else {
                scope.groupfilters[theme] = [filter];
              }
            }else{
              // le filtre ne possède pas de catégorie
              if (scope.groupfilters.hasOwnProperty('default')){
                scope.groupfilters['default'].push(filter);
              }else{
                scope.groupfilters['default'] = [filter];
              }
            }
          }
          // trie des filtres
          for (let groupFilter of Object.values(scope.groupfilters)){
            groupFilter.sort((a, b) => {
              const aTitle = a.data.title.toUpperCase();
              const bTitle = b.data.title.toUpperCase();
              return aTitle.localeCompare(bTitle);
            });
          }
        };

        /**
         * Met à jour l'objet <code>groupfilters</code>
         * et les tableaux <code>groupFiltersArray</code> <code>displayGroupFiltersArray</code>
         * après mise à jour ou suppression d'un filtre
         * @param filter filtre mis à jour ou supprimé
         * @param isDeleted est true si la méthode est exécutée après la suppression du filtre en paramètre
         */
        const refreshAfterUpdateOrDelete = (filter, isDeleted) => {

          // met à jour ou supprime le filtre dans la catégorie de filtres
          for (const filters of Object.values(scope.groupfilters)){
            const index = filters.findIndex(f => f.name === filter.name);
            if (index > -1){
              if (isDeleted){
                filters.splice(index, 1);
              }else{
                filters[index] = filter;
              }
            }
          }

          // met à jour ou supprime le filtre dans l'arborescence
          let indexInArray;
          for (let i=0; i<scope.groupFiltersArray.length; i++){
            indexInArray= scope.groupFiltersArray[i].filters.findIndex(f => f.name === filter.name);
            if (indexInArray > -1){
              if (isDeleted){
                scope.groupFiltersArray[i].filters.splice(indexInArray, 1);
                scope.displayGroupFiltersArray[i] = scope.groupFiltersArray[i];
              }else {
                scope.groupFiltersArray[i].filters[indexInArray] = filter;
                scope.displayGroupFiltersArray[i].filters[indexInArray] = filter;
              }
              break;
            }
          }
          scope.currentResources = scope.df.filters;
        };


        /**
         * Ouvre tous les groupes de filtres
         */
        scope.showAllPanels = () => {
          scope.hiddenPanels = {};
          scope.allPanelHidden = false;
        };

        /**
         * Ferme tous les groupes de filtres
         */
        scope.hideAllPanels = () => {
          for (let title in scope.groupfilters) {
            scope.hiddenPanels[title] = true;
            scope.allPanelHidden = true;
          }
        };

        // model de l'input de filtrage
        scope.filterLayers = '';

        /**
         * Filtre des filtres suivant la saisie de l'input situé dans la zone verte du panel
         * Ne tiens pas compte de la casse
         */
        scope.$watch('filterLayers', () => {
          scope.displayGroupFiltersArray = []
          if (scope.filterLayers === '') {
            scope.displayGroupFiltersArray = scope.groupFiltersArray;
            removeTextFilter();
          } else {
            onFilterKeydown();
            for (const group of scope.groupFiltersArray) {
              const filteredElements = group.filters.filter(
                  f => f.data.title.toLowerCase().includes(scope.filterLayers.toLowerCase()));
              const displayedGroup = {title: group.title, filters: filteredElements};
              scope.displayGroupFiltersArray.push(displayedGroup);
            }
          }
        });

        /**
         * Détermine la ligne du tableau en cours d'édition
         * Réceptionne la transmission de la table editlist enfant
         */
        scope.$on('edit_resource', (event, edit_resource)=>{
          scope.edit_resource = edit_resource;
        });

        /**
         * Récupère le filtre courant modifié dans le composant enfant defaultfilter
         * Exécute la sauvegarde du filtre
         */
        scope.$on('saveFilter',(event,filter) =>{
          scope.df.current = filter;
          scope.saveFilter();
        });

        /**
         * Rapatrie la catégorie dans les propriété du filtre
         * pour compenser l'impossibilité pour une string dans <code>editListCfg.col[n]</code> de contenir un point (i.e 'data.theme').<br>
         * Ce défaut de la directive editList impose ici une gymnastique entre les deux propriétés:
         * <ul>
         *   <li><code>filter.data.theme</code>: catégorie sauvegardée ou issue du repo</li>
         *   <li><code>filter.theme</code>: catégorie affichée dans la colonne 'Catégorie' de la table des filtres</li>
         * </ul>
         */
        const createFilterThemes = (filters) => {
          for (const filter of filters){
            if (filter.data && filter.data.theme){
              filter.theme = typeof filter.data.theme === 'string' ? filter.data.theme : filter.data.theme.name;
            }
          }
        };

        /**
         * Ferme la popup d'ajout/édition d'un filtre
         */
        scope.cancel = () =>{
          if (scope.addEditFilterDialog){
            scope.addEditFilterDialog.close()
          }
        };

        /**
         * Préserve la hauteur de la table au clic sur le bouton de filtrage
         * de la popup de la liste des filtres
         */
        scope.$on('searchModeChange', (event, obj) => {
          if (obj.name && obj.searchMode) {
            $('.defaultFilters_config').height('+=35');
          } else if (obj.name && !obj.searchMode) {
            $('.defaultFilters_config').height('-=35');
          }
        });

        /**
         * Supprime la catégorie des filtres et de la configuration
         * supprime oui ou non les filtres contenus dans la catéogie
         * @see defaultFiltersFactory.deleteCategory
         */
        scope.$on('onDeleteCategory', (event, toDeleteCategoryData) => {
          toDeleteCategoryData.filterNames = scope.df.filters.filter(
              f => f.data && (f.data.theme && f.data.theme.name === toDeleteCategoryData.name)).map(
              f => f.name);
          toDeleteCategoryData.filters = scope.df.filters.filter(
              f => f.data && (f.data.theme && f.data.theme.name === toDeleteCategoryData.name));

          onDeleteCategory(toDeleteCategoryData);

        });

        /**
         * Lance la suppression de la catégorie
         * Rafraichit l'arborescence des calques et les filtres de la carte
         * @param toDeleteCategoryData objet de configuration de la suppression
         * {name, filterNames, deletedFilterNames, updatedFilterNames}
         */
        const onDeleteCategory = (toDeleteCategoryData) => {
          defaultFiltersFactory.deleteCategory(scope.config, toDeleteCategoryData, scope.ConfigName).then(
              res => {
                if (res && typeof res === 'object') {
                  const deletedFilterNames = res.deletedFilterNames;
                  const updatedFilters = res.updatedFilters;
                  if (deletedFilterNames && deletedFilterNames.length > 0) {
                    if (toDeleteCategoryData.filterNames.length === deletedFilterNames.length) {
                      // si tous les filtres ont bien été supprimés alors on supprime les filtres du front
                      scope.df.filters = scope.df.filters.filter(
                          f => !deletedFilterNames.includes(f.name));
                      if (defaultFiltersFactory.resources){
                        scope.config.categories = defaultFiltersFactory.resources.categories;
                      }
                      scope.currentResources = scope.df.filters;
                      getFilterGroups();
                      scope.applyFilters();
                      if (scope.addEditFilterDialog){
                        scope.addEditFilterDialog.close();
                      }
                      gaDomUtils.hideGlobalLoader();
                      require('toastr').success($filter('translate')(
                          'default_filters.config.categoryDeleteSuccess'
                      ));
                    }else{
                      gaDomUtils.hideGlobalLoader();
                      require('toastr').info($filter('translate')(
                          'default_filters.config.categoryAndFiltersDeleteCancel'
                      ));
                      // rollback, on restaure les filtres supprimés
                      const filters = scope.df.filters.filter(
                          f => deletedFilterNames.includes(f.name));
                      const activeFilters = Object.values(scope.groupfilters).filter(f => f.visible).map(f => f.name);
                      defaultFiltersFactory.saveFilters(filters).then(
                          () => {
                            getFilters(activeFilters);
                            gaDomUtils.hideGlobalLoader();
                          },
                          err => {
                            console.log(err);
                          }
                      );
                    }
                  } else if (updatedFilters && updatedFilters.length > 0) {
                    getFilterGroups();
                    scope.applyFilters();
                    if (scope.addEditFilterDialog){
                      scope.addEditFilterDialog.close();
                    }
                    gaDomUtils.hideGlobalLoader();
                  }
                }else{
                  getFilterGroups();
                  scope.applyFilters();
                  if (scope.addEditFilterDialog){
                    scope.addEditFilterDialog.close();
                  }
                  gaDomUtils.hideGlobalLoader();
                }
              },
              err => {
                console.log(err);
              }
          );
        }

        /**
         * Objet de configuration de la liste des filtres<br>
         * <ul>
         *   <li><code>sort</code> est true pour activer les fonctions de tri par colonne</li>
         *   <li><code>noHeaderOffset</code> est true pour supprimer le décalage de l'en-tête de la table</li>
         * </ul>
         */
        scope.editListCfg = {
          name: 'configuration',
          dataModule: 'model',
          resource_type: 'default_filters',
          cols: ['name', 'theme'],
          addResourceButton: true,
          addFunction: openAddFiltreDialog,
          editFunction: openEditFiltreDialog,
          removeTemplate:
              'js/XG/widgets/mapapp/defaultfilters/views/modal/modal.defaultfilters.filtre.remove.html',
          extraGlobalActions: [],
          extraActions: [
            {
              iconFn: (filter) => {
                return filter.data.svisible ? '<i class="fa fa-check success"></i>' : '<i class="fa fa-times default"></i>';
              },
              label: $filter('translate')('default_filters.config.on_init'),
              fn: updateStartupVisible
            }
          ],
          warning: 'featuresWarning',
          width: 'width1100',
          allowMultipleSelection: {
            uniqueKey: 'uid',
            deleteTemplate:
                'js/XG/widgets/mapapp/defaultfilters/views/modal/modal.defaultfilters.filtre.remove.html',
          },
          sort: true,
          noHeaderOffset: true,
        };
        scope.deleteModalTitle = $filter('translate')('model.default_filters.delete');

        /**
         * Supprime le filtrage quand on appuie sur la touche "escape"
         * Uniquement lors du 1er caractère écrit:<ul><li>
         * Sauvegarde l'état de d'affichage des groupes</li><li>
         * Dégroupe tous les groupes de couches</li><ul>
         */
        const onFilterKeydown = () => {
          if (!scope.filterState && scope.filterLayers.length > 0) {
            // initialise filterState pour ne plus exécuter après le 1er caractère saisi
            scope.filterState = true;
            if (Object.keys(scope.hiddenPanels).length > 0) {
              scope.hiddenPanelsPrevState = angular.copy(scope.hiddenPanels);
            }
            // modifie la variable allPanelHidden
            scope.showAllPanels();
          }
        };

        /**
         * Après avoir effacé le texte de saisie du filtre,
         * restaure l'état initial d'affichage des groupes de couches
         */
        const removeTextFilter = () => {
          if (scope.filterLayers.length === 0) {

            if (scope.filterState){
              scope.filterState = false;
            }

            // restaure un état initial existant
            if (scope.hiddenPanelsPrevState) {
              scope.hiddenPanels = scope.hiddenPanelsPrevState;
              // restaure allPanelHidden
              if (Object.keys(scope.hiddenPanels).length === Object.keys(scope.groupFiltersArray).length) {
                scope.allPanelHidden = Object.values(scope.hiddenPanels).every(active => active);
              }
              scope.hiddenPanelsPrevState = null;
            }
          }
        };

        /**
         * Au clic sur la case à cocher d'un filtre, met à jour l'état de visibilité du filtre.
         * Applique les filtres actifs
         * @param {string} idFilter propriété id du filtre à modifier
         */
        scope.updateFilterVisibility = (idFilter) => {
          const filter = scope.df.filters.find(filter => filter.id === idFilter);
          if (filter) {
            updateBackupVisibilityState(filter);
            scope.applyFilters();
          }
        };

        /**
         * Met à jour le tableau du service qui contient les noms de calques actifs
         * Ce tableau sert à cocher les filtres actifs quand on retourne dans le widget
         * après avoir navigué dans d'autres catégories
         * @param {object} filter filtre de données issu du tableau scope.df.filters
         */
        const updateBackupVisibilityState = (filter) => {
          if (filter.visible) {
            if (!Array.isArray(defaultFiltersFactory.activeFilters)) {
              defaultFiltersFactory.activeFilters = [filter.name];
            } else if (!defaultFiltersFactory.activeFilters.includes(filter.name)) {
              defaultFiltersFactory.activeFilters.push(filter.name);
            }
          } else {
            if (!Array.isArray(defaultFiltersFactory.activeFilters)) {
              defaultFiltersFactory.activeFilters = [];
            }
            if (defaultFiltersFactory.activeFilters.includes(filter.name)){
              const desactivatedFilternameIndex = defaultFiltersFactory.activeFilters.findIndex(filtername => filtername === filter.name);
              defaultFiltersFactory.activeFilters.splice(desactivatedFilternameIndex, 1);
            }
          }
        };
      },
    };
  };

  defaultfilterswidget.$inject = [
    'ParametersFactory',
    '$filter',
    'extendedNgDialog',
    'gclayers',
    '$rootScope',
    'FeatureTypeFactory',
    'defaultFiltersFactory',
    'gaJsUtils',
    'ConfigFactory',
    'gaDomUtils',
    '$timeout'
  ];
  return defaultfilterswidget;
});
