'use strict';
define(function () {
  var dictwidget = function (
    DICTFactory,
    FeatureTypeFactory,
    DataStoreFactory,
    ReportFactory,
    SridFactory,
    $filter,
    ngDialog,
    gaDomUtils,
    PrintFactory,
    $rootScope,
    $timeout,
    extendedNgDialog,
    ParametersFactory,
    GeometryFactory,
    $q,
    AtlasArcGIS108Service, AtlasService, gaJsUtils, $compile, $location
  ) {
    let parsedJsonAtlasResp;

    const NO_CONFIG = 'NO_CONFIG';

    const DOCS_COUNTS_TEMPLATE = {
      demandsToTreatCount: 0,
      demandsToValidateCount: 0,
      demandsToSendCount: 0,
      incompleteDemandsCount: 0,
      demandsToResendCount: 0,
      demandsToManuallyProcessCount: 0,
    };

    const DICT_CONFIG_TEMPLATE = {
      dictLayerUID: null,
      dictActorUID: null,
      dictDraftUID: null,
      dictDraftItemUID: null,
      dictManuelDocumentsUID: null,

      autoAtlas : {
        intersectBuffer: 2,
        atlasUrl: '',
        a4Model: '',
        a3Model: ''
      },

      automation : {
        notConcernedReport: '',
        bufferSize: 2,
        tabs: [{
          title: $filter('translate')('common.network') + ' 1',
          filter: '',
          reportName: '',
          intersectingComponents: []
        }]
      }
    };

    const BROUILLON_TEMPLATE = {
      commentaire: '',
      piecesJointes: [],
      donneesReponse: null,
    };

    const PIECE_JOINTE_TEMPLATE = {
      donnees: '',
      nom: '',
      type: 'PDF',
    };


    const PRINT_CONF_TEMPLATE = { printTmplts: [], url: '' };
    const styles = {
      MultiPolygon: [
        new ol.style.Style({
          stroke: new ol.style.Stroke({
            color: 'yellow',
            width: 1,
          }),
          fill: new ol.style.Fill({
            color: 'rgba(255, 255, 0, 0.1)',
          }),
        }),
      ],
      Polygon: [
        new ol.style.Style({
          stroke: new ol.style.Stroke({
            color: 'blue',
            lineDash: [4],
            width: 3,
          }),
          fill: new ol.style.Fill({
            color: 'rgba(0, 0, 255, 0.7)',
          }),
        }),
      ],
    };


    const DRAFT_DOCUMENT_TEMPLATE = {
      draftDocsCount: 0,
      loadedDraftDocsCount: 0,
      draftData: {}
    };


    const styleFunction = (feature) => {
      return styles[feature.getGeometry().getType()];
    };

    var dictSource = new ol.source.Vector({
      //create empty vector
    });
    var dictLayer = new ol.layer.Vector({
      source: dictSource,
      style: styleFunction,
    });

    var geoJsoNFormat = new ol.format.GeoJSON();

    return {
      templateUrl: 'js/XG/widgets/mapapp/dict/views/dictWidget.html',

      restrict: 'AE',
      link: function (scope) {
        scope.counts = angular.copy(DOCS_COUNTS_TEMPLATE);

        scope.workspace = {};
        scope.config = PRINT_CONF_TEMPLATE;
        scope.srids = SridFactory.sridsList;
        scope.selectedStore = null;
        scope.datastores = [];
        scope.delDraftItems = [];
        scope.printres = {};
        scope.selectedDocument = null;
        scope.baseDocuments = [];
        scope.documents = [];
        scope.currentSelectedTypeOfDocuments = 'A_RECEPTIONNER';
        scope.workspace.documentsView = 'DOCS_VIEW';
        scope.currentDocumentDraft = DRAFT_DOCUMENT_TEMPLATE;
        scope.automationTabInfos = {
          currentTabNumber: 0,
          dlbConcernedComponents: []
        };

        scope.printScales = [
          {
            value: 500,
            label: '1:500',
          },
          {
            value: 1000,
            label: '1:1000',
          },
          {
            value: 2000,
            label: '1:2000',
          },
          {
            value: 5000,
            label: '1:5000',
          },
          {
            value: 10000,
            label: '1:10000',
          },
          {
            value: 20000,
            label: '1:20000',
          },
          {
            value: 5000,
            label: '1:50000',
          },
        ];

        scope.extraConfig = {
          selectedPrintScale: scope.printScales[2],
          selectedDataStore: null,
        };

        scope.orders = [
          {
            name: 'id',
            label: 'ID(ASC)',
            type: 'ASC',
            active: true,
            text: 'Tri par ID',
            click: handleOrderTypeChanged,
          },
          {
            name: 'data.dateReception',
            label: 'Date réception',
            type: 'ASC',
            active: false,
            text: 'Tri par date de réception',
            click: handleOrderTypeChanged,
          },
        ];

        scope.selectedOrder = scope.orders[0];

        scope.draftMode = false;

        scope.templatefeature = {
          type: 'Feature',
          geometry: {},
          properties: {},
        };

        scope.workspace.currentDraftView = 'default';

        var map = scope.map;
        map.addLayer(dictLayer);

        function getMapCRS() {
          return map
            .getView()
            .getProjection()
            .getCode();
        }

        function handleOrderTypeChanged($event) {
          var item = $event.$parent.item;
          scope.selectedOrder.active = false;
          scope.selectedOrder = item;
          scope.selectedOrder.active = true;
          scope.selectedOrder.type =
            scope.selectedOrder.type == 'ASC' ? 'DESC' : 'ASC';
          scope.selectedOrder.name =
            scope.selectedOrder.name.indexOf('-') == -1
              ? '-' + scope.selectedOrder.name
              : scope.selectedOrder.name.replace('-', '');
        }

        function showConfirm(confirmMessage, okFunction) {
          if (!$('#dataConfirmModal').length) {
            $('body').append(
              '<div class="modal fade" id="dataConfirmModal" tabindex="-1" '
              + 'role="dialog" aria-labelledby="dataConfirmLabel" >'+
              + '<div class="modal-dialog"> <div class="modal-content">'
              + '<div class="modal-header">'
              + ' <button type="button" class="close" data-dismiss="modal" aria-label="Close">'
              + '<span >&times;</span></button> <h4 class="modal-title" id="myModalLabel">'
              + 'Confirmation'
              + '</h4> </div><div class="modal-body"> </div><div class="modal-footer">'
              + '<button type="button" class="btn btn-default" data-dismiss="modal">Non</button>'
              + '<button type="button" class="btn btn-primary" '
              + 'id="dataConfirmOK" data-dismiss="modal">'
              + 'Oui</button> </div></div></div></div>'
            );
          }
          $('#dataConfirmModal')
            .find('.modal-body')
            .text(confirmMessage);
          $('#dataConfirmOK').off('click');
          $('#dataConfirmOK').on('click', okFunction);
          $('#dataConfirmModal').modal({
            show: true,
          });

          return false;
        }

        const getDocument = (docId) => {
          const dictDocument = scope.documents.find(doc => docId==doc.id);
          return dictDocument;
        };

        function getGeometry(documentData) {
          if (documentData.documentSite)
            return documentData.documentSite.emprise;
        }

        function buildDocument(documentData) {
          var builtDoc = {
            state: 'A_TRAITER',
            id: documentData.documentRealId
              ? documentData.documentRealId
              : documentData.documentId,
            title: angular.isDefined(documentData.documentSite)
              ? documentData.documentSite.numConsultation
              : null,
            resume: null,
            isConcerned: !documentData.noConcerned,
            isIncomplete: documentData.incomplete,
            isWaitingForValidation: documentData.waitingForValidation,
            isWaitingForSending: documentData.waitingForSending,
            isWaitingForResending: documentData.waitingForResending,
            data: {},
          };

          updateDocState(builtDoc);
          updateCounts(builtDoc);
          return builtDoc;
        }


        /**
         * Met à jour l'état "à traiter ?" (propriété isNeedTreatement) d'un document
         * en fonction de ses indicateurs d'attente.
         *
         * @param {Object} doc - L'objet document à mettre à jour.
         */
        function updateDocState(doc) {
          doc.isNeedTreatement =
            !doc.isWaitingForValidation &&
            !doc.isWaitingForSending &&
            !doc.isWaitingForResending;
        }

        function updateCounts(doc) {
          if (doc.isNeedTreatement) scope.counts.demandsToTreatCount++;
          if (doc.isWaitingForValidation) scope.counts.demandsToValidateCount++;
          if (doc.isWaitingForSending) scope.counts.demandsToSendCount++;
          if (doc.isIncomplete) scope.counts.incompleteDemandsCount++;
          if (doc.isWaitingForResending) scope.counts.demandsToResendCount++;
        }

        function handleGetDocumentsData(documentsData) {
          const defer = $q.defer();
          scope.counts = angular.copy(DOCS_COUNTS_TEMPLATE);
          scope.documents = [];
          scope.baseDocuments = [];

          if (documentsData != 'NO_CONFIG') {
            for (var i = 0; i < documentsData.length; i++) {
              var documentData = documentsData[i];
              var dictDocument = buildDocument(documentData);
              dictDocument.data = documentData;
              dictDocument.geometry = getGeometry(documentData);

              scope.baseDocuments.push(dictDocument);
            }
          }

          scope.documents = scope.baseDocuments;

          DICTFactory.getmanueldocscount(null, true).then(res => {
            if (res.data) {
              const parsedCount = parseInt(res.data);
              if (parsedCount && parsedCount > 0)
                scope.counts.demandsToManuallyProcessCount = parsedCount;
            }
            scope.$emit('refreshDatatable', { uid: scope.dictDocumentFti.uid });
          }).finally(() => {
            scope.displayLoader = false;
            defer.resolve();
          });

          filterDocumentsByType();

          return defer.promise;
        }


        function receiveDocumentResult(documentData) {
          //for (var i = 0; i < scope.baseDocuments.length; i++) {
          const dictDocument = getDocument(documentData.id);
          if (dictDocument) {
            dictDocument.state = 'A_TRAITER';
            dictDocument.data = documentData;

            dictDocument.geometry = getGeometry(documentData);
            scope.selectedDocument = dictDocument;
            selDocument();
          }
          //}

          filterDocumentsByType('A_TRAITER');
        }

        function receiveDocument(document) {
          gaDomUtils.showGlobalLoader();
          DICTFactory.receivedocument(
            null,
            document.id,
            map
              .getView()
              .getProjection()
              .getCode()
          ).then(function (res) {
            gaDomUtils.hideGlobalLoader();
            receiveDocumentResult(res.data);
          });
        }

        /**
         * Met dans scope.documents la liste des DICTs qui correspondent
         * au paramétre "documenState".
         * Quand "documenState" n'est aps fourni, on utilise le type de document courant
         * (stocké dans scope.currentSelectedTypeOfDocuments).
         *
         * @param {type} documentsState - L'état des documents.
         * @out {type} scope.documents Liste des DICTs qui correspondent au paramètre documentsState
         */
        function filterDocumentsByType(documentsState) {
          if (documentsState)
            scope.currentSelectedTypeOfDocuments = documentsState;
          scope.documents = $.grep(scope.baseDocuments, function (doc) {
            if (scope.currentSelectedTypeOfDocuments == 'NO_CONCERNED')
              return doc.isNeedTreatement && !doc.isConcerned;
            else if (scope.currentSelectedTypeOfDocuments == 'INCOMPLETE')
              return doc.isIncomplete;
            else if (scope.currentSelectedTypeOfDocuments == 'TO_VALIDATE')
              return doc.isWaitingForValidation;
            else if (scope.currentSelectedTypeOfDocuments == 'TO_SEND')
              return doc.isWaitingForSending;
            else if (scope.currentSelectedTypeOfDocuments == 'TO_RESEND')
              return doc.isWaitingForResending;
            else return doc.isNeedTreatement;
          });
        }

        function handleGetDraftForDocument(
          document,
          documentDraft,
          initActiveTab
        ) {
          scope.selectedDocument = document;
          dictSource.clear();

          try {
            selDocument();
          } catch (e) {
            console.error(e);
            require('toastr').error($filter('translate')('dict.failMapDisplay'));
          }
          scope.delDraftItems = [];
          let loadedDraftsCt;
          if (Object.prototype.hasOwnProperty.call(documentDraft,'statut')
             && String(documentDraft.statut) == 'NOT_FOUND'
          )
            loadedDraftsCt = 0;
          else if (Object.prototype.hasOwnProperty.call(documentDraft,'piecesJointes')) {
            var piecesJointes = documentDraft.piecesJointes;
            loadedDraftsCt = piecesJointes != null ? piecesJointes.length : 0;
          } else loadedDraftsCt = 0;

          const draftData = loadedDraftsCt == 0 ? null : documentDraft;
          const cdraftData = angular.copy(draftData);

          scope.currentDocumentDraft = {
            draftDocsCount: loadedDraftsCt,
            loadedDraftDocsCount: loadedDraftsCt,
            draftData: draftData,
            cdraftData: cdraftData,
          };

          if (initActiveTab) {
            scope.workspace.currentDraftView = 'default';
            scope.dictTabs.activeTab = 1;
            scope.workspace.documentsView = 'TREAT_DOC';
          }
        }

        function handleDeleteDraftForDocument(document, draftResponse) {
          if (draftResponse == 'success') {
            scope.selectedDocument.state = 'A_TRAITER';
            scope.selectedDocument.isWaitingForValidation = false;
            filterDocumentsByType();
            scope.demandsSentCount =
              scope.demandsSentCount == 0 ? 0 : scope.demandsSentCount - 1;
            scope.currentDocumentDraft = DRAFT_DOCUMENT_TEMPLATE;
          } else {
            require('toastr').error('Error deleting draft : ' + draftResponse);
          }
        }

        function openByteArrayInNewTab(byteArray, pieceJointe) {
          var file = new Blob([byteArray], {
            type: 'application/' + pieceJointe.type,
          });

          var fileURL = URL.createObjectURL(file);
          window.open(fileURL);
        }

        function getFilteredDraftData() {
          var filteredDraft = angular.copy(
            scope.currentDocumentDraft.draftData
          );
          filteredDraft.piecesJointes = [];
          return filteredDraft;
        }

        /**
         * Create / update / delete the file contained in the repo
         * @param {*} sendDraft true if this function is used for sending draft
         * @param {*} uploadProcessId
         * @param {*} gotoToTreat TRUE si on doit aprés actualisaton se postionner sur la vue
         *                        du document en cours dans le cas d'une DICT qui passe
         *                        à "A TRAITER".
         */
        function handleCreateOrUpdateDraft(sendDraft, gotoToTreat) {
          gaDomUtils.showGlobalLoader();
          DICTFactory.createorupdatedraftfordocument(
            getFilteredDraftData(), scope.delDraftItems, scope.selectedDocument.id).then(() => {
            gaDomUtils.hideGlobalLoader();
            scope.selectedDocument.state = 'DRAFT_RESPONSE_SENT';
            filterDocumentsByType();
            scope.draftItems = [];
            scope.delDraftItems = [];

            const cdraftData = scope.currentDocumentDraft.cdraftData;
            // -- Reste-t-il une carte dans les piéces jointes (Ticket 3330)
            let mapRemaining = false;
            for (const pieceJointe of cdraftData.piecesJointes) {
              scope.currentDocumentDraft.loadedDraftDocsCount++;
              if (pieceJointe.nom && pieceJointe.nom.startsWith('plan')) {
                mapRemaining = true;
              }
            }
            // If we send the draft to backend, we go back to the summary page
            // -- ou s'il ne reste plus de plan (Ticket 3330)
            if (sendDraft || (!mapRemaining && scope.selectedDocument.state!=='A_TRAITER')) {
              if (!gotoToTreat) {
                scope.dictTabs.activeTab = 0;
                scope.currentSelectedTypeOfDocuments = 'TO_VALIDATE';
              }
              scope.reloadData().then(() => {
                if (gotoToTreat && scope.workspace.documentsView!=='TREAT_DOC') {
                  scope.dictTabs.activeTab = 1;
                  scope.workspace.documentsView = 'TREAT_DOC';
                }
              });
            }
          });
        }


        /**
         * Ok pour l'envoi si dans le cas d'une DICT CONCERNE
         * on a au moins 2 documents joints (normalement un rapport et un plan)
         * et dans le cas DICT NON CONCERNE on a au moins 1 document joint (le rapport).
         *
         * Depuis que les rapports sont créés automatiquements, on ne doit plus se référer
         * à scope.draftItems mais à scope.currentDocumentDraft.draftData.piecesJointes
         * (qui est actualaisé en même temps que scope.draftItems).
         */
        const okToSend = () => {
          const piecesJointes = scope.currentDocumentDraft.draftData.piecesJointes;
          return piecesJointes
            && (((scope.currentSelectedTypeOfDocuments==='A_TRAITER'
                || scope.currentSelectedTypeOfDocuments==='TO_VALIDATE')
                && piecesJointes.find(pieceJointe => pieceJointe.nom.startsWith('plan'))
                && piecesJointes.find(pieceJointe => !pieceJointe.nom.startsWith('plan')))
            || (!scope.selectedDocument.isConcerned && piecesJointes.length>0
              && piecesJointes.find(pieceJointe => !pieceJointe.nom.startsWith('plan')))
            );
        };


        function getPieceJointeName(lastPrintUrl) {
          var pieceJointeName;
          var fnInd = lastPrintUrl.indexOf('filename=');
          if (fnInd != -1) {
            //-- Cas de l'atlas
            let endFnInd = lastPrintUrl.indexOf('&', fnInd);
            if (endFnInd == -1) {
              pieceJointeName = lastPrintUrl.substring(fnInd + 9);
            } else {
              pieceJointeName = lastPrintUrl.substring(fnInd + 9, endFnInd);
            }
            pieceJointeName += '_' + scope.currentDocumentDraft.draftDocsCount;
          } else {
            //-- Cas de l'impression printv2.
            pieceJointeName = lastPrintUrl.substring(
              lastPrintUrl.lastIndexOf('/') + 1
            );
          }
          return pieceJointeName;
        }

        function docIsAtlas(lastPrintUrl) {
          return lastPrintUrl.indexOf('/atlas/') != -1;
        }


        /**
         * Appelé pour finaliser l'ajout d'une piéce jointe.
         * la finalisation consiste à positionner des variables
         * de travail qui permette la visualisation correct des documents joints à la DICT.
         *
         * @param {*} pieceJointeName Nom de la piéce jointe
         * @param {*} lastPrintUrl URL d'accés à la piéce jointe
         */
        const draftAdded = (pieceJointeName, lastPrintUrl) => {
          if (!scope.currentDocumentDraft)
            scope.currentDocumentDraft = DRAFT_DOCUMENT_TEMPLATE;
          //piecesJointes
          scope.currentDocumentDraft.draftDocsCount++;

          if (!scope.currentDocumentDraft.draftData
            || !scope.currentDocumentDraft.draftData.piecesJointes) {
            scope.currentDocumentDraft.draftData = angular.copy(
              BROUILLON_TEMPLATE
            );
            scope.currentDocumentDraft.cdraftData = angular.copy(
              scope.currentDocumentDraft.draftData
            );
          }

          // Server draft data
          var pieceJointe = angular.copy(PIECE_JOINTE_TEMPLATE);
          pieceJointe.nom = pieceJointeName;
          if (docIsAtlas(lastPrintUrl)) {
            pieceJointe.type = 'ZIP';
          }

          scope.currentDocumentDraft.draftData.piecesJointes.push(
            pieceJointe
          );
          // Server draft data
          // Client draft data

          const cPieceJointe = angular.copy(pieceJointe);
          scope.currentDocumentDraft.cdraftData.piecesJointes.push(
            cPieceJointe
          );
        };


        /**
         * Récupération de l'url de téléchragement de l'atlas et
         * éventuellemnt de l'identifiant du document DICT.
         *
         * @param {object} params - Paramétres reçus de ArcGisJob ou URL selon l'origine de l'appel.
         * @return {object} Deux informations, l'utl de téléchargeùment de l'atlas et
         *                  id dentifiant du document DICT.
         */
        const getUrl = (params) => {
          const ret = {};
          ret.url = scope.printres.value;
          if (ret.url == null) {
            ret.url = PrintFactory.lastPrintUrl;
          }
          let url;
          if (params && params.url) {
            url = params.url;
            if (params.params) {
              ret.docId = params.params.docId;
              ret.atlasFileName = params.params.atlasFileName;
              ret.updateDocPromises = params.params.updateDocPromises;
              // -- Defer à résoudre une fois les atlas joints.
              ret.defer = params.params.atlasFunctionDefer;
            }
          }
          else {
            if (params) {
              url = params;
            }
            ret.docId = scope.selectedDocument.id;
          }
          if (url) {
            ret.url = url;
          }
          return ret;
        };


        const saveAttachedDocumentByUrl = (downloadInfo) => {
          // -- downloadPrintUrl n'est appelé que suite à la génération
          // -- d'une impression de carte ou d'un atlas, donc le type
          // -- de document peut être indiqué en dur ici ('plan')
          const promise
            = DICTFactory.saveAttachedDocumentByUrl(downloadInfo.docId, downloadInfo.url,
              'plan', false, downloadInfo.atlasFileName);
          promise.then((pieceJointeName) => {
            draftAdded(pieceJointeName.data,downloadInfo.url);
            scope.cleanPrintView();
          });
          return promise;
        };


        function downloadPrintUrl(params, automatic) {
          const downloadInfo = getUrl(params);
          if (automatic) {
            // Une fois l'url de l'impression récupérée, sauvegarde de l'atlas dans le feature
            // -- Ticket 3569
            // -- Génération automatique des documents: ne pas télécherger l'atlas,
            // -- se contenter de le joindre à la DICT.
            saveAttachedDocumentByUrl(downloadInfo).then(() => {
              // -- Le plan a été joint
              const promise = DICTFactory.updateinnerstate(downloadInfo.docId, 'A_VALIDER', true);
              downloadInfo.updateDocPromises.push(promise);
            }).finally(() => {
              // -- Que le plan ait été joint ou non,
              // -- le traitement concernant la génération de ce plan est terminé
              const ind = scope.makingAtlasFor.indexOf(downloadInfo.docId);
              if (ind!==-1) {
                scope.makingAtlasFor.splice(ind,1);
                makingAtlasForAsString();
              }
              if (downloadInfo.defer) {
                // -- On demande la liste actualisée des DICT à partir de là
                // -- (quand génération automatique).
                downloadInfo.defer.resolve();
              }
            });
          }
          else {
            DICTFactory.downloadPrintPDF(downloadInfo.url).then(() => {
              gaDomUtils.hideGlobalLoader();
              saveAttachedDocumentByUrl(downloadInfo);
              // Client draft data
              scope.workspace.currentDraftView = 'default';
            });
          }
        }

        function getFTIByUID(fUID) {
          for (var i = 0; i < scope.featuretypes.length; i++)
            if (scope.featuretypes[i].uid == fUID) return scope.featuretypes[i];
        }

        function initSelectedPrintScale() {
          if (scope.mainConfig.printScale) {
            for (var i = 0; i < scope.printScales.length; i++)
              if (scope.mainConfig.printScale == scope.printScales[i].value)
                scope.extraConfig.selectedPrintScale = scope.printScales[i];
          }
        }

        function initselectedDataStore() {
          if (scope.mainConfig.dictDataStore) {
            for (var i = 0; i < scope.datastores.length; i++) {
              if (scope.mainConfig.dictDataStore == scope.datastores[i].name) {
                scope.extraConfig.selectedDataStore = scope.datastores[i];
              }
            }
          }
        }

        function initReportsDataStore() {
          if (scope.mainConfig.reportsDataStore) {
            for (var i = 0; i < scope.datastores.length; i++) {
              if (
                scope.mainConfig.reportsDataStore == scope.datastores[i].name
              ) {
                scope.extraConfig.reportsDataStore = scope.datastores[i];
              }
            }
          }
        }


        /**
         * Fabrication de l'url de téléchargement du rapport
         * ou du plan qui vient d'être fabriqué.
         *
         * @param {*} endOfUrl Fin de l'url
         * @returns URL compléte d'accés au document (raaport ou plan joint)
         */
        const documentUrl = (endOfUrl) => {
          return location.protocol + '//' + location.hostname + ':' + location.port
            + '/' + endOfUrl;
        };


        scope.commonProcessReports = [];
        scope.commonProcessReportsNames = [];
        scope.filterNames = [];

        function launchCommonReport(reportJson, reportName, suffix) {
          ReportFactory.consultgeneratedreport(reportJson, false,
            reportName, scope.mainConfig.reportsDataStore, 'pdf', true, suffix).then((res) => {
            if (res.data && res.data.strValeur) {
              const url = documentUrl(res.data.strValeur);
              DICTFactory.saveAttachedDocumentByUrl(scope.selectedDocument.id, url,'')
                .then((ret) => {
                  draftAdded(ret.data,url);
                });
            }
            else console.log(res);
          },
          (reason) => {
            console.log(reason);
            require('toastr').error(
              'Une erreur est survenue lors de la requete de génération du rapport: ' +
                reason.statusText
            );
          }
          );
        }

        function launchCommonReportByName(commonReportName) {
          for (var comProcessReportIndex in scope.commonProcessReports) {
            var comProcessReport =
              scope.commonProcessReports[comProcessReportIndex];
            const documentValues = scope.selectedDocument.data.values;
            if (comProcessReport.text == commonReportName) {
              var reportJson = {};
              angular.forEach(comProcessReport.data.parameters, function (
                param
              ) {
                if (param.saisieMode == 'Attribut')
                  reportJson[param.name] =
                    'id' == param.attribut
                      ? scope.selectedDocument.id
                      : documentValues[param.attribut];
                else if (param.saisieMode == 'Statique')
                  reportJson[param.name] = param.value;
              });

              launchCommonReport(reportJson, comProcessReport.data.file,
                documentValues.numconsultationchantier);
            }
          }
        }

        function initSelectedReports() {
          scope.commonProcessReports = [];
          scope.commonProcessReportsNames = [];
          if (scope.mainConfig.dictLayerUID) {
            var dictLayerFTI = getFTIByUID(scope.mainConfig.dictLayerUID);
            if (dictLayerFTI && dictLayerFTI.actions) {
              angular.forEach(dictLayerFTI.actions, function (action) {
                if (
                  action.actionType === 'RAPPORT' &&
                  action.rapports
                ) {
                  angular.forEach(action.rapports, function (actionRapport) {
                    if (actionRapport.typeInfo === 'JasperModel') {
                      scope.commonProcessReportsNames.push(actionRapport.name);
                      scope.commonProcessReports.push({
                        text: actionRapport.name,
                        data: actionRapport,
                        click: function ($event) {
                          launchCommonReportByName($event.$parent.item.text);
                        },
                      });
                    }
                  });
                }
              });
            }
          }
        }

        /**
         * Récupération des noms des filtres sur composants existants dans le tableau
         * scope.filterNames
         */
        const initFilters = () => {
          scope.filtersNames = [];
          ParametersFactory.getbytype('defaultfilter').then(
            (res) => {
              if (res.data && Array.isArray(res.data)) {
                scope.filterNames = res.data.map(obj => obj.data.title);
              }
            },
            () => {
              require('toastr').error(
                $filter('translate')('elastic.search.search_failed'));
            }
          ).finally(() => {
            scope.filterNames.unshift($filter('translate')('dict.noFilter'));
          });

        };


        const errorGettingTemplates = (defer, message) => {
          scope.atlasModelList = [];
          require('toastr').error($filter('translate')('dict.atlasError'));
          $timeout(() => { scope.esriInitializing = false;});
          console.error('Error getting esri atlas templates: ' + message);
          defer.reject();
        };


        scope.atlasModelList = [];
        /**
         * Changement de la configuration d'url de l'atlas arcgis
         */
        const autoAtlasUrlChange = () => {
          const defer = $q.defer();
          // Récupération des informations de l'atlas
          const url = scope.mainConfig.autoAtlas.atlasUrl;
          if (!url || !url.startsWith('http')) {
            defer.resolve();
          }
          else {
          // Récupération des informations de l'atlas
            scope.esriInitializing = true;
            $.get(url + '?f=json').then(
              info => {
                if(typeof info === 'string') {
                  try {
                    parsedJsonAtlasResp = JSON.parse(info);
                  }
                  catch(err) {
                    errorGettingTemplates(defer, '');
                    return;
                  }
                } else {
                  parsedJsonAtlasResp = info;
                }

                // Gestion du cas d'erreur
                if (parsedJsonAtlasResp.error) {
                  errorGettingTemplates(defer, parsedJsonAtlasResp.error.message);
                }
                else if (parsedJsonAtlasResp.parameters) {
                  let template = parsedJsonAtlasResp.parameters.filter(
                    parameter => parameter.name === 'template');
                  if (template && template.length!=0) {
                    template = template[0];
                    if (template.choiceList) {
                    // -- Service atlas avant ArcGIS 10.8
                      scope.atlasMode  = 'esri';
                      scope.atlasModelList = Array.isArray(scope.atlasModelList)
                        ? scope.atlasModelList.splice(0, scope.atlasModelList.length) : [];
                      for (const temp of template.choiceList) {
                        scope.atlasModelList.push({name: temp});
                      }
                      scope.esriInitializing = false;
                      defer.resolve();
                    }
                    else {
                    // -- Service atlas à partir de ArcGIS 10.8
                      scope.atlasMode  = 'esri_108';
                      AtlasArcGIS108Service.initEsri108Atlas(scope, url, info).finally(() => {
                        scope.esriInitializing = false;
                        defer.resolve();
                      });
                    }
                  } else {
                    errorGettingTemplates(defer, '');
                  }
                }
              },
              // Cas d'erreur
              () => {
                errorGettingTemplates(defer, '');
              }
            );
          }
          return defer.promise;
        };


        /**
         * Code factorisé pour être utilisé à deux occasions:
         * 1) A la lecture initiale de la configuration
         * 2) Au changement d'URL du service atlas (dans la fenêtre de configuration)
         *
         * Change la description du modéle chosi pour n'en garder que le nom.
         *
         * @param {*} defer Si fourni faire le resolve en fin de traitement
         */
        const initTemplateOfConfig = (defer) => {
          if (scope.atlasModelList) {
            for (const aModel of scope.atlasModelList) {
              if (scope.mainConfig.autoAtlas.a4Model===aModel.name) {
                scope.mainConfig.autoAtlas.a4Model = aModel;
              }
              if (scope.mainConfig.autoAtlas.a3Model===aModel.name) {
                scope.mainConfig.autoAtlas.a3Model = aModel;
              }
            }
            if (defer) {
              defer.resolve();
            }
          }
        };


        /**
         * Récupérationde la liste des mises en page d'atlas disponibles.
         * Puis initialisation dans les listes de choix des modèles configurés.
         */
        scope.initAtlasModels = () => {
          const defer = $q.defer();
          let datastore = scope.mainConfig.dictDataStore;
          if (!datastore.name) {
            datastore = scope.extraConfig.selectedDataStore;
          }
          if (datastore.type!=='ArcGis') {
            defer.resolve();
          }
          else {
            autoAtlasUrlChange().then(() => {
              initTemplateOfConfig(defer);
            });
          }
          return defer.promise;
        };


        function initModuleConfigurationCallback(res, openConfig) {
          scope.mainConfig =
            res.data == NO_CONFIG
              ? angular.copy(DICT_CONFIG_TEMPLATE)
              : res.data;

          if (!scope.mainConfig.automation) {
            scope.mainConfig.automation = {
              notConcernedReport: '',
              bufferSize: 2,
              tabs: [{
                title: $filter('translate')('common.network') + ' 1',
                filter: '',
                reportName: '',
                intersectingComponents: []
              }]
            };
          }

          if (!scope.mainConfig.autoAtlas) {
            scope.mainConfig.autoAtlas = {
              intersectBuffer: 2,
              atlasUrl: '',
              a4Model: '',
              a3Model: ''
            };
          }

          if (scope.mainConfig.automation.tabs.length !==
            scope.automationTabInfos.dlbConcernedComponents.length) {
            scope.automationTabInfos.dlbConcernedComponents = [];
            for (let tab of scope.mainConfig.automation.tabs) {
              // Dual-list-box de configuration des composants à intersecter pour l'état concerné
              scope.automationTabInfos.dlbConcernedComponents.push({
                leftData: tab.intersectingComponents,
                leftDisplayAttribute: 'alias',
                rightData: angular.copy(scope.lightFeatureTypes),
                rightDisplayAttribute: 'alias',
                leftTitle: 'dict.intersectingComponents',
                rightTitle: 'dict.availableComponents',
                source: 'right',
              });
            }
          }

          if (!scope.workspace) {
            scope.workspace = {};
          }
          if (!scope.mainConfig.table) {
            scope.mainConfig.table = {};
          }
          if (!scope.mainConfig.table.attributes) {
            scope.mainConfig.table.attributes = [];
          }
          scope.workspace.tableAttributes = gaJsUtils.deepCopy(scope.mainConfig.table.attributes);

          scope.config =
            res.data == NO_CONFIG ||
              angular.isUndefined(res.data.printConfig) ||
              res.data.printConfig == null ||
              res.data.printConfig == 'null'
              ? null
              : angular.fromJson(res.data.printConfig);

          initSelectedPrintScale();
          initselectedDataStore();
          initReportsDataStore();
          initSelectedReports();
          initFilters();
          initBoardDisplayInfos();
          initTemplateOfConfig();

          if (openConfig) {
            ngDialog.open({
              template: 'js/XG/widgets/mapapp/dict/views/dictWidgetConfig.html',
              className: 'ngdialog-theme-plain width800 nopadding dictConfigPopup miniclose',
              closeByDocument: false,
              scope: scope
            });
          }
        }

        function bisInitModuleConfiguration(openConfig, defer) {
          DICTFactory.getmoduleconfig().then((res) => {
            FeatureTypeFactory.get().then(() => {
              scope.featuretypes = FeatureTypeFactory.resources.featuretypes;
              scope.lightFeatureTypes = scope.featuretypes.map(
                obj => { return {uid: obj.uid, name: obj.name, alias: obj.alias};
                });
              initModuleConfigurationCallback(res, openConfig);
              defer.resolve();
            });
          });
        }

        function initModuleConfiguration(openConfig) {
          const defer = $q.defer();
          if (DataStoreFactory.resources.datastores.length > 0) {
            scope.datastores = DataStoreFactory.resources.datastores;
            bisInitModuleConfiguration(openConfig, defer);
          } else
            DataStoreFactory.get().then(function () {
              scope.datastores = DataStoreFactory.resources.datastores;
              bisInitModuleConfiguration(openConfig, defer);
            });
          return defer.promise;
        }


        const deleteDraftItemEl = (pieceJointe) => {
          // If we delete the last one, state of the document goes back to "A TRAITER"
          if (scope.currentDocumentDraft.draftDocsCount == 1) {
            scope.deleteDraft();
          } else {
            // -- Else, we just delete the draftItem quand on enlève un rapport ou un plan
            // -- la DICT n'est plus valide, on la bascule en à traiter
            scope.delDraftItems.push(pieceJointe.nom);
            handleCreateOrUpdateDraft(false, true);
          }

          var index = scope.currentDocumentDraft.cdraftData.piecesJointes.indexOf(
            pieceJointe
          );

          scope.currentDocumentDraft.cdraftData.piecesJointes.splice(index, 1);
          scope.currentDocumentDraft.draftData.piecesJointes.splice(index, 1);
        };

        /**
         * Visualisation of the draft item
         * @param {*} pieceJointe
         */
        function viewDraftItemEl(pieceJointe) {
          var pieceJointeOpened = false;

          // If the file is already loaded in the front side
          angular.forEach(scope.draftItems, function (draftItem) {
            if (pieceJointe.nom == draftItem.title) {
              openByteArrayInNewTab(draftItem.data, pieceJointe);
              pieceJointeOpened = true;
            }
          });

          // If we need to load it from the server
          if (!pieceJointeOpened) {
            DICTFactory.getdownloadurl(
              scope.selectedDocument.id,
              pieceJointe.nom
            ).then(function (res) {
              var resData = res.data;
              if (resData != 'Fichier inexistant') {
                window.open(res.data);
              } else {
                require('toastr').error(
                  $filter('translate')('dict.failRecupFileDisplay')
                );
                console.log('viewDraftItemEl could not retrieve the file: ' + pieceJointe.nom);
              }
            });
          }
        }

        function testConfigService() {
          if (scope.extraConfig.selectedDataStore)
            scope.mainConfig.dictDataStore =
              scope.extraConfig.selectedDataStore.name;
          if (scope.extraConfig.reportsDataStore)
            scope.mainConfig.reportsDataStore =
              scope.extraConfig.reportsDataStore.name;

          if (scope.extraConfig.selectedPrintScale)
            scope.mainConfig.printScale =
              scope.extraConfig.selectedPrintScale.value;

          DICTFactory.testservice(scope.mainConfig).then(function (res) {
            if (res.data.version == null)
              require('toastr').warning(
                'Authentification invalide .',
                'Erreur'
              );
            else
              require('toastr').success('Portail DICT accessible .', 'Succes');
          });
        }


        /**
         * Préparation de la configuration pour faire une sauvegarde correcte.
         *
         * @param {*} config Configuration de travail (pour UI)
         * @returns Configuration pour enregistrement
         */
        const getConfigToSave = (config) => {
          const config2save = angular.copy(config);
          delete (config2save.autoAtlas.arcgis108Desc);
          if (config2save.autoAtlas) {
            config2save.autoAtlas = angular.copy(config2save.autoAtlas);
            if (config2save.autoAtlas.a3Model && config2save.autoAtlas.a3Model.name) {
              config2save.autoAtlas.a3Model = angular.copy(config2save.autoAtlas.a3Model);
              config2save.autoAtlas.a3Model = config2save.autoAtlas.a3Model.name;
            }
            if (config2save.autoAtlas.a4Model && config2save.autoAtlas.a4Model.name) {
              config2save.autoAtlas.a4Model = angular.copy(config2save.autoAtlas.a4Model);
              config2save.autoAtlas.a4Model = config2save.autoAtlas.a4Model.name;
            }
          }
          return config2save;
        };


        function updateModelElements(modelInfos) {
          if (!scope.mainConfig) scope.mainConfig = {};

          if (modelInfos.dictDocumentsFti)
            scope.mainConfig.dictLayerUID = modelInfos.dictDocumentsFti.uid;
          if (modelInfos.dictManuelDocumentsFti)
            scope.mainConfig.dictManuelDocumentsUID =
              modelInfos.dictManuelDocumentsFti.uid;
          if (modelInfos.dictActorsFti)
            scope.mainConfig.dictActorUID = modelInfos.dictActorsFti.uid;
          if (modelInfos.dictDraftsFti)
            scope.mainConfig.dictDraftUID = modelInfos.dictDraftsFti.uid;
          if (modelInfos.dictDraftItemsFti)
            scope.mainConfig.dictDraftItemUID =
              modelInfos.dictDraftItemsFti.uid;
        }

        function fillModel() {
          gaDomUtils.showGlobalLoader();
          DICTFactory.fillmodel(scope.extraConfig.selectedDataStore.uid).then(
            function (res) {
              gaDomUtils.hideGlobalLoader();
              var result = res.data;
              if (result.error)
                require('toastr').error(result.error.message, 'Erreur');
              else {
                if (
                  !result.dictDocumentsFti &&
                  !result.dictActorsFti &&
                  !result.dictDraftsFti &&
                  !result.dictDraftItemsFti
                )
                  require('toastr').error(
                    'Veuillez générer tout d\'abord le modèle',
                    'Erreur'
                  );
                else {
                  if (
                    !result.dictDocumentsFti ||
                    !result.dictActorsFti ||
                    !result.dictDraftsFti ||
                    !result.dictDraftItemsFti
                  )
                    require('toastr').warning(
                      'Le remplissation automatique n\'a pas trouvé de correspondance '
                      + 'pour un ou plusieurs composants, veuillez les selectionner manuellement',
                      'Attention'
                    );
                  updateModelElements(result);
                }
              }
            }
          );
        }

        var ngDialogPromise;

        function handleOpenEsriPrintConfig() {
          if (scope.config == null) scope.config = PRINT_CONF_TEMPLATE;

          ngDialogPromise = ngDialog.openConfirm({
            template: 'js/XG/widgets/mapapp/esriprint/views/configuration.html',
            className:
              'ngdialog-theme-plain overflowY width800 nopadding miniclose',
            scope: scope,
          });

          ngDialogPromise.then(
            function (data) {
              scope.$broadcast('getserviceinfo_esriprint');
              console.log(data);
              require('toastr').info(data);
            },
            function (data) {
              require('toastr').info(data);
            }
          );
        }

        function buildModel(selStore, currentsridName) {
          scope.selectedStore = selStore;
          if (scope.selectedStore && currentsridName) {
            gaDomUtils.showGlobalLoader();
            DICTFactory.buildmodel(
              scope.selectedStore.uid,
              currentsridName
            ).then(function (res) {
              gaDomUtils.hideGlobalLoader();
              var result = res.data;
              if (result.error)
                require('toastr').error(result.error.message, 'Erreur');
              else {
                updateModelElements(result);
                require('toastr').success('Modèle généré .', 'Succes');
              }
            });
          }
        }

        /**
         * Changement de l'état de la DICT de CONCERNE à NON CONCERNE ou l'inverse.
         */
        const toggleConcernedState = () => {
          gaDomUtils.showGlobalLoader();
          DICTFactory.initconcerning(null, scope.selectedDocument.id,
            !scope.selectedDocument.isConcerned
          ).then(() => {
            scope.selectedDocument.isConcerned = !scope.selectedDocument.isConcerned;
            scope.currentSelectedTypeOfDocuments = scope.selectedDocument.isConcerned
              ? 'A_TRAITER'
              : 'NO_CONCERNED';
            loadDataStates().then(() => {
              scope.getDraftForDocument (scope.selectedDocument, null).then(() => {
                // -- Ticket 3329:  Le dossier doit s'ouvrir dans le widget dans le bon onglet
                // --     invitant l'utilisateur à régénérer le rapport manuellement.
                scope.dictTabs.activeTab = 1;
                // -- Vue en mode travail sur document dict
                scope.workspace.documentsView = 'TREAT_DOC';
                // -- Vue avec piéces jointes du document + commandes du bas actives
                scope.workspace.currentDraftView = 'default';
                // -- Onglet A TRAITER "visible"
                scope.currentSelectedTypeOfDocuments = 'A_TRAITER';
                $timeout(() => {
                  scope.currentTab = scope.boardTabs[0];
                });
              });
            });
          }).finally(() => {
            gaDomUtils.hideGlobalLoader();
          });
        };

        function handleUpdateDraftFileName(newPieceJointeName, pieceJointe) {
          if (pieceJointe.type === 'PDF' && !newPieceJointeName.endsWith('.pdf')) {
            newPieceJointeName += '.pdf';
          }
          for (let i = 0; i < scope.draftItems.length; i++) {
            if (pieceJointe.nom == scope.draftItems[i].title) {
              scope.draftItems[i].title = newPieceJointeName;
              break;
            }
          }
          for (let i = 0; i < scope.currentDocumentDraft.draftData.piecesJointes.length; i++) {
            if (pieceJointe.nom == scope.currentDocumentDraft.draftData.piecesJointes[i].nom) {
              scope.currentDocumentDraft.draftData.piecesJointes[i].nom = newPieceJointeName;
              break;
            }
          }
          for (let i = 0; i < scope.currentDocumentDraft.cdraftData.piecesJointes.length; i++) {
            if (pieceJointe.nom == scope.currentDocumentDraft.cdraftData.piecesJointes[i].nom) {
              scope.currentDocumentDraft.cdraftData.piecesJointes[i].nom = newPieceJointeName;
              break;
            }
          }
        }

        /**
         * @param changeConfig boolean indiquant si l'actualisation se fait après
         * une modification de la configuration des DICTs
         */
        function loadDataStates(changeConfig) {

          const defer = $q.defer();
          if (!scope.dictDocumentFti) {
            // -- DICT non configuré, il ne sert à rien d'essayer de travailler sur les DICTs.
            defer.resolve();
            return defer.promise;
          }
          // Loader display
          scope.displayLoader = true;

          DICTFactory.computeStatesAndReports(getMapCRS(), changeConfig)
            .then(intersectingFeatures => {
            // Lancement de l'impression des atlas automatique pour les features
              if (scope.extraConfig.selectedDataStore.type == 'ArcGis') {
                DICTFactory.getalldocuments(null, true, getMapCRS()).then(res => {
                  handleGetDocumentsData(res.data).then(() => {
                    scope.displayLoader = true;
                    addAllAtlasAuto(intersectingFeatures.data).finally(() => {
                      DICTFactory.getalldocuments(null, true, getMapCRS()).then(res => {
                        handleGetDocumentsData(res.data).then(() => { defer.resolve();})
                          .finally(() => scope.displayLoader = false);
                      });
                    });
                  });
                });
              } else {
                DICTFactory.getalldocuments(null, true, getMapCRS()).then(res => {
                  handleGetDocumentsData(res.data).then(() => { defer.resolve();});
                });
              }
            }, e => {
            // Si la mise à jour de l'état concerné/non concerné est en erreur
              console.error(e.data);
              if (e.data && e.data.message) {
                require('toastr').warning(e.data.message);
                if (e.data.message.includes('rapport')) {
                  scope.reportStatus = 'failed';
                }
              }
              else {
                require('toastr').warning($filter('translate')('dict.errorOnAutoConcerned'));
              }
            }).finally(() => {
            // always reset depsite the reset param
            // TODO yyyyyy bizarre cet appel on appel cette fonction
            // dans then puis dans le finally !!!!!!!!!!!
              DICTFactory.getalldocuments(null, true, getMapCRS()).then(function (
                res
              ) {
                handleGetDocumentsData(res.data).then(() => { defer.resolve();});
              });
            });
          return defer.promise;
        }

        /*
         *    Le lancement du calcul de l'état, la génération du rapport et du plan
         * ne doit pas se faire à l’ouverture de la catégorie dans laquelle
         * se trouve le widget DICT mais à l’ouverture du widget lui-même.
           --> scope.$on('openTools_dictwidgetdirective', () => {
            Enlevé, car, à chaque ouverture de la catéorie, on instancie le widget
            et l'instance existante n'étant pas réutilisée,
            l'événement d'ouverture est reçue par chaque instance créée et
            le traitement fait pour chacune d'elle.
        */
        initModuleConfiguration(false).then(() => {
          scope.currentSelectedTypeOfDocuments = 'A_TRAITER';
          loadDataStates();
        });

        scope.dictTabs = [
          {
            title: 'Résumé',
          },
          {
            title: 'Documents',
          },
        ];

        scope.dictTabs.activeTab = 0;

        scope.documentsViewIndex = 0;

        scope.availableReports = [];

        const appname = $rootScope.xgos.sector;

        // récupère les rapports de la catégorie "Tableaux de bord"
        ReportFactory.getjasperfiles(appname,false).then(function (res) {
          for (var i = 0; i < res.data.length; i++) {
            var reportName = res.data[i];
            scope.availableReports.push({
              text: reportName,
              click: function ($event) {
                launchReportByName($event.$parent.item.text);
              },
            });
          }
        });

        scope.getDocumentData = function () {
          //console.log('test');
        };

        scope.filterDocuments = documentsState => {
          filterDocumentsByType(documentsState);
        };

        scope.goToDocumentsViewAndFilterDocuments = function (documentsState) {
          scope.dictTabs.activeTab = 1;
          scope.workspace.documentsView = 'DOCS_VIEW';
          filterDocumentsByType(documentsState);
        };

        scope.goToManuelApp = () => {
          let dictManuelUrl = '/kistask/dict_manuelles.html#/dictmanuel/';
          dictManuelUrl += '?portalId=' + $rootScope.xgos.portal.uid;
          dictManuelUrl += '&userName=' + $rootScope.xgos.user.name;
          dictManuelUrl += '&appName='
            + ($location.search().app ? $location.search().app : localStorage.getItem('app'));
          // -- KIS-3462 : Transmettre la source de données à KisTask
          // --    ATTENTION il doit s'agir d'une source de données PostGIS
          dictManuelUrl += '&dataStore=' + scope.mainConfig.dictDataStore;
          window.open(dictManuelUrl);
        };


        function loadToSimplifiedDocumentPart(
          simplifiedDocument,
          propertyPattern,
          propertyValue
        ) {
          for (var inProperty in propertyValue) {
            var inPropertyValue = propertyValue[inProperty];
            if (typeof inPropertyValue === 'object')
              loadToSimplifiedDocumentPart(
                simplifiedDocument,
                propertyPattern + inProperty + '_',
                inPropertyValue
              );
            else
              simplifiedDocument[
                propertyPattern + inProperty
              ] = inPropertyValue;
          }
        }

        const getSimplifiedSelectedDocument = () => {
          const simplifiedDocument = {};
          if (scope.selectedDocument) {
            for (var property in scope.selectedDocument) {
              var propertyValue = scope.selectedDocument[property];
              if (typeof propertyValue === 'object')
                loadToSimplifiedDocumentPart(
                  simplifiedDocument,
                  property + '_',
                  propertyValue
                );
              else simplifiedDocument[property] = propertyValue;
            }
          }
          return simplifiedDocument;
        };


        function launchReportByName(reportName) {
          if (reportName.indexOf('.') !== -1)
            reportName = reportName.split('.')[0];

          if (scope.mainConfig.dictDataStore) {
            ReportFactory.consultgeneratedreport(getSimplifiedSelectedDocument(), false,
              reportName, scope.mainConfig.dictDataStore, 'pdf').then((res) => {
              if (res.data && res.data.ok) {
                const url = documentUrl(res.data.strValeur);
                DICTFactory.saveAttachedDocumentByUrl(scope.selectedDocument.id, url, '').
                  then(() => {
                    draftAdded(reportName,url);
                  });
              }
              else {
                console.log(res);
              }
            },
            (reason) => {
              console.log(reason);
              require('toastr').error(
                'Une erreur est survenue lors de la requete de génération du rapport: ' +
                  reason.statusText
              );
            }
            );
          }
        }

        function selDocument(reproject = false) {
          // Get a geometry
          let olGeometry = geoJsoNFormat.readGeometry(
            scope.selectedDocument.geometry
          );

          // KIS-3393: lorsque le composant dictdocument est dans une projection différente
          // de la projection de la carte
          const mapProj = scope.map.getView().getProjection().getCode();
          const compProj = scope.dictDocumentFti.srid;
          if (reproject && mapProj !== compProj) {
            olGeometry.transform(compProj, mapProj);
          }

          var highlightFeature = new ol.Feature({
            name: '',
          });
          highlightFeature.setGeometry(olGeometry);
          dictSource.addFeature(highlightFeature);
          var mapView = map.getView();
          var featureExtent = dictSource.getExtent();
          mapView.fit(featureExtent, map.getSize());
        }

        /**
         * Surbrillance de l'objet dict selectionné
         * @param document
         */
        scope.highlightDocumentOrReceive = function (document) {
          // Dans le cas ou l'appel est fait depuis la datatable
          if (document.type === 'Feature') {
            document = findFrontDocumentObjectFromFeature(document);
          }
          scope.selectedDocument = document;
          dictSource.clear();
          if ('A_TRAITER' == document.state && document.geometry) selDocument();
          else
            showConfirm(
              'Il est impossible de sélectionner ce document, '
              + 'voullez vous tout d\'abord le réceptionner ?',
              function () {
                receiveDocument(document);
              }
            );
        };

        /**
         * @param feature objet feature (généré par la datatable)
         * @returns object front document utilisé par le widget
         */
        const findFrontDocumentObjectFromFeature = feature => {
          return scope.baseDocuments.find(
            document => document.id === parseInt(feature.properties.id));
        };

        scope.receiveDocument = function (document, $event) {
          console.log($event.target);
          $event.stopPropagation();
          receiveDocument(document);
        };

        // --TO REMOVE--
        // scope.receiveAllDocuments = function () {
        //  receiveAllDocuments();
        // };

        scope.getPDFForDocument = function (document, $event) {
          $event.stopPropagation();
          scope.selectedDocument = document;
          DICTFactory.getdocumentpdf(document.id);
        };

        scope.toggleConcerned = (document) => {
          // Dans le cas ou l'appel est fait depuis la datatable
          if (document.type === 'Feature') {
            document = findFrontDocumentObjectFromFeature(document);
          }
          scope.selectedDocument = document;
          toggleConcernedState();
        };

        scope.deleteDraftForDocument = function (document) {
          scope.selectedDocument = document;
          gaDomUtils.showGlobalLoader();
          DICTFactory.deletedraftfordocument(
            null,
            scope.selectedDocument.id
          ).then(function (res) {
            gaDomUtils.hideGlobalLoader();
            //require('toastr').error(res.data);
            handleDeleteDraftForDocument(scope.selectedDocument, res.data);
            scope.dictTabs.activeTab = 0;
            scope.reloadData();
          });
        };

        /**
         * Changer l'état d'un document de 'A_VALIDER' à 'A_ENVOYER' ou
         * de 'A_TRAITER' à 'A_VALIDER'.
         *
         * @param {Object} document Le document à mettre à jour
         * @param {Boolean} validate true pour mettre l'état à A_ENVOYER,
         *    false pour mettre l'état à A_VALIDER
         */
        scope.toggleValidateDocument = function (document, validate, isNotBuiltDocument,
          closePreview) {
          if (isNotBuiltDocument) {
            document = scope.baseDocuments.find(
              baseDocument => baseDocument.data.fId === document.id
            );
          }
          if (document) {
            scope.selectedDocument = document;
          }
          scope.getDraftForDocument (scope.selectedDocument, null).then(() => {
            // -- Vérifier si le document est en attente d'envoi et
            // -- si ce n'est pas le cas, si il y a un plan ou un rapport
            if (!scope.selectedDocument.isWaitingForSending && !okToSend()) {
              require('toastr').error('Il manque soit un rapport soit un plan !');
              return;
            }

            gaDomUtils.showGlobalLoader();
            scope.previewIsWorking = true;
            // -- Appeler le backend pour changer l'état du document
            DICTFactory.updateinnerstate(scope.selectedDocument.id,
              validate ? 'A_ENVOYER' : 'A_VALIDER'
            ).then(() => {
              gaDomUtils.hideGlobalLoader();
              // -- Mettre à jour les états des documents dans le front
              scope.selectedDocument.isWaitingForSending = validate;
              scope.selectedDocument.waitingForValidation = !validate;

              updateDocState(scope.selectedDocument);
              // -- Pas de document sélectionné à présenter
              delete scope.selectedDocument;
              // -- Etre sûr que la présentation est la liste des DICTs
              // -- (et non la fiche d'une DICT particulière)
              scope.workspace.documentsView = 'DOCS_VIEW';
              // -- Recharger les DICTs
              loadDataStates().then(() => {
                if (closePreview) {
                  scope.clearToValidAttachementPreview();
                }
              }).finally(() => {
                scope.previewIsWorking = false;
              });
            }, () => {
              scope.previewIsWorking = false;
              gaDomUtils.hideGlobalLoader();
            });
          });
        };


        scope.getDraftForDocument = function (document, $event) {
          if ($event) {
            $event.stopPropagation();
          }
          // Dans le cas ou l'appel est fait depuis la datatable
          if (document.type === 'Feature') {
            document = findFrontDocumentObjectFromFeature(document);
          }
          scope.selectedDocument = document;
          gaDomUtils.showGlobalLoader();
          const promise = DICTFactory.getdraftfordocument(null, document.id);
          promise.then(res => {
            gaDomUtils.hideGlobalLoader();
            handleGetDraftForDocument(document, res.data, true);
          });
          return promise;
        };

        scope.addPrintItem = function () {
          scope.workspace.currentDraftView = 'draftPrintView';
          scope.printres = {};

          $timeout(function () {
            if (scope.extraConfig.selectedDataStore.type !== 'ArcGis') {
              $rootScope.$broadcast('openTools_gaprintdirv2', {
                directive: 'gaprintdirv2',
              });
            } else {
              $rootScope.$broadcast('openTools_esriprint', undefined, true);
            }
          }, 1000);
        };

        scope.addReportItem = function () {
          console.log('report launched');
        };

        scope.deleteDraft = (isNotBuiltDocument, closePreview) => {
          gaDomUtils.showGlobalLoader();
          scope.previewIsWorking = true;
          if (isNotBuiltDocument) {
            scope.selectedDocument = scope.baseDocuments.find(
              document => document.data.fId === scope.selectedDocument.id
            );
          }
          DICTFactory.deletedraftfordocument(
            null,
            scope.selectedDocument.id
          ).then((res) => {
            gaDomUtils.hideGlobalLoader();
            handleDeleteDraftForDocument(scope.selectedDocument, res.data);
            scope.dictTabs.activeTab = 0;
            scope.reloadData().then(() => {
              if (closePreview) {
                scope.clearToValidAttachementPreview();
              }
            }).finally(() => {
              scope.previewIsWorking = false;
            });
          },
          () => {
            scope.previewIsWorking = false;
            gaDomUtils.hideGlobalLoader();
          });
        };

        scope.getDraftPdfData = function () {
          //console.log(url);
        };

        scope.configDictWidget = function () {
          initModuleConfiguration(true);
        };

        scope.loadUrlToBytes = (params, automatic) =>{
          // Wait for the data to be filled before executing
          $timeout(() => {
            downloadPrintUrl(params, automatic);
          });
        };

        const loadUrlToBytesAuto = (params) => {
          scope.loadUrlToBytes(params, true);
        };


        scope.viewDraftItem = function (pieceJointe) {
          viewDraftItemEl(pieceJointe);
        };

        scope.deleteDraftItem = function (pieceJointe) {
          deleteDraftItemEl(pieceJointe);
        };

        scope.testDICTConfigService = function () {
          testConfigService();
        };


        /**
         * Récupére quand elles sont définies les valeurs de configuration
         * depuis l'extra config pour alimenter la configuration normale.
         *
         * @param {*} source Nom de la propiété à récupérer depuis extraConfig
         * @param {*} sourceProperty Nom de la sous proriété à récupérer depuis extraConfig
         * @param {*} destination Nom de la propriété de mainConfig à alimenter
         */
        const fromExtraConfig = (source,sourceProperty, destination) => {
          if (scope.extraConfig[source])
            scope.mainConfig[destination] = scope.extraConfig[source][sourceProperty];
        };


        scope.saveDICTConfigService = (close,onlySaveConfigFile) => {
          if (scope.workspace.tableAttributes && scope.mainConfig.table) {
            scope.mainConfig.table.attributes = gaJsUtils.deepCopy(scope.workspace.tableAttributes);
          }

          fromExtraConfig('selectedDataStore','name','dictDataStore');
          fromExtraConfig('reportsDataStore','name','reportsDataStore');
          fromExtraConfig('selectedPrintScale','value','printScale');

          scope.mainConfig.printConfig = angular.isDefined(scope.config)
            ? angular.toJson(scope.config)
            : null;

          DICTFactory.saveconfig(getConfigToSave(scope.mainConfig),onlySaveConfigFile)
            .then((res) => {
              if (res.data != 'success') {
                require('toastr').warning('Erreur configuration .', 'Erreur');
              }
              else {
                require('toastr').success('Portail DICT configuré .', 'Succes');
                if (!onlySaveConfigFile) {
                  scope.currentSelectedTypeOfDocuments = 'A_TRAITER';
                  loadDataStates(true);
                }
              }
            }).finally(() => {
              if (close) {
                tableConfDialog.close();
              }
            });
        };


        scope.fillDICTModel = function () {
          fillModel();
        };

        scope.openEsriPrintConfig = function () {
          handleOpenEsriPrintConfig();
        };

        scope.buildDICTModel = function (selStore, currentsridName) {
          buildModel(selStore, currentsridName);
        };

        scope.reloadData = function () {
          scope.currentSelectedTypeOfDocuments = 'A_TRAITER';
          return loadDataStates();
        };

        scope.cleanPrintView = function () {
          $rootScope.$broadcast('closeTools_gaprintv2', {
            directive: 'gaprintdirv2',
          });
          scope.$broadcast('closeTools_esriprint', {
            directive: 'gaprintdirv2',
          });
          scope.$broadcast('atlas_clearDrawing');
        };

        scope.updateDraftFileName = function (newPieceJointeName, pieceJointe) {
          // timeout is used so angular consider the modification made
          $timeout(()=> {
            handleUpdateDraftFileName(newPieceJointeName, pieceJointe);
          });
        };

        scope.updateViewsProperties = function () {
          scope.workspace.documentsView = 'DOCS_VIEW';
          scope.currentSelectedTypeOfDocuments = 'A_TRAITER';
          // KIS-3093 | Mise à jour des données non nécéssaire au changement d'onglet
          // loadDataStates();
        };


        /**
         * Mettre dans scope.dictBoardFieldsName la liste des noms d'attribut
         * à afficher dans al présentation tabulaire des DICTs.
         */
        const prepareAttributeList = () => {
          const tableAttributes = scope.mainConfig.table.attributes;
          scope.workspace.indeterminate
            = tableAttributes.some(att => !att.show) && tableAttributes.some(att => att.show);
          scope.dictBoardFieldsName.splice(0);
          // -- Parcours des attributs du FTI (dans l'order d'affichage)
          for (const att of scope.dictDocumentFti.attributes) {
            const tableAtt = tableAttributes.find(tableAtt => tableAtt.name === att.name);
            if (tableAtt.show) {
              // -- Ajouter l'attribut pour la datatable
              // -- si celui-ci est configuré comme étant à afficher.
              scope.dictBoardFieldsName.push(att.name);
            }
          }
        };


        /**
         * Ouverture du tableau d'affichage détaillé des DT/DICT
         */
        let boardPopup;
        scope.openDictBoard = () => {
          if (boardPopup) {
            boardPopup.close();
          }
          prepareAttributeList();
          boardPopup = extendedNgDialog.open({
            template: 'js/XG/widgets/mapapp/dict/views/dictBoardPopup.html',
            className: 'ngdialog-theme-plain width925 nopadding miniclose',
            closeByDocument: false,
            scope: scope,
            title: $filter('translate')('dict.boardTitle'),
            draggable: true,
            resizable: true,
            minimizeMaximize: true,
            minWidth: '400px',
            minHeight: '350px',
            broadcastHeightChanged: 'updateGcDataTableHeight'
          });
        };

        /**
         * Au clic sur le bouton du tableau de bord des documents DICT à valider,
         * partage la fenêtre en 2 parties et affiche à gauche un visualisateur de document PDF
         * @param {object} doc document DICT à valider (document de la ligne du tableau
         * contenant le bouton cliqué)
         */
        scope.openDocumentPreview = (doc) => {

          const PREVIEWID = 'dict-validation-preview';
          const isPreViewerExists = document.getElementById(PREVIEWID) !== null;

          if (isPreViewerExists) {
            gaDomUtils.showLocalLoader(`#${PREVIEWID}`);
          } else {
            gaDomUtils.showGlobalLoader();
          }

          // Définit la ligne cliquée comme dossier sélectionné
          scope.selectedDocument = doc;

          // Centre la carte sur l’emprise du chantier DICT
          dictSource.clear();
          selDocument(true);

          // Récupère les documents associés au dossier
          DICTFactory.getdraftfordocument(null, doc.properties.id).then(
            res => {
              if (Array.isArray(res.data.piecesJointes) && res.data.piecesJointes.length > 0) {

                // Tableau des documents associés au dossier
                const piecesJointes = res.data.piecesJointes;

                // Tableau des valeurs du select
                scope.previewAttachmentNames = piecesJointes.map(pj => pj.nom);

                // Plan/rapport affiché à l'initialisation
                let firstDraftLoaded = piecesJointes[0];
                const plan = piecesJointes.find(pj => pj.nom.startsWith('plan_'));
                if (plan) {
                  firstDraftLoaded = plan;
                }

                // Création de l'objet contenant le nom du fichier à afficher
                // à l'initialisation de la prévisualisation
                scope.previewAttachment = {};
                scope.previewAttachment.name = firstDraftLoaded.nom;

                if (isPreViewerExists) {
                  gaDomUtils.removeLocalLoader(`#${PREVIEWID}`);
                } else {
                  gaDomUtils.hideGlobalLoader();
                }

                // Exécute la séparation de la fenêtre en 2 parties et
                // charge le fichier dans l'attribut ng-src de l'embed
                scope.onValidDocPreviewChange(firstDraftLoaded,true);
              } else {
                if (isPreViewerExists) {
                  gaDomUtils.removeLocalLoader(`#${PREVIEWID}`);
                } else {
                  gaDomUtils.hideGlobalLoader();
                }
              }
            },
            err => {
              if (isPreViewerExists) {
                gaDomUtils.removeLocalLoader(`#${PREVIEWID}`);
              } else {
                gaDomUtils.hideGlobalLoader();
              }
              console.error('openDocumentPreview', err.data);
            }
          );
        };

        /**
         * A l'initialisation de la vue en 2 colonnes
         * quand on clique sur le bouton "prévisualisation" d'une ligne de l'onglet "A Valider"
         * de la popup "Demandes à traiter/réceptionner.
         * Masque la barre verticale des catégories et le panel du widget
         */
        const hideMapLeftMenu = () => {
          const mapAside = document.getElementById('map_aside');
          mapAside.style.display = 'none';
        };

        /**
         * Au clic sur le bouton "Quitter" de la prévisualisation des documents associés
         * au dossier dans la partie gauche de la fenêtre
         */
        scope.clearToValidAttachementPreview = () => {

          //Supprime la div du viewer (partie gauche de la fenêtre)
          const previewDiv = document.querySelector('.dict-validation-preview-container');
          previewDiv.parentNode.removeChild(previewDiv);

          // Restaure l'affichage du body
          const divCible = document.getElementById('xgos_main');
          const parentDiv = divCible.parentNode;
          parentDiv.style.display = 'block';

          // Restaure l'affichage du mapLeftMenu
          document.getElementById('map_aside').style.display = 'block';

          // Passe le boolean à false pour initialiser la vue à 2 colonnes au prochain
          // clic sur un bouton "prévisualisation"
          scope.isValidDraftPreviewOpened = false;
        };


        /**
         * A l'initialisation de la vue à 2 colonnes de la fenêtre et de l'apparition
         * de la prévisualisation PDF
         * Déplacement de la popup réduite "Demandes à traiter/à réceptionner"
         * au milieu-haut de la fenêtre
         */
        const moveToValidateDocumentsPopup = () => {
          // Réduction de la popup
          boardPopup.features.makeItTab();

          $timeout(() => {
            // Sélection de la popup draggable
            const popupToMove = boardPopup.dialog[0].nextSibling.firstElementChild;

            // Déplacement de la popup
            popupToMove.style.left = '0';
            popupToMove.style.top = 0 - (window.innerHeight / 2) + 'px';

            // Définition de la taille de la popup en mode "normal"
            // (2nd clic sur le bouton minimize)
            const popupPropsToSet = {
              top: popupToMove.style.top,
              left: popupToMove.style.left,
              width: (window.innerWidth /2) + 'px',
              height: window.innerHeight + 'px'
            };
            boardPopup.features.setNormalSize(popupPropsToSet);
          });

        };

        /**
         * A l'initialisation de la vue à 2 colonnes de la fenêtre et de l'apparition
         * de la prévisualisation PDF,
         * puis au changement de valeur dans le select des documents du dossier.<br>
         * Récupère le chemin d'accès au fichier pdf.
         * Seulement à l'initialisation: création de la vue à 2 colonnes et
         * déplacement de la popup "Demandes à traiter/réceptionner"
         *
         * @param {object} pj premier plan/rapport à afficher à l'initialisation
         * de la prévisualisation @see {scope.openDocumentPreview}.
         * Paramètre null au changement de valeur du select
         * @param {boolean} initializing true au clic sur le bouton "prévisualisation" de la popup.
         * Puis false au changement de valeur du select de la prévisualisation
         */
        scope.onValidDocPreviewChange = (pj, initializing = false) => {
          const PREVIEWID = 'dict-validation-preview';
          const isPreViewerExists
            = initializing ? document.getElementById(PREVIEWID) !== null : false;
          if (isPreViewerExists) {
            gaDomUtils.showLocalLoader(`#${PREVIEWID}`);
          }

          // Au changement de valeur du select: pj === null
          // pj existe uniquement à l'initialisation
          if (!pj && scope.previewAttachment.name) {
            pj = {
              nom: scope.previewAttachment.name,
              type: 'pdf'
            };
          }


          DICTFactory.getdownloadurl(scope.selectedDocument.properties.id, pj.nom).then(
            res => {
              // Associe le chemin à l'attribut ng-src de l'objet embed
              scope.previewAttachment.src = res.data;

              // Création de la vue à 2 colonnes et déplacement
              //de la popup "Demande à traiter/réceptionner"
              if (initializing && !scope.isValidDraftPreviewOpened) {
                hideMapLeftMenu();
                createViewerSide();
                moveToValidateDocumentsPopup();
              }

              if (isPreViewerExists) {
                gaDomUtils.removeLocalLoader(`#${PREVIEWID}`);
              }
            },
            () => {
              if (isPreViewerExists) {
                gaDomUtils.removeLocalLoader(`#${PREVIEWID}`);
              }
            }
          );
        };

        /**
         * Au clic sur un bouton "prévisualisation" d'une ligne de l'onglet
         * "A Valider" de la popup "Demandes à traiter/réceptionner"
         * Création de la vue à 2 colonnes avec le viewer PDF en partie gauche
         */
        const createViewerSide = () => {
          //Bascule l'affichage du body en gauche/droite
          const divCible = document.getElementById('xgos_main');
          const parentDiv = divCible.parentNode;
          parentDiv.style.display = 'flex';

          //Applique une hauteur par défaut au visualiseur
          scope.previewHeight =((70 * window.innerHeight) / 100) + 'px';

          // Compile le html
          const str = '<div ng-include="'
              + '\'js/XG/widgets/mapapp/dict/views/dictValidationPreview.html\'' + '"></div>';
          const cmp = $compile(str)(scope);

          // Insertion de la nouvelle div avant la div cible
          $(cmp).insertBefore($('#xgos_main'));

          // Permet aux autres clics sur un bouton "prévisualisation"
          // de ne pas créer une autre division de l'écran quand elle existe déjà
          // mais de mettre à jour la partie gauche
          scope.isValidDraftPreviewOpened = true;

          // Applique une hauteur maximale au visualiseur
          // Après apparition de la popup
          adjustViewerToMaxHeight(5);
        };

        /**
         * Ajuste la hauteur de l'objet embed du pdf à la hauteur maximale de la page
         * à laquelle st soutrait la hauteur des autres élements contenus
         * dans un conteneur de classe "footer"
         * @param tries nombre d'essais pour parcourir le dom à la recherche
         * du conteneur de classe "footer"
         */
        const adjustViewerToMaxHeight = (tries) => {
          const footer = document.querySelector('.dict-validation-preview-container .footer');
          if (footer) {
            scope.previewHeight = (window.innerHeight - footer.clientHeight) + 'px';
          } else if (tries >= 0) {
            $timeout(() => {
              adjustViewerToMaxHeight(--tries);
            });
          }
        };


        // Onglets de l'affichage tableau
        scope.boardTabs = [
          {
            id: 0,
            name: 'A_TRAITER',
            whereCondition: 'innerstate=\'A_TRAITER\''
          },
          {
            id: 1,
            name: 'A_VALIDER',
            whereCondition: 'innerstate=\'A_VALIDER\''
          },
          {
            id: 2,
            name: 'INCOMPLET',
            whereCondition: 'innerstate=\'INCOMPLET\''
          },
          {
            id: 3,
            name: 'A_ENVOYER',
            whereCondition: 'innerstate=\'A_ENVOYER\''
          },
          {
            id: 4,
            name: 'A_RENVOYER',
            whereCondition: 'innerstate=\'A_RENVOYER\''
          }
        ];



        // Board actions
        scope.boardActions = [{
          name: 'highlightDocumentOrReceive',
          icone: 'glyphicon glyphicon-eye-open',
          callFunction: scope.highlightDocumentOrReceive,
          title: $filter('translate')('dict.selectLabel')
        },
        {
          name: 'getDraftForDocument',
          icone: 'fa fa-file-text',
          callFunction: scope.getDraftForDocument,
          title: $filter('translate')('dict.treatLabel')
        },
        {
          name: 'toggleConcerned',
          icone: 'fa fa-link',
          callFunction: scope.toggleConcerned,
          title: $filter('translate')('dict.switchConcernedState')
        },
        {
          name: 'documentPreview',
          icone: 'fa fa-file-picture-o',
          callFunction: scope.openDocumentPreview,
          title: $filter('translate')('dict.documentPreview'),
          hideFn: () => scope.currentTab.id !== 1
        }
        ];

        // Liste des champs à afficher dans la vue tableau
        scope.dictBoardFieldsName = [
          'id', 'type', 'emetteur', 'numconsultation', 'commune',
          'nrdictrecepisseplanstaille', 'datereception', 'datecreation', 'datedebut'
        ];

        /**
         * La sélection d'un onglet dans le board modifie le filtre sur les DT/DICT
         * @param tab nom de l'onglet = état de la DT/DICT selectionné (A_TRAITER)
         */
        scope.selectTab = tab => {
          scope.currentTab = tab;
          scope.whereCondition = tab.whereCondition;
        };


        /**
         * La case à cocher permettant de cocher/décocher tous les attributs a été activée.
         * En conséquence on change la propriété "affichable" de tous les attributs.
         */
        scope.updateAllAttribute = () => {
          const tableAttributes = scope.workspace.tableAttributes;
          for (const att of tableAttributes) {
            att.show = scope.workspace.tableConfigAllAttributes;
          }
        };


        /**
         * Un attribut a été rendu affichable ou non affichable.
         * On regarde si tous les attributs sont à afficher dans la gcdatatable ou non.
         * Ceci permet d'actualiser l'apparence de la case à cocher
         * qui permet de rendre tout affichable ou non.
         * Quand il y a des attributs affichables et
         * des attributs non affichable la case à cocher prend l'état indéterminé.
         */
        scope.oneAttributeUpdated = () => {
          const tableAttributes = scope.workspace.tableAttributes;
          // -- On coche la case tous s'il n'existe pas d'attribut dont l'attribut "show" est FAUX
          scope.workspace.tableConfigAllAttributes = !tableAttributes.some(att => !att.show);
          scope.workspace.indeterminate
            = tableAttributes.some(att => !att.show) && tableAttributes.some(att => att.show);
        };


        /**
         * Actualisa la liste des attributs pour la configuration
         * des attributs à afficher dans la présentation tabulaire des DICTs.
         */
        const checkDictTableAttributes =() => {
          if (scope.dictDocumentFti) {
          // -- Ajouter les attributs manquants
            const tableAttributes = scope.mainConfig.table.attributes;
            for (const att of scope.dictDocumentFti.attributes) {
              if (!tableAttributes.find( configAtt => configAtt.name===att.name)) {
                tableAttributes.push({name: att.name, show: true});
              }
            }
            scope.oneAttributeUpdated();
            // -- Enlever les attributs en trop
            for (let ind=tableAttributes.length-1;ind>=0; ind-- ) {
              if (!scope.dictDocumentFti.attributes.find(
                ftiAtt => ftiAtt.name===tableAttributes[ind].name)) {
                tableAttributes.splice(ind,1);
              }
            }
          }
        };

        const initBoardDisplayInfos = () => {
          scope.currentTab = scope.boardTabs[0];
          scope.dictDocumentFti = getFTIByUID(scope.mainConfig.dictLayerUID);
          scope.selectTab(scope.currentTab);
          checkDictTableAttributes();
        };


        /**
         * Construction de la liste des paramétres pour le service de construction d'un atlas.
         *
         * @param {} printModel Model d'impression à utiliser pour les cartes de l'atlas.
         * @param {*} bufferedGeometry Géométrie englobante de la zone à mettre dans l'atlas
         * @param {*} otherParams Autres paramétres spécifiques que sont needsPromise, et
         *        doNotCallAtlasFunction.
         * @param {*} docId Identifiant de la DICT afin de mettre un filtre qui permet
         *        de ne dessiner que le contour de cette DICT sur le plan
         * @returns
         */
        const parametersOfEsriAtlas = (printModel, bufferedGeometry, otherParams, docId, doc) => {
          // Configuration atlas actuel DEA 11/01/2024
          // Configuration de l'atlas esri avant impression automatique

          // -- Ticket 3569:
          // --   Le titre est la concaténation du champ “type”, d’un espace et
          // --   du champ “numconsultationchantier“
          // --   Exemple :   DT 20201010900287TVB -->
          doc = doc ? doc : scope.selectedDocument;
          const atlasEsri = {
            //title: 'Atlas DT/DICT',
            title: doc.data.values.type + ' ' + doc.data.values.numconsultationchantier,
            description: doc.data.values.description ? doc.data.values.description : '',
            selectedTemplate: {name: printModel},
            type: 'area',
            areaGeometry: bufferedGeometry,
            area_export_type: 'pdf',
            queryDefinitions:
              `[{ "layerName":"${scope.dictDocumentFti.name}", "queryDefinition": "id = ${docId}"}]`
          };

          // -- Définir l'échelle d'impression
          let gridScale;
          if (scope.atlasEsri && scope.atlasEsri.geoprocessFields) {
            atlasEsri.geoprocessFields = scope.atlasEsri.geoprocessFields;
            gridScale = atlasEsri.geoprocessFields.find( field => field.name==='gridScale');
          }
          if (gridScale) {
            gridScale.value = scope.extraConfig.selectedPrintScale.value;
          }
          else {
            atlasEsri.gridScale = scope.extraConfig.selectedPrintScale.value;
          }

          const esriConfig = {
            urlArea: scope.mainConfig.autoAtlas.atlasUrl
          };
          const callAtlasFunction = !otherParams || !otherParams.doNotCallAtlasFunction;

          return { atlasEsri, esriConfig,
            atlasMode: scope.atlasMode,
            scope,
            atlasResponse: parsedJsonAtlasResp,
            // La fonction scope.loadUrlToBytes est la fonction permettant
            // de joindre l'atlas à la DICT
            atlasFunction: loadUrlToBytesAuto,
            // -- Demander la liste des atlas actialisées aprés que les atlas aient été joints.
            needsToWaitAtlasFunction: true,
            callAtlasFunction,
            otherParams,
            atlasFunctionParams: {
              docId,
              atlasFileName: doc.data.values.type + '_' + doc.data.values.numconsultationchantier,
              updateDocPromises:
                otherParams.updateDocPromises ? otherParams.updateDocPromises : undefined
            }
          };
        };


        /**
         * Nettoyage des états des construction d'atlas précédents.
         */
        const clearAtlasStatus = () => {
          delete scope.jobsStatus;
          delete scope.arcgisJobs;
          delete scope.jobInfoStatus;
          delete scope.executingJob;
        };


        /**
         * Construire la chaîne de caratères qui contient la liste des numéros
         * de DICT dont on est en train de construie le plan (l'atlas).
         * La chaîne de caractères est construite en "joignant"
         * le tableau qui contient la liste des identifiants de ces DICTs.
         * Le but est de l'afficher à côté du spinner pour informer
         * l'utilisateur du traitement en cours.
         * Si la chaîne st trop longue on la tronque avec des "...".
         */
        const makingAtlasForAsString = () => {
          if (scope.makingAtlasFor && scope.makingAtlasFor.length) {
            let aString = scope.makingAtlasFor.join(', ');
            scope.makingAtlasForAsStr
              = aString.length >30 ? aString.substring(0,30) + ' ...' : aString;
          }
          else {
            scope.makingAtlasForAsStr = '';
          }
        };

        /**
         * Ajout automatique de tous les atlas aux demandes à traiter
         * @param intersectingFeatures
         */
        const addAllAtlasAuto = intersectingFeatures => {
          clearAtlasStatus();
          scope.noJobMessage = true;
          const addAllAtlasAutoDefer = $q.defer();
          // Promesses des création des atlas
          const geometryPromises = [];
          // Promesses des création des atlas
          const createAtlasPromises = [];
          // Promesses des sauvegarde des atlas
          const saveAtlasPromises = [];
          // Promesses des sauvegarde des atlas
          const updateDocPromises = [];
          if (!scope.makingAtlasFor) {
            scope.makingAtlasFor = [];
          }

          // Pour chaque chantier, on imprime si besoin un atlas dont on récupère l'URL
          for (const [id, value] of Object.entries(intersectingFeatures)) {
            const intersectingCollection = JSON.parse(value);
            // Si un chantier est intersecté par au moins une feature, on imprime un atlas
            if (intersectingCollection.totalFeatures > 0) {


              // Récupération de la taille du modèle
              // -- Ne pas mettre une égalité stricte dans le test ci-dessous
              // -- car le id est un texte et doc.id est un entier.
              let document = scope.baseDocuments.find(doc => doc.id == id);
              let printModel;
              if (document.data.values.nrdictrecepisseplanstaille === 'A4' ||
                  document.data.values.nrdictrecepisseplanstaille === 'A_4') {
                printModel = scope.mainConfig.autoAtlas.a4Model;
              } else {
                printModel = scope.mainConfig.autoAtlas.a3Model;
              }

              if (printModel) {
                scope.makingAtlasFor.push(id);
                makingAtlasForAsString();
                // Créer la géométrie pour l'atlas:
                // la variable 'intersectingCollection' contient toutes les features, que l'on doit
                // union and buffer avec le buffer 'scope.mainConfig.autoAtlas.intersectBuffer'
                const geomPromise
                  = GeometryFactory.unionandbuffer(intersectingCollection, 'ROUND',
                    scope.mainConfig.autoAtlas.intersectBuffer);
                geometryPromises.push(geomPromise);
                geomPromise.then(res => {

                  const params2 = {
                    needsPromise: true,
                    doNotCallAtlasFunction: true,
                    updateDocPromises
                  };
                  AtlasService.submitAtlasEsri(parametersOfEsriAtlas(
                    printModel,res.data, params2, id, document));
                  createAtlasPromises.push(params2.promise);
                });
              }
            }
          }


          // Une fois tous les atlas récupérés et sauvegardés, résolution de la promesse
          // de la fonction
          $q.all(geometryPromises).finally(() => {
            $q.all(createAtlasPromises).finally(() => {
              $q.all(saveAtlasPromises).finally(() => {
                $q.all(updateDocPromises).finally(() => {
                  addAllAtlasAutoDefer.resolve();
                  scope.noJobMessage = false;
                  if (scope.jobsStatus==='failed') {
                    $timeout (() => {
                      scope.displayLoader = false;
                      delete scope.makingAtlasFor;
                    },1500);
                  }
                });
              });
            });
          });
          return addAllAtlasAutoDefer.promise;
        };


        /**
         * Ajout d'un onglet à la configuration des liens filtre / rapports jasper concernés
         */
        scope.addTabsToNetworkJasper = () => {
          scope.mainConfig.automation.tabs.push({
            title: $filter('translate')('common.network')
              + ' ' + (scope.mainConfig.automation.tabs.length+1),
            filter: '',
            reportName: '',
            intersectingComponents: []
          });
          scope.automationTabInfos.dlbConcernedComponents.push({
            leftData: [],
            leftDisplayAttribute: 'alias',
            rightData: angular.copy(scope.lightFeatureTypes),
            rightDisplayAttribute: 'alias',
            leftTitle: 'dict.intersectingComponents',
            rightTitle: 'dict.availableComponents',
            source: 'right',
          });
          scope.automationTabInfos.currentTabNumber = scope.mainConfig.automation.tabs.length - 1;
        };

        /**
         * Toggle tab edit mode
         * @param mode
         */
        scope.toggleTabEditMode = mode => {
          scope.editMode = mode;
        };

        /**
         * Remove a tab
         */
        scope.removeTab = index => {
          if (scope.mainConfig.automation.tabs.length > 1) {
            scope.mainConfig.automation.tabs.splice(index, 1);
            scope.automationTabInfos.dlbConcernedComponents.splice(index, 1);
            scope.automationTabInfos.currentTabNumber--;
          }
        };

        /**
         * Déplacement d'un onglet
         */
        scope.moveTab = (index, direction) => {
          // Cas d'une extrémitée
          if ((index === 0 && direction === -1) ||
              (index === scope.mainConfig.automation.tabs.length - 1 && direction === 1)) {
            return false;
          }
          // Sauvegarde de l'onglet
          const savedTab = scope.mainConfig.automation.tabs[index];
          const savedDLB = scope.automationTabInfos.dlbConcernedComponents[index];

          // Changement des onglets
          scope.mainConfig.automation.tabs[index]
            = scope.mainConfig.automation.tabs[index + direction];
          scope.automationTabInfos.dlbConcernedComponents[index]
            = scope.automationTabInfos.dlbConcernedComponents[index + direction];
          scope.mainConfig.automation.tabs[index + direction] = savedTab;
          scope.automationTabInfos.dlbConcernedComponents[index + direction] = savedDLB;

          // Navigation vers l'onglet
          // KIS-3340: le déplacement d'un onglet doit garder le focus sur cet onglet
          $timeout(() => {
            scope.automationTabInfos.currentTabNumber = index + direction;
          });
        };

        /**
         * Au changement dans une DLB, mise à jour de la configuration
         * @param index
         */
        scope.dlbAfterMoveItem = (index) => {
          scope.mainConfig.automation.tabs[index].intersectingComponents =
              scope.automationTabInfos.dlbConcernedComponents[index].leftData;
        };

        // A la fermeture du widget
        scope.$on('openCloseTools_dictwidgetdirective', () => {
          if (boardPopup) {
            boardPopup.close();
          }
        });

        // A la fermeture de la catégorie
        scope.$on('closingCategory', () => {
          if (boardPopup) {
            boardPopup.close();
          }
        });

        // Au changement de catégorie
        scope.$on('switchingCategory', () => {
          if (boardPopup) {
            boardPopup.close();
          }
        });

        let tableConfDialog;

        /**
         * Ouverture de la fenêtre de configuration de la présentation tabulaire des DICTs.
         */
        scope.openTableConfiguration = () => {
          tableConfDialog = ngDialog.open({
            template: 'js/XG/widgets/mapapp/dict/views/dictTableConfig.html',
            className: 'ngdialog-theme-plain width400 nopadding miniclose',
            closeByDocument: false,
            scope: scope,
            preCloseCallback: () => {
              scope.workspace.tableAttributes
                = gaJsUtils.deepCopy(scope.mainConfig.table.attributes);
              return true;
            }
          });
        };


        /**
         * Mettre fond bleu pour dict concerne et vert pour dict non concernée
         * dans le tableau (gcdatatable).
         * @param {*} obj Ligne du tableau = une dict
         * @returns Le ng-class pour la ligne
         */
        scope.tableLineNgClass = (obj) => {
          return {'dict-concerned-line' : obj.properties.docinnerstate==='CONCERNE',
            'dict-not-concerned-line' : obj.properties.docinnerstate==='NON CONCERNE'};
        };

        /**
         * Zone mémoire de travail pour l'atlas manuel.
         * Il est nécessaire qu'elle soit dans ce scope,
         * pour accepter le travail avec différentes DICT.
         */
        scope.atlasEsri = {
          type: 'area',
          line_models: [],
          area_models: [],
          line_formats: [],
          area_formats: [],
        };
      },
    };
  };

  dictwidget.$inject = [
    'DICTFactory',
    'FeatureTypeFactory',
    'DataStoreFactory',
    'ReportFactory',
    'SridFactory',
    '$filter',
    'ngDialog',
    'gaDomUtils',
    'PrintFactory',
    '$rootScope',
    '$timeout',
    'extendedNgDialog',
    'ParametersFactory',
    'GeometryFactory',
    '$q',
    'AtlasArcGIS108Service', 'AtlasService', 'gaJsUtils', '$compile', '$location'
  ];
  return dictwidget;
});