'use strict';

/**
 * @ngdoc service
 * @name modules.model.service:FeatureTypeFactory
 * @description
 * FeatureTypeFactory
 */
define(function() {
  var FeatureTypeFactory = function($http, $q, $filter, processFactory) {
    var FeatureTypeFactory = {};

    /**
     * Class : FeatureTypeFactory
     * Factory WebServices
     */

    // isFiltered: true lorsque un filtre est présent sur le geocatalog
    // à partir des propriétés d’une application
    var resources = { featuretypes: [], isFiltered: false };
    var loaded = false;
    var specialHpoAppName = { name: undefined };

    /**
     * Function: add
     */
    function add(senddata) {
      var promise = $http.post(
        '/services/{portalid}/model/featuretype/add?f=json',
        senddata
      );
      promise.then(function(res) {
        const index = resources.featuretypes.findIndex(feat => feat.uid === res.data.uid);
        if(index ===-1){
          resources.featuretypes.push(res.data);
        }else{
          resources.featuretypes.splice(index,1,res.data);
        }
      });
      return promise;
    }
    /**
     * Function: postupdate
     */
    function postupdate(id) {
      var promise = $http.post(
        '/services/{portalid}/model/featuretype/postupdate?f=json' + '&id=' + id
      );
      promise.then(function(res) {
        if (res.data && res.data instanceof Array && res.data.length > 0) {
          var dataInfos = res.data;
          var dupes = {};

          $.each(dataInfos, function(i, fti) {
            if (!dupes[fti.uid]) {
              dupes[fti.uid] = true;

              var publishIndex = resources.featuretypes
                .map(function(x) {
                  return x.uid;
                })
                .indexOf(fti.uid);
              if (publishIndex != -1)
                resources.featuretypes[publishIndex] = fti;
            }
          });
        }
      });
      return promise;
    }

    /**
     * Function: removemultiple
     */
    function removemultiple(ids,removeTable) {
      var promise = $http.post(
        '/services/{portalid}/model/featuretype/removemultiple?f=json'
        + '&removeTable=' + removeTable,
        ids
      );
      promise.then((res)=> {
        if (Array.isArray(res.data.idsList)) {
          res.data.idsList.forEach((id) => {
            const deleteIndex = resources.featuretypes.findIndex((x) => x.uid === id);
            if (deleteIndex !== -1) {
              resources.featuretypes.splice(deleteIndex, 1);
            }
          });
        }
        if(res.data.errorList.length > 0){
          require('toastr').error(
            $filter('translate')('model.featuretypes.multiRemoveTableErrorName').replace(
              '$1',res.data.errorList)
          );
        }
      });
      return promise;
    }

    /**
     * Function: remove
     */
    function remove(id,removeTable) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/remove?f=json'
        + '&id=' + id + '&removeTable=' + removeTable
      );
      promise.then(function() {
        var deleteIndex = resources.featuretypes
          .map(function(x) {
            return x.uid;
          })
          .indexOf(id);
        resources.featuretypes.splice(deleteIndex, 1);
      });
      return promise;
    }

    /**
     * Function: removetable
     */
    function removetable(id) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/remove/table?f=json' +
          '&id=' +
          id
      );
      promise.then(function() {
        var deleteIndex = resources.featuretypes
          .map(function(x) {
            return x.uid;
          })
          .indexOf(id);
        resources.featuretypes.splice(deleteIndex, 1);
      });
      return promise;
    }

    let getPromise;

    /**
     * Function: get
     * Récupéaration de la liste des FTIs.
     * Modifié le 22/10/2021 pour éviter que 10000 appels soient faits
     * à cette fonction en appelant le service du serveur.
     * Si la liste contient déjà les FTIs, on la retourne.
     */
    function get(force) {
      const def = $q.defer();
      if (!force && resources.featuretypes.length !== 0 && !resources.getting) {
        //-- Les FTIs ont déjà été récupérés (et pas de récupération en cours)
        //-- donc, pas la peine de refaire appel au service.
        def.resolve(resources.featuretypes);
      }
      else {
        if (!force && resources.getting && getPromise) {
          // si la méthode est re-appelée avant la réponse du serveur,
          // on renvoie la promise du 1er appel API
          return getPromise;
        } else {
          resources.getting = true;
          $http.get(
            '/services/{portalid}/model/featuretype/get?f=json'
          ).then(function (res) {
            loaded = true;

            if (typeof res.data === 'string') {
              swal({
                title: $filter('translate')('common.featuretypes.errorGettingFeatureTypes'),
                text: res.data,
                type: 'error',
                showCancelButton: false,
                confirmButtonColor: '#DD6B55',
                confirmButtonText: $filter('translate')('common.ok'),
                closeOnConfirm: true,
              });
              def.resolve([]);
              return;
            }

            // distingue si le tableau de fti renvoyé est la totalité des fti du repo
            // ou bien une sélection filtrée depuis le fitre de couches de la configuration
            // de l'application
            let featuretypes = res.data;

            if (featuretypes.length > 0) {
              if (specialHpoAppName.name) {
                var uids = resources.featuretypes.map(function (x) {
                  return x.uid;
                });
                for (let i = 0; i < featuretypes.length; i++) {
                  if (featuretypes[i] && featuretypes[i].storeName === specialHpoAppName.name) {
                    const idx = uids.indexOf(featuretypes[i].uid);
                    if (~idx) {
                      resources.featuretypes[idx] = featuretypes[i];
                    } else {
                      resources.featuretypes.push(featuretypes[i]);
                    }
                  }
                }
              } else {
                resources.featuretypes = featuretypes.sort((a, b) => {
                  if (a.name < b.name) {
                    return -1;
                  }
                  if (a.name > b.name) {
                    return 1;
                  }
                  return 0;
                });
              }
            } else{
              resources.featuretypes = [];
            }

            resources.getting = false;
            def.resolve(resources.featuretypes);
          });
        }
      }
      getPromise = def.promise;
      return def.promise;
    }
    /**
     * ce service permet de mettre à jour une couche locale kis via le WFS
     * Méthode obsolète depuis KIS-3351
     * @param {string} featuretype
     * @returns
     */
    const updateFromWfs = (featuretype) => {
      return $http.post('/services/{portalid}/model/featuretype/updateFromWfs?f=json',featuretype);
    };


    /**
     * Function: update
     */
    function update(senddata) {
      var promise = $http.post(
        '/services/{portalid}/model/featuretype/update?f=json',
        senddata
      );
      promise.then(function(res) {
        if (res.data && res.data.feat) {
          var replaceIndex = resources.featuretypes
            .map(function(x) {
              return x.uid;
            })
            .indexOf(senddata.uid);
          resources.featuretypes[replaceIndex] = res.data.feat;
        }
      });
      return promise;
    }

    /**
     * Function: getextent
     */
    function getExtent(id, srid) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/getextent?f=json' +
          '&id=' +
          id +
          '&srid=' +
          srid
      );
      return promise;
    }

    /**
     * Function: publish
     */
    function publish(id) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/publish?f=json' + '&id=' + id
      );
      promise.then(() => {
        let feature = resources.featuretypes.find(feat => feat.uid === id);
        if(feature.geographic){
          feature.published = true;
        }
      });
      return promise;
    }
    /**
     * Function: mapping
     */
    function mapping(id) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/mapping?f=json' + '&id=' + id
      );
      promise.then(function(res) {
        var replaceIndex = resources.featuretypes
          .map(function(x) {
            return x.uid;
          })
          .indexOf(id);
        resources.featuretypes[replaceIndex].attributes = res.data.attributes;
        resources.featuretypes[replaceIndex].published = res.data.published;
        resources.featuretypes[replaceIndex].typeInfo = res.data.typeInfo;
        resources.featuretypes[replaceIndex].srid = res.data.srid;
        resources.featuretypes[replaceIndex].primaryKey = res.data.primaryKey;
        resources.featuretypes[replaceIndex].sharedWithArcGIS
          = res.data.sharedWithArcGIS;
      });
      return promise;
    }

    /**
     * getFeatureByUid
     * @param uid
     * @returns Object
     */
    function getFeatureByUid(uid) {
      if (typeof ftiFactory !== 'undefined') {
        return JSON.parse(ftiFactory.getFeatureByUid(uid));
      } else {
        for (var fType in resources.featuretypes) {
          if (angular.equals(resources.featuretypes[fType].uid, uid)) {
            return angular.copy(resources.featuretypes[fType]);
          }
        }
        return { uid:''};
      }
    }

    function getFeatureTypeDesc(storeName, ftName, deferred) {
      var indFeatureType;

      for (
        indFeatureType = 0;
        indFeatureType < resources.featuretypes.length;
        indFeatureType++
      )
        if (
          resources.featuretypes[indFeatureType].name == ftName &&
          resources.featuretypes[indFeatureType].storeName == storeName
        ) {
          if (deferred != null)
            deferred.resolve(
              angular.copy(resources.featuretypes[indFeatureType])
            );
          else return angular.copy(resources.featuretypes[indFeatureType]);
          break;
        }
    }
    /**
     * Function: addstyle
     */
    function addstyle(fid, name) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/' +
          fid +
          '/style/add?f=json&name=' +
          name
      );
      return promise;
    }

    /**
     * Function: setdefaultstyle
     */
    function setdefaultstyle(fid, name) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/' +
          fid +
          '/style/setDefault?f=json&name=' +
          name
      );
      return promise;
    }

    /**
     *     Look for table (layer) in the model repository list
     *  of tables (layers) gotten from the server.
     *  If the table is found then its description is returned,
     *  else NULL is returned.
     */
    function getFeatureTypeDescAsPromise(storeName, ftName) {
      var deferred = $q.defer();

      if (
        resources.featuretypes.length != undefined &&
        resources.featuretypes.length != 0
      )
        getFeatureTypeDesc(storeName, ftName, deferred);
      else
        get().then(function() {
          getFeatureTypeDesc(storeName, ftName, deferred);
        });

      return deferred.promise;
    }

    /**
     *
     * @param name
     */
    function getFeatureUidByName(name) {
      for (var fType in resources.featuretypes) {
        if (angular.equals(resources.featuretypes[fType].name, name)) {
          return angular.copy(resources.featuretypes[fType].uid);
        }
      }
      return null;
    }

    /**
     * Function: verify
     */
    function verify() {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/verify?f=json'
      );
      return promise;
    }
    /**
     * Function: publishall
     */
    function publishComponents(publishAll) {
      // ici ici
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/publishComponents?f=json&publishAll='
        + publishAll
      );
      promise.then(function(response) {
        for (var id in response.data) {
          resources.featuretypes[id].published
            = resources.featuretypes[id].geographic ? true : false;
        }

      });
      return promise;
    }

    /**
     * getFeatureByNameAndDatastore
     */
    var getFeatureByNameAndDatastore = function(datastore_name, feature_name) {
      if (typeof ftiFactory !== 'undefined') {
        return JSON.parse(
          ftiFactory.getFeatureByNameAndDatastore(datastore_name, feature_name)
        );
      } else {
        for (var i in resources.featuretypes) {
          var ft = resources.featuretypes[i];

          if (ft.storeName == datastore_name && ft.name == feature_name) {
            return ft;
          }
        }
        return false;
      }
    };
    /**
     * Function: isfeatureindatabase
     */
    function isfeatureindatabase(id) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/isFeatureInDatabase?f=json' +
          '&id=' +
          id
      );
      return promise;
    }

    /**
     *
     * @param name
     */
    function getFeatureByName(name) {
      for (var fType in resources.featuretypes) {
        if (angular.equals(resources.featuretypes[fType].name, name)) {
          return angular.copy(resources.featuretypes[fType]);
        }
      }
      return null;
    }

    /**
     * Function: checkAttributes
     */
    function checkAttributes(uid) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/checkAttributes?f=json' +
          '&uid=' +
          uid
      );
      return promise;
    }

    /**
     * Function: getFeatureTypeFromDataBase
     */
    function getFeatureTypeFromDataBase(uid) {
      var promise = $http.get(
        '/services/{portalid}/model/featuretype/getFeatureTypeFromDataBase?f=json' +
          '&uid=' +
          uid
      );
      return promise;
    }

    function getCheckAttributesReponse() {
      return [
        {
          name: 'groupe',
          alias: $filter('translate')(
            'model.featuretypes.checkAttributes.groupe'
          ),
          type: 'java.lang.String',
          isNillable: false,
          size: 500,
          restrictions: [],
          popup: false,
          autoCalcul: null,
        },
        {
          name: 'name',
          alias: $filter('translate')(
            'model.featuretypes.checkAttributes.name'
          ),
          type: 'java.lang.String',
          isNillable: false,
          size: 500,
          restrictions: [],
          popup: false,
          autoCalcul: null,
        },
        {
          name: 'type',
          alias: $filter('translate')(
            'model.featuretypes.checkAttributes.type'
          ),
          type: 'java.lang.String',
          isNillable: false,
          size: 500,
          restrictions: [],
          popup: false,
          autoCalcul: null,
        },
        {
          name: 'description',
          alias: $filter('translate')(
            'model.featuretypes.checkAttributes.description'
          ),
          type: 'java.lang.String',
          isNillable: false,
          size: 500,
          restrictions: [],
          popup: false,
          autoCalcul: null,
        },
        {
          name: 'acceptable',
          alias: $filter('translate')(
            'model.featuretypes.checkAttributes.acceptable'
          ),
          type: 'java.lang.Boolean',
          isNillable: false,
          size: 500,
          restrictions: [],
          popup: false,
          autoCalcul: null,
        },
        {
          name: 'solution',
          alias: $filter('translate')(
            'model.featuretypes.checkAttributes.solution'
          ),
          type: 'java.lang.String',
          isNillable: false,
          size: 500,
          restrictions: [],
          popup: false,
          autoCalcul: null,
        },
      ];
    }

    function hasAttributeWithName(ftiUid, attName, caseSensitive) {
      var ind;
      var fti = getFeatureByUid(ftiUid);

      if (fti == undefined) return '';

      if (!caseSensitive) {
        for (ind = 0; ind < fti.attributes.length; ind++)
          if (fti.attributes[ind].name == attName)
            return fti.attributes[ind].name;
      } else {
        attName = attName.toLowerCase();
        for (ind = 0; ind < fti.attributes.length; ind++)
          if (fti.attributes[ind].name.toLowerCase() == attName)
            return fti.attributes[ind].name;
      }
      return '';
    }

    function isLoaded() {
      return loaded;
    }

    function integerAttributeExistsInArcgisFeatures(portalid, attribute) {
      return $http.get(
        '/services/' + portalid +
        '/model/featuretype/arcgisintegerattributeexists?f=json' +
        '&attribute=' + attribute
      );
    }

    /**
     * Met à jour les index des composants ArcGIS
     * @returns {Promise} contenant la liste des composants KIS issus
     * d’Arcgis qui ne sont pas retrouvé dans le(s) mapservice(s)
     */
    function updateEsriComponentIndexes(){
      return $http.get(
        '/services/{portalid}/model/featuretype/updateEsriComponentIndexes'
      );
    }

    /**
     * Récupère le nom du fti à partir de son uid fourni en paramètre
     * La recherche s'effectue uniquement sur le tableau de fti du service: resources.featuretypes
     * @param ftiuid uid du fti à rechercher
     * @return {null|string} nom du fti recherché ou null si le paramètre
     * est null|undefined ou si le fti n'existe pas dans le tableau de fti du service
     */
    const getFeatureTypeNameByUid = (ftiuid) => {
      if (ftiuid) {
        const fti = resources.featuretypes.find(fti => fti.uid === ftiuid);
        if (fti) {
          return fti.name;
        }
      }
      return null;
    };

    /**
     * Lance la mise à jour de composants par WFS.
     * Un thread sera créé pour chaque processus de mise à jour de composant
     * @param {string} componentNames noms des composants séparés par une virgule
     * (wfsComponent.config.typeName)
     * @return {Promise} contient une map des processus créés {nom composant -> processus}
     */
    const startWfsUpdates = (componentNames) => {
      return $http.get('/services/{portalid}/model/featuretype/startWfsUpdates?f=json' +
          '&components=' + componentNames
      );
    };

    /**
     * Vérifie l'état du processus de mise à jour de composants par WFS
     * @param {string} processUids noms des composants séparés par
     *  une virgule (wfsComponent.config.typeName)
     * @return {Promise} contient une map des processus en cours {nom composant -> processus}
     */
    const checkWfsUpdates = (processUids) => {
      return $http.get('/services/{portalid}/model/featuretype/checkWfsUpdates?f=json' +
          '&processUids=' + processUids
      );
    };

    /**
     * Interruption de la mise à jour d'un composant par WFS.
     * Le thread est interrompu et le processus est passé en statut "CANCELED"
     * @param {string} processUids identifiant des processus séparés par une virgule
     * @return {Promise} contenant le processus stoppé
     */
    const stopWfsUpdates = (processUids) => {
      return $http.get('/services/{portalid}/model/featuretype/stopWfsUpdates?f=json' +
          '&processUids=' + processUids
      );
    };

    /**
     * Dans le tableau de composants du service, filtre les composants ayant une configuration WFS
     * @return {Promise} contient un tableau de composants ayant une configuration WFS
     */
    const findWfsFeatureTypes = () => {
      const defer = $q.defer();

      const reduceFeatureTypes = () => {
        return resources.featuretypes.reduce((accumulator, ft) => {
          if (Object.prototype.hasOwnProperty.call(ft, 'wfsConfig')
            && ft.wfsConfig && Object.keys(ft.wfsConfig).length > 0
            && typeof ft.wfsConfig.url === 'string' && ft.wfsConfig.url.length > 0) {
            accumulator.push({name: ft.name, config: ft.wfsConfig});
          }
          return accumulator;
        }, []);
      };

      if (resources.featuretypes.length > 0) {
        defer.resolve(reduceFeatureTypes());
      } else {
        get(false).then(
          () => {
            defer.resolve(reduceFeatureTypes());
          },
          () => {
            defer.resolve([]);
          }
        );
      }
      return defer.promise;
    };

    /**
     * Construit un tableau à partir des composants ayant une mise à jour WFS (wfsConfig != null).
     * Créé par composant un objet ayant 3 propriétés:<ul><li>
     * <code>name</code> qui est le nom du composant (fti.name)</li><li>
     * <code>config</code> qui est la configuration WFS du composant (fti.wfsConfig)</li><li>
     * <code>process</code> qui est le processus de mise à jour WFS
     * si un fichier est présent dans le dossier PROCESSUS</li></ul>
     * @return {Promise} tableau <code>[{name: fti.name,
     * config: fti.wfsConfig, process: processm},{...},...]</code>
     */

    /**
     * Vérifie la présence d'un processus de mise à jour WFS
     * @return {Promise} contient true si un processus est à l'état "RUNNING"
     */
    const checkWfsUpdateProcesses = () => {
      const defer = $q.defer();

      findWfsFeatureTypes().then(
        res => {
          if (Array.isArray(res) && res.length > 0) {
            const componentNames = res.map(comp => comp.name).join(',');
            processFactory.getProcessesByNametype(componentNames, 'wfsUpdate').then(
              res => {
                if (res.data) {
                  defer.resolve(Object.values(res.data).some(
                    process => process !== null && process.etat === 'RUNNING'));
                } else {
                  defer.resolve(false);
                }
              },
              err => {
                console.error(err.data);
                defer.resolve(false);
              }
            );
          }

        },
        () => {
          defer.resolve(false);
        });
      return defer.promise;
    };

    /**
     * Ajoute/enlève une classe CSS au bouton d'action "Synchronisation WFS"
     * @param isUpdating true si un composant possède un processus de mise à jour
     *   en cours d'exécution
     * @param extraGlobalActions boutons d'action globale de la page "/feature"
     */
    const setWFSButtonBorder = (isUpdating, extraGlobalActions) => {
      const WFS_CSS_CLASS = 'wfs-updating';
      // Détermine le rang du bouton WFS dans la configuration de la page /feature
      const buttonIndex = extraGlobalActions.findIndex(
        btn => btn.label === 'model.featuretypes.wfsMonitor.tooltip');
      if (buttonIndex > -1) {

        // Récupère les élements HTML des boutons d'action globale de la page /feature
        const buttons = document.querySelectorAll('.editList_header .buttons button');
        if (buttons && buttons.length > buttonIndex) {

          // Récupère le bouton WFS au même rang que celui du bouton dans la configuration
          const wfsButton = buttons[buttonIndex];

          // Teste s'il s'agit du bon bouton
          if (wfsButton && wfsButton.querySelector('i.fa-cloud') != null
              && wfsButton.querySelector('i.fa-database') != null) {

            if (isUpdating) {
              // Démarrage de l'upload: ajoute la classe "wfs-updating" au bouton
              if (!wfsButton.classList.contains(WFS_CSS_CLASS)) {
                wfsButton.classList.add(WFS_CSS_CLASS);
              }
            } else if (wfsButton.classList.contains(WFS_CSS_CLASS)) {
              // Arrêt de l'upload: retire la classe "wfs-updating" au bouton
              wfsButton.classList.remove(WFS_CSS_CLASS);
            }
          }
        }
      }
    };

    return {
      FeatureTypeFactory: FeatureTypeFactory,
      resources: resources,
      specialHpoAppName: specialHpoAppName,
      add: add,
      postupdate: postupdate,
      update: update,
      removemultiple: removemultiple,
      remove: remove,
      get: get,
      getExtent: getExtent,
      publish: publish,
      mapping: mapping,
      getFeatureTypeDesc: getFeatureTypeDesc,
      addstyle: addstyle,
      setdefaultstyle: setdefaultstyle,
      getFeatureTypeDescAsPromise: getFeatureTypeDescAsPromise,
      getFeatureByUid: getFeatureByUid,
      getFeatureUidByName: getFeatureUidByName,
      verify: verify,
      publishComponents: publishComponents,
      isfeatureindatabase: isfeatureindatabase,
      getFeatureByNameAndDatastore: getFeatureByNameAndDatastore,
      getFeatureByName: getFeatureByName,
      checkAttributes: checkAttributes,
      getCheckAttributesReponse: getCheckAttributesReponse,
      hasAttributeWithName: hasAttributeWithName,
      isLoaded: isLoaded,
      integerAttributeExistsInArcgisFeatures: integerAttributeExistsInArcgisFeatures,
      updateEsriComponentIndexes: updateEsriComponentIndexes,
      getFeatureTypeFromDataBase: getFeatureTypeFromDataBase,
      getFeatureTypeNameByUid: getFeatureTypeNameByUid,
      updateFromWfs:updateFromWfs,
      checkWfsUpdates: checkWfsUpdates,
      startWfsUpdates: startWfsUpdates,
      stopWfsUpdates: stopWfsUpdates,
      findWfsFeatureTypes: findWfsFeatureTypes,
      checkWfsUpdateProcesses: checkWfsUpdateProcesses,
      setWFSButtonBorder: setWFSButtonBorder
    };
  };
  FeatureTypeFactory.$inject = ['$http', '$q', '$filter', 'processFactory'];
  return FeatureTypeFactory;
});