'use strict';
define(function() {
  var javalangstring = function(FeatureAttachmentFactory, $filter, gaJsUtils, formFunctionUtils,
    ngDialog, FilesFactory, $timeout, EditFactory, FormsHelper) {
    return {
      templateUrl: 'js/XG/widgets/utilities/form/views/gcattachments.html',
      restrict: 'A',
      scope: {
        ftid: '=ftid',
        att: '=gcatt',
        result: '=result',
        activelabel: '=activelabel',
        initvalue: '=initvalue',
        fieldData: '=?fieldData',
        current: '=current',
        uploadfile: '=uploadfile'
      },

      link: function(scope, elt) {
        scope.restrict = false;
        scope.restricttype = '';

        scope.selected_filename = {};
        if (!angular.isUndefined(scope.initvalue)) {
          if (scope.initvalue[scope.att.name]) {
            //scope.selected_filename.name =  scope.initvalue[scope.att.name];
            scope.filenames = scope.initvalue[scope.att.name].split(',');
          }
        }

        scope.filenames = [];

        /**
         * Appelé quand l'objet sélectionné change pour relecture
         * de la liste des fichiers attachés. La liste stocké selon
         * un format "fichier 1,fichier2,fichier3, ...,fichierN" est transformé
         * en un tableau pour prise en compte dans la directive d'affichage.
         *
         */
        const refreshFileList = () => {
          if (scope.result != undefined && scope.result[scope.att.name] != null) {
            scope.filenames = scope.result[scope.att.name].split(',');
          } else if (Array.isArray(scope.filenames)) {
            const filenamesCount = scope.filenames.length;
            if (filenamesCount > 0) {
              scope.filenames.splice(0, filenamesCount);
            }
          } else {
            scope.filenames = [];
          }
        };

        scope.$watch('current', refreshFileList);


        /**
         * Un fichier choisi a changé.
         *
         * @param {*} a : element HTML contenant les fichiers choisis
         */
        scope.fileNameChanged = function (a) {
          if ('files' in a) {
            for (let i = 0; i < a.files.length; i++) {
              const validFilename = gaJsUtils.replaceSpecialChars(a.files[i].name);
              scope.filenames.push(validFilename);
            }

            scope.result[scope.att.name] = scope.filenames.join(',');

            if (!scope.uploadfile) {
              scope.uploadfile = {};
            }
            if (scope.uploadfile[scope.att.name]) {
              for (let i = 0; i < a.files.length; i++) {
                const validFile = gaJsUtils.renameFileWithoutSpecialChar(a.files[i]);
                scope.uploadfile[scope.att.name].push(validFile);
              }
            } else {
              scope.uploadfile[scope.att.name] = [];

              for (let i = 0; i < a.files.length; i++) {
                const validFile = gaJsUtils.renameFileWithoutSpecialChar(a.files[i]);
                scope.uploadfile[scope.att.name].push(validFile);
              }
            }
            uploadFiles(a);
          }
        };

        /**
         * Au clic sur le bouton "Supprimer" (-) du champ de type attachments,
         * supprime les fichiers sélectionnés : <ul><li>
         * du dossier UPLOAD en mode création</li><li>
         * du dossier ATTACHMENTS en mode édition</li></ul>
         */
        scope.removeFile = function() {
          if (Array.isArray(scope.selected_filename.name) && scope.current) {

            const featureIsAlreadySaved = (typeof scope.current.id === 'string' && scope.current.id.length > 0)
                  || (typeof scope.current.id === 'number' && Number.isFinite(scope.current.id));

            if (!featureIsAlreadySaved && scope.fieldData.hasOwnProperty('attachmentId')) {

              // mode création (objet pas enregistré): on supprime les fichiers sélectionnés du dossier upload
              const promises = [];
              for (let selectedFilename of scope.selected_filename.name) {
                const promise = FilesFactory.removeUploaded(scope.fieldData.attachmentId, selectedFilename);
                promises.push(promise);
              }
              const startTime = new Date().getTime();
              $q.all(promises).then(
                res => {
                  if (Array.isArray(res.data)) {
                    for (const deletedFilename of res.data) {
                      const deleteIndex = scope.filenames.indexOf(deletedFilename);
                      scope.filenames.splice(deleteIndex, 1);
                      scope.result[scope.att.name] = scope.filenames.join();
                    }
                    const endTime = new Date().getTime();
                    const elapsedTime = (endTime - startTime) / 1000;
                    console.info($filter('translate')('featureattachment.uploadDeleteSuccess')
                        + scope.selected_filename.name.join(), ' ' + elapsedTime + ' sec');
                  }
                },
                error => {
                  if (Array.isArray(error.data)) {
                    for (const err of error.data) {
                      console.error($filter('translate')('featureattachment.deleteOneError') + error.data);
                    }
                  } else {
                    console.error($filter('translate')('featureattachment.deleteOneError') + error.data);
                  }
                }
              );
            } else {
              const severalFilesToDelete = scope.selected_filename.name.length > 1;
              const title = 'featureattachment.delete' + (severalFilesToDelete ? 'All' : '') + 'Confirm';
              let isProcessing = false;
              swal({
                title: $filter('translate')(title),
                type: 'warning',
                showCancelButton: true,
                confirmButtonColor: '#DD6B55',
                confirmButtonText: $filter('translate')('common.yes'),
                cancelButtonText: $filter('translate')('common.no'),
                closeOnConfirm: true,
              },
              isConfirm => {
                if (isConfirm && !isProcessing) {
                  // mode édition (objet enregistré): on supprime les fichiers sélectionnés du dossier attachments
                  if (scope.selected_filename.name.length > 0) {
                    const filenamesString = scope.selected_filename.name.join();
                    const startTime = new Date().getTime();
                    FeatureAttachmentFactory.remove(filenamesString, scope.ftid.name, scope.current.id).then(
                      res => {
                        const endTime = new Date().getTime();
                        const elapsedTime = (endTime - startTime) / 1000;
                        console.info($filter('translate')(res.data) + ' ' + filenamesString, ' ' + elapsedTime + ' sec');
                        for (const selectedFilename of scope.selected_filename.name) {
                          const deleteIndex = scope.filenames.indexOf(selectedFilename);
                          scope.filenames.splice(deleteIndex, 1);
                        }
                        scope.result[scope.att.name] = scope.filenames.join();
                        // KIS-3010:  l’enregistrement du champ attachment/attachment-multi (et uniquement celui-ci) se fait automatiquement
                        updateAttachmentsAttributeOnly(scope.current.id, []);
                      }
                    );
                  }
                  isProcessing = true;
                }
              }
              );
            }
          } else {
            require('toastr').error($filter('translate')('featureattachment.noselectedfile'));
          }
        };
        scope.addFile = function() {};

        scope.download = function() {
          if (angular.isDefined(scope.current) && scope.current.id) {
            if ( scope.selected_filename.name) {
              scope.selected_filename.name.forEach(function(name) {
                console.log(name);
                let id = gaJsUtils.getIdInCaseEsriId(scope.current, scope.ftid, scope.result);
                FeatureAttachmentFactory.getdownloadurl(
                  name, scope.ftid.name, id
                ).then((res) => {
                  gaJsUtils.downloadFile(res.data,id,name);
                },
                (res) => {
                  require('toastr').error(res.data);
                });
              }
              );
            } else {
              require('toastr').error(
                $filter('translate')('featureattachment.noselectedfile')
              );
            }
          }
        };

        /**
         * Après avoir sélectionné plusieurs fichiers,
         * si l'objet du formulaire n'est pas enregistré, charge les fichiers dans le dossier "UPLOAD".<br>
         * Ou bien si l'objet du formulaire est déjà enregistré, charge les fichiers dans le dossier "ATTACHMENT".<br>
         * Lance l'ouverture d'une popup de progression du chargement des fichiers
         * @param {object} inputData objet contenant la liste des fichiers importés
         */
        const uploadFiles = (inputData) => {
          if (!scope.ftid) {
            require('toastr').error($filter('translate')('featureattachment.formWithoutFti'));
            return;
          }
          if (inputData && inputData.files && inputData.files.length > 0) {

            setFilesToUploadProgressionData();
            if (!scope.uploadedFiles) {
              scope.uploadedFiles = [];
            } else {
              scope.uploadedFiles.splice(0, scope.uploadedFiles.length);
            }

            if (scope.current) {
              const featureIsAlreadySaved = (typeof scope.current.id === 'string'
                  && scope.current.id.length > 0)
                  || (typeof scope.current.id === 'number' && Number.isFinite(scope.current.id));

              if (featureIsAlreadySaved) {
                const featureId = gaJsUtils.getIdInCaseEsriId(scope.current, scope.ftid, null);
                $timeout(openProgressDialog);
                copyFilesToAttachmentFolder(featureId, inputData.files, 0, []);
              } else {
                const attachmentId = formFunctionUtils.setAttachmentProcessId(scope.fieldData);
                $timeout(openProgressDialog);
                copyFilesToUploadFolder(attachmentId, inputData.files, 0);
              }
            } else {
              console.error('uploadFile : aucun objet lié au formulaire. current = ',
                scope.current);
            }
          }
        };

        /**
         * Après avoir sélectionné plusieurs fichiers, quand l'objet du formulaire n'est pas enregistré,
         * charge un fichier après l'autre dans le dossier "UPLOAD" du repo
         * @param {string} attachmentId nom du sous-dossier où importer les fichiers attachés dans le dossier "UPLOAD"
         * @param {FileList} files tableau des fichiers choisis dans l'input file
         * @param {number} indexFile rang dans le tableau files du fichier à importer
         */
        const copyFilesToUploadFolder = (attachmentId, files, indexFile) => {
          const fileToImport = gaJsUtils.renameFileWithoutSpecialChar(files[indexFile]);
          scope.fileBeingLoaded = fileToImport.name;
          const startTime = new Date().getTime();
          FilesFactory.uploadFile(attachmentId, fileToImport).then(
            () => {
              const endTime = new Date().getTime();
              const elapsedTime = (endTime - startTime) / 1000;
              console.info($filter('translate')('featureattachment.uploadFileSuccess'),
                files[indexFile].name, ' ' + elapsedTime + ' sec');
              scope.uploadedFiles.push(scope.fileBeingLoaded);
            }
          ).catch(error => {
            console.error(error);
            require('toastr').error(
              $filter('translate')('featureattachment.importToAttachmentsFailed'),
              files[indexFile].name);
          })
            .finally(() => {
              if (++indexFile < files.length) {
                copyFilesToUploadFolder(attachmentId, files, indexFile);
              } else {
                scope.isImportFilesDone = true;
                require('toastr').success($filter('translate')('featureattachment.importDone'));
              }
            });
        };

        /**
         * Après avoir sélectionné plusieurs fichiers, quand l'objet du formulaire est déjà enregistré,
         * charge un fichier après l'autre dans le dossier "ATTACHMENTS" du repo
         * @param {string} featureId nom du sous-dossier où importer les fichiers attachés dans le dossier "ATTACHMENTS"
         * @param {FileList} files tableau des fichiers choisis dans l'input file
         * @param {number} indexFile rang dans le tableau files du fichier à importer
         * @param {string[]} failedFiles tableau pour recevoir les noms des fichiers dont l'upload échoue
         */
        const copyFilesToAttachmentFolder = (featureId, files, indexFile, failedFiles) => {
          // copie directement le fichier dans le dossier ATTACHMENTS du repo
          const fileToImport = gaJsUtils.renameFileWithoutSpecialChar(files[indexFile]);
          scope.fileBeingLoaded = fileToImport.name;
          const startTime = new Date().getTime();
          FeatureAttachmentFactory.addFile(scope.ftid.name, featureId, fileToImport).then(
            (attach) => {
              const endTime = new Date().getTime();
              const elapsedTime = (endTime - startTime) / 1000;
              console.info($filter('translate')('featureattachment.copyToAttachmentSuccess'),
                attach.data.fileName, ' ' + elapsedTime + ' sec');
              scope.uploadedFiles.push(scope.fileBeingLoaded);
            },
            (error) => {
              console.error(error);
              require('toastr').error($filter('translate')('featureattachment.importToAttachmentsFailed'),
                files[indexFile].name);
              if (Array.isArray(failedFiles)) {
                failedFiles.push(files[indexFile].name);
              }
            }
          )
            .finally(() => {
              if (++indexFile < files.length) {
                copyFilesToAttachmentFolder(featureId, files, indexFile, failedFiles);
              } else {
              // KIS-2937 après que tous les fichiers aient été copiés dans le dossier attachments
              // on enregistre la nouvelle valeur de l'attribut de l'objet
                updateAttachmentsAttributeOnly(featureId, failedFiles);
              }
            });
        };

        /**
         * Après ouverture de la popup des fichiers attachés,
         * affiche la progression de l'upload des fichiers attachés
         * @param totalFileSize somme de la taille de tous les fichiers attachés
         */
        scope.updateProgressBar = (totalFileSize) => {

          const frame = () => {

            if (widthPct >= 100 || Number.isNaN(widthPct)
                || !Number.isFinite(widthPct) || !Number.isFinite(totalFileSize)) {
              // l'inport est terminé
              // stoppe la répétition
              clearInterval(id);
              // ferme la popup de progression de l'upload
              closeProgressBarPopup();
            } else {
              // calcule le poids total des fichiers déjà importés
              let uploadedSize = 0;
              for (const file of
                scope.filesToUpload.filter(file => scope.uploadedFiles.includes(file.name))) {
                uploadedSize += file.size;
              }
              if (Number.isFinite(totalFileSize)) {
                // calcule la largeur de la barre de progression et le pourcentage à afficher
                widthPct = Math.round(uploadedSize / totalFileSize * 100);
                scope.progressWidth = widthPct;
                scope.uploadProgress = widthPct + '%';
              }
            }
          };
          let widthPct = 0;
          const id = setInterval(frame, 10);
        };

        /**
         * Après dépôt de fichiers attachés dans le champ de l'attribut,
         * créé les variables nécessaires à l'affichage de la progression de l'upload des fichiers attachés
         */
        const setFilesToUploadProgressionData = () => {
          scope.filesToUpload = [];
          scope.totalFileSize = 0;
          if (Array.isArray(scope.uploadfile[scope.att.name])) {
            const attributeFiles = scope.uploadfile[scope.att.name];
            for (const file of attributeFiles) {
              scope.filesToUpload.push({
                name: file.name,
                size: file.size
              });
              scope.totalFileSize += file.size;
            }
          }
        };

        /**
         * Après dépôt de fichiers attachés dans le champ de l'attribut,
         * ouvre la popup qui affiche la barre de progression de l'upload des fichiers attachés
         */
        const openProgressDialog = () => {
          let isClosing = false;
          ngDialog.open({
            template: 'js/XG/widgets/utilities/edit/views/gceditsave.upload.progress.html',
            className: 'ngdialog-theme-plain width600 nopadding noclose gceditsave-upload',
            closeByDocument: false,
            scope: scope,
            preCloseCallback: () => {
              if (!isClosing) {
                delete scope.totalFileSize;
                delete scope.uploadProgress;
                delete scope.progressWidth;
                delete scope.isImportFilesDone;
                isClosing = true;
              }
            }
          });
        };

        /**
         * KIS-2937
         * Uniquement dans le cas d'un objet déjà enregistré,
         * met à jour uniquement l'attribut gcattachments de l'objet après copie des fichiers dans le dossier ATTACHMENTS du repo
         * @param {string} featureId
         * @param {string[]} failedFiles
         */
        const updateAttachmentsAttributeOnly = (featureId, failedFiles) => {
          // met à jour l'atttribut de l'objet directement après la copie dans le dossier ATTACHMENTS
          if (scope.ftid.type === 'esri' && gaJsUtils.notNullAndDefined(scope.result['objectid'])) {
            featureId = scope.ftid.name + '.' + scope.result['objectid'];
          }
          const featureToUpdate = {
            id: featureId,
            properties: {}
          };

          // il est possible que tous les fichiers n'aient pas été correctement uploadés
          // dans ce cas on ne conserve que les noms des fichiers réellement uploadés
          featureToUpdate.properties[scope.att.name] = updateUploadedFilenamesOnly(scope.result[scope.att.name], failedFiles);
          const featureToUpdateWrapper = {
            features: [featureToUpdate]
          };

          EditFactory.updatespecifiedproperties(scope.ftid.uid, featureToUpdateWrapper, null).then(
            () => {
              scope.isImportFilesDone = true;
              require('toastr').success($filter('translate')('featureattachment.importDone'));
              // met à jour l'objet dans la liste des objets sélectionnés
              scope.$emit('updateGcdatatable', {isNew: false, idPopup: FormsHelper.getIdDiv(elt)});
            },
            err => {
              if (err.data && Array.isArray(err.data.errors)) {
                for (const error of err.data.errors) {
                  console.error(error);
                }
                require('toastr').error($filter('translate')('featureattachment.updateFeatureError'),
                  ' Attribut : ', scope.att.name);
              }
            }
          ).finally(closeProgressBarPopup);
        };

        /**
         * Retourne une valeur d'attribut gcattachements en enlevant les noms de fichiers qui sont inclus dans une liste fournie en paramètre
         * @param {string} filenames valeur de l'attribut gcattachements
         * @param {string[]} failedFiles tableau de noms de fichiers à effacer dans la valeur de l'attribut gcattachements
         * @return {string} valeur d'attribut gcattachements sans les noms de fichiers inclus dans le tableau <code>failedFiles</code>
         */
        const updateUploadedFilenamesOnly = (filenames, failedFiles) => {
          if (Array.isArray(failedFiles) && failedFiles.length > 0) {
            let uplodedFileNamesArray = [];
            let filenamesArray = [];
            if (typeof filenames === 'string') {
              filenamesArray = filenames.split(',').filter(name => typeof name === 'string').map(name => name.trim());
            } else if (Array.isArray(filenames)) {
              filenamesArray = filenames;
            }
            for (const filename of filenamesArray) {
              if (!failedFiles.includes(filename)) {
                uplodedFileNamesArray.push(filename);
              }
            }
            return uplodedFileNamesArray.join(',');
          } else {
            return filenames;
          }
        };

        /**
         * Ferme la popup de la barre de progression
         * Recherche la popup contenant l'élément HTML portant l'id de la barre de progression
         */
        const closeProgressBarPopup = () => {
          const progressBar = document.getElementById('progressBar');
          if (progressBar) {
            const dialog = progressBar.closest('.ngdialog');
            if (dialog) {
              let dialogId = dialog.getAttribute('id');
              ngDialog.close(dialogId);
            }
          }
        };


        /**
         * Réaction à la suppression d'un fichier depuis l'onglet "Pièces jointes".
         */
        scope.$on('attachmentChanged', (event, params) => {
          const id = scope.current.id;
          if (params && params.scopeId !== scope.$id && params.featureId===id) {
            if (params.removedFile) {
              // -- Enlever le nom du fichier dans la liste des dcuments joints
              const filenamesArray = scope.result[scope.att.name].split(',');
              for (const filename of filenamesArray) {
                if (filename === params.removedFile) {
                  const deleteIndex = filenamesArray.indexOf(filename);
                  filenamesArray.splice(deleteIndex, 1);
                  break;
                }
              }
              // -- Mettre à jour l'attribut gcattachments
              scope.result[scope.att.name] = filenamesArray.join();
              updateAttachmentsAttributeOnly(scope.current.id, []);
            }
          }
        });

      },
    };
  };

  javalangstring.$inject = ['FeatureAttachmentFactory', '$filter', 'gaJsUtils', 'formFunctionUtils',
    'ngDialog', 'FilesFactory', '$timeout', 'EditFactory', 'FormsHelper'];
  return javalangstring;
});
