'use strict';
define(function() {
  var gcelement = function(
    ngDialog,
    $filter,
    $timeout,
    ConfigFactory,
    hotkeys,
    $rootScope,
    FeatureTypeFactory,
    taOptions,
    gaJsUtils,
    AssociationFactory,
    $window,
    isUtils
  ) {
    return {
      templateUrl: 'js/XG/widgets/utilities/form/views/formBuilder.html',
      restrict: 'A',
      scope: {
        map: '=map',
        templateform: '=res',
        loadTemplate: '=?loadTemplate',
        toolBarWidget: '=?toolbarwidget',
        onvalidate: '&',
        formIsDirty: '=?',
      },
      controller: [
        '$scope',
        function($scope) {
          $scope.tree = [];
          $scope.feattree = {};
        },
      ],
      link: function(scope) {
        scope.arboButtonsTitle = {
          visibility: $filter('translate')('tools.builder.tree.visibility'),
          options: $filter('translate')('tools.builder.tree.options'),
          add_branch: $filter('translate')('tools.builder.tree.add_branch'),
          remove_branch: $filter('translate')(
            'tools.builder.tree.remove_branch'
          ),
          remove_branch_children: $filter('translate')(
            'tools.builder.tree.remove_branch_children'
          ),
        };

        scope.xgos = $rootScope.xgos;

        const ISANDSIMILARFORMTYPES = ['intervention_simple', 'table_simple', 'recherche_donnees'];

        const checkIfFormWasModified = function() {
          $timeout(() => {
            let cmp = getComparableFormData();
            scope.formIsDirty = cmp != baseFormData;
          });
        };

        // store the currentBuilderForm data so we can use it in the function browser
        let baseTime = new Date();
        scope.$watch(
          'templateform',
          function(templateform) {
            // let's not check during the first 3 seconds, cause the form needs some time to be loaded
            // and 3 seconds needs like a reasonable amoutn of time during which no user modification can happen
            // ugly af but that will do for now
            if (new Date() - baseTime > 3000) checkIfFormWasModified();
            $rootScope.currentBuilderForm = templateform;
          },
          1
        );

        //set default values
        scope.templateform.formtype = 'form';
        scope.savefield = {
            config:{
              association:'selection_cible'
            }
          };

        scope.oldFormType = scope.templateform.formtype;

        /**
         * there are three types of intervention_simple like form
         * they are practically the same but some UI is hidden in some cases:
         * -intervention_simple: the complete version
         * -table_simple: a simpler version of intervention_simple without geographic stuff
         * -recherche_donnees: a simpler version of intervention_simple where you cannot modify data
         */
        scope.formTypes = [
          {
            key: 'form',
            pre: '<i class=\'fa fa-clipboard\'></i>',
            label: 'tools.builder.formtype.form',
          },
          {
            key: 'formtable',
            pre: '<i class=\'fa fa-list-ul\'></i>',
            label: 'tools.builder.formtype.formtable',
          },
          {
            key: 'intervention_simple',
            pre: '<i class=\'fa fa-cubes\'></i>',
            label: 'tools.builder.formtype.intervention_simple',
          },
          {
            key: 'table_simple',
            pre: '<i class=\'fa fa-edit\'></i>',
            label: 'tools.builder.formtype.table_simple',
          },
          {
            key: 'recherche_donnees',
            pre: "<i class='fa fa-search'></i>",
            label: 'tools.builder.formtype.recherche_donnees',
          }
        ];

        scope.switchFormType = function() {
          switch (scope.templateform.formtype) {
            case 'formtable':
              scope.resetForm();
              scope.curtemplate.fields = [
                {
                  label: 'Datatable',
                  type: 'data',
                  config: {
                    name: 'datable',
                  },
                },
              ];
              break;
            case 'intervention_simple':
              scope.resetForm();
              scope.curtemplate.breadcrumb = isUtils.initIsBreadCrumb('builder', scope.curtemplate, false, null);
              break;
            case 'table_simple':
              if (scope.oldFormType !== 'intervention_simple' && scope.oldFormType !== 'recherche_donnees') {
                scope.resetForm();
              }
              scope.curtemplate.breadcrumb = isUtils.initIsBreadCrumb('builder', scope.curtemplate, false, null);
              scope.initLeftTabs(true);
              break;
            case 'recherche_donnees':
              if (scope.oldFormType !== 'intervention_simple' && scope.oldFormType !== 'table_simple') {
                scope.resetForm();
                isUtils.setIsDefaultLabelsAndIcons(scope.curtemplate);
              }
              break;
            case 'form':
              scope.resetForm();
              scope.addTree();
              break;
            default:
              console.error('unknow form type: ' + scope.templateform.formtype);
              break;
          }

          scope.oldFormType = scope.templateform.formtype;
        };

        // ----------------------------------------
        // Main Menu, actions & Hotkeys
        // ----------------------------------------
        scope.builderActions = [
          {
            key: 'new',
            pre: '<em>Alt+N</em><i class="fa fa-plus-circle"></i> ',
            label: 'tools.builder.new',
          },
          {
            key: 'save',
            pre: '<em>Alt+S</em><i class="fa fa-floppy-o"></i>',
            label: 'tools.builder.save',
          },
          {
            key: 'open',
            pre: '<em>Alt+O</em><i class="fa fa-folder-open-o"></i>',
            label: 'tools.builder.open',
          },
          {
            key: 'render',
            pre: '<em>Alt+R</em><i class="fa fa-desktop"></i>',
            label: 'tools.builder.display_render',
          },
        ];

        // hotkeys
        hotkeys.add({
          combo: 'alt+r',
          description: 'Display in Render',
          allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
          callback: function() {
            switchToRender();
          },
        });
        hotkeys.add({
          combo: 'alt+s',
          description: 'Save',
          allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
          callback: function() {
            saveFormModal();
          },
        });
        hotkeys.add({
          combo: 'alt+o',
          description: 'Open',
          allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
          callback: function() {
            openFormModal();
          },
        });
        /**
         * Remove hotkeys when directive is destroyed
         */
        scope.$on('$destroy', function() {
          hotkeys.del('alt+r');
        });
        /**
         * Toggle CheatSheet
         */
        scope.toggleCheatSheet = function() {
          hotkeys.toggleCheatSheet();
        };

        /**
         * Building the editTree object which is used to edit the tree
         * @param data
         * @param depth
         * @returns {Array}
         */
        function buildEditTree(data, depth) {
          depth = depth || 1;

          var editTree = [];
          data.forEach(function(d) {
            // toggle visibility
            scope.setTreeViewVisibility(d);

            var element = {
              label: d.label,
              children: [],
              level: depth,
              style: getInputStyle(depth),
              template: d.template,
              visible: d.visible == false ? false : true,
              classes: d.classes,
              cfg: d.cfg ? d.cfg : {},
            };

            if (d.children.length > 0) {
              var sub = buildEditTree(d.children, depth + 1);
              sub.forEach(function(s) {
                element.children.push(s);
              });
            }
            editTree.push(element);
          });

          return editTree;
        }

        /*
         * Save / load form
         * */
        scope.currentFormData = {};
        function checkFormFtid(config) {
          var ind, prop, fti, ftid, ftiname;
          if (config instanceof Array)
            for (ind = 0; ind < config.length; ind++)
              checkFormFtid(config[ind]);
          else if (config instanceof Object != undefined) {
            if (config.ftid && config.ftiname) {
              fti = FeatureTypeFactory.getFeatureByUid(config.ftid);
              if (!fti) {
                //-- Le ftid n'existe pas, on cherche avec le nom du fti.
                ftid = FeatureTypeFactory.getFeatureUidByName(config.ftiname);
                if (ftid) config.ftid = ftid;
              }
            }
            for (prop in config) {
              if (config[prop] instanceof Object) checkFormFtid(config[prop]);
            }
          }
        }

        let baseFormData = {};
        const getComparableFormData = () => {
          let copy = angular.copy(scope.templateform);
          if (copy.data && angular.isArray(copy.data)) {
            copy.data.map(function(x) {
              // removing all abn tree view data
              delete x.level;
              delete x.visible;
              delete x.selected;
              delete x.expanded;
              delete x.uid;
              removePropertyInRelationsForm(x,'isactivepoint');
              removePropertyInRelationsForm(x,'value');
            });
          }else {
            // supprime la propriété isactivepoint du ftiselected des filtres de relations
            removePropertyInRelationsForm(copy.data,'isactivepoint');
            removePropertyInRelationsForm(copy.data,'value');
          }
          return JSON.stringify(copy);
        };

        let setFormData = function(data) {
          scope.templateform = {};
          scope.templateform = data;

          checkFormFtid(scope.templateform);

          // build a tree only if data is an array
          if (angular.isArray(scope.templateform.data)) {
            scope.tree = scope.templateform.data;
            // set editTree
            scope.editTree[0].children = buildEditTree(scope.templateform.data);
            $timeout(function() {
              scope.feattree.expand_all();
            }, 500);
          } else {
            // delete the defaut tree created by setDefaultTree
            delete scope.editTree;
            scope.tree = [];
            scope.curtemplate = scope.templateform.data;
          }

          if (scope.templateform.variables) {
            //if default variables are not in the form -> add them
            for(let defaultVariable of scope.formvariables) {

              const isContained = scope.templateform.variables.find(
                formVariable => formVariable.label === defaultVariable.label);
              if (!isContained) {
                scope.templateform.variables.push(defaultVariable);
              }
            }
            scope.formvariables = scope.templateform.variables;
          }

          scope.oldFormType = scope.templateform.formtype;
          if (ISANDSIMILARFORMTYPES.includes(scope.templateform.formtype)) {
            scope.curtemplate.breadcrumb = isUtils.initIsBreadCrumb('builder', scope.curtemplate, false, null);
          } else {
            scope.initLeftTabs(scope.templateform.formtype === 'table_simple');
          }

          if (ISANDSIMILARFORMTYPES.includes(scope.templateform.formtype) &&
            angular.isDefined(scope.curtemplate.fti_info)
          ) {
            // "fti_info" mélange les informations d'affichage de l'onglet "Liste" avec les informations sur le fti (store, name)
            scope.isComponent = {
              x: angular.copy(
                FeatureTypeFactory.getFeatureByNameAndDatastore(
                  scope.curtemplate.fti_info.storeName,
                  scope.curtemplate.fti_info.name
                )
              ),
            };
          }
        };

        /**
         * Load the form
         */
        scope.openForm = function(openData) {
          if (openData.name.indexOf('.form') == -1) {
            openData.name += '.form';
          }

          scope.currentFormData = openData;

          ConfigFactory.get(
            scope.currentFormData.type,
            scope.currentFormData.name,
            scope.currentFormData.application_name
          ).then(function(res) {
            setFormData(angular.copy(res.data));
            baseFormData = getComparableFormData();
            // récupère les infos du bouton "Enregistrer" global: scope.savefield = data.savefield
            setSaveFieldIS(res.data);
          });
        };
        function checkFtiNameForFtid(config) {
          var ind, prop, fti, ftid, ftiname;
          if (config instanceof Array)
            for (ind = 0; ind < config.length; ind++)
              checkFtiNameForFtid(config[ind]);
          else if (config instanceof Object != undefined) {
            if (config.ftid && !config.ftiname) {
              fti = FeatureTypeFactory.getFeatureByUid(config.ftid);
              if(fti)
                config.ftiname = fti.name;
            }
            for (prop in config) {
              if (config[prop] instanceof Object)
                checkFtiNameForFtid(config[prop]);
            }
          }
        }

        /**
         * Save the form
         */
        scope.saveForm = function() {
          var saveName =
            scope.currentFormData.name.indexOf('.form') == -1
              ? scope.currentFormData.name + '.form'
              : scope.currentFormData.name;

          checkFtiNameForFtid(scope.templateform);

          // ajoute les informations du bouton "Enregistrer" dans le form
          defineSaveField();

          baseFormData = getComparableFormData();
          scope.formIsDirty = false;

          ConfigFactory.add(
            scope.templateform,
            scope.currentFormData.type,
            saveName,
            scope.currentFormData.application_name
          ).then(function(res) {
            scope.$emit('formbuilder_save', {
              form: scope.currentFormData,
            });
            require('toastr').success(
              $filter('translate')('common.saved'),
              '',
              {
                positionClass: 'toast-bottom-left',
              }
            );
          });
        };
        /**
         * OpenForm Modal
         */
        var openFormModal = function() {
          ngDialog.open({
            template:
              'js/XG/widgets/utilities/form/views/modal/builderOpen.html',
            className: 'ngdialog-theme-plain width1000 nopadding miniclose',
            closeByDocument: false,
            scope: scope,
          });
        };

        /**
         * SaveForm Modal
         */
        var saveFormModal = function() {
          ngDialog.open({
            template:
              'js/XG/widgets/utilities/form/views/modal/builderSave.html',
            className: 'ngdialog-theme-plain width1000 nopadding miniclose',
            closeByDocument: false,
            scope: scope,
          });
        };
        /**
         * Switch the view to the render view
         */
        var switchToRender = function() {
          scope.newobj = {};
          scope.edit_resource = scope.ftis;
          scope.ftemplatedata = scope.templateform;

          ngDialog.open({
            template:
              'js/XG/modules/model/views/modals/modal.featuretypes.render.preview.html',
            className: 'ngdialog-theme-plain width1000 nopadding miniclose',
            closeByDocument: false,
            scope: scope,
          });
        };

        /**
         * Reset the form
         */
        scope.resetForm = function() {
          // reset tree
          delete scope.editTree;
          scope.tree = [];
          scope.curtemplate = {
            fields: [],
          };
          scope.templateform.data = scope.curtemplate;
          scope.droppedElements = {};
          scope.editMode = 'global';
          scope.isComponent = {};
        };

        /**
         * Reset the form and creates a new one
         */
        var newForm = function() {
          var ans = confirm($filter('translate')('tools.builder.confirm_new'));
          if (ans) {
            scope.resetForm();
            scope.setDefaultTree();
          }
        };

        /**
         * Calling actions according to what is selected
         */
        scope.builderAction = function() {
          switch (scope.selectedAction) {
          case 'new':
            newForm();
            break;
          case 'save':
            saveFormModal();
            break;
          case 'open':
            openFormModal();
            break;
          case 'render':
            switchToRender();
            break;
          }
          // reset action
          scope.selectedAction = '-';
        };

        // ----------------------------------------
        // Form events
        // ----------------------------------------
        scope.formevents = [];
        scope.possibleEvents = [
          {
            key: 'formEvents',
            label: 'tools.builder.form_events.events.form.title',
          },
          {
            key: 'formVariableEvents',
            label: 'tools.builder.form_events.events.form_variables.title',
          },
          {
            key: 'branchEvents',
            label: 'tools.builder.form_events.events.tree_branch.title',
          },{
            key: 'globalActionEvents',
            label: 'tools.builder.form_events.events.globalAction.title',
          },
          {
            key: 'externalsEvents',
            label: 'tools.builder.form_events.events.externals.title',
          }
        ];

        scope.addFormEvent = function() {
          scope.formevents.push({
            origin: {
              key: '',
              target: '',
            },
            actions: [],
          });
        };

        scope.showFormEvents = function() {
          if (scope.templateform.events) {
            scope.formevents = scope.templateform.events;
          }

          scope.$watch(
            'formevents',
            function(formevents) {
              var events = [];
              formevents.forEach(function(fe) {
                if (1) {
                  events.push(fe);
                }
              });

              if (events.length > 0) {
                scope.templateform.events = events;
              } else {
                delete scope.templateform.events;
              }
            },
            1
          );

          scope.formtree = angular.copy(scope.templateform.data);
          ngDialog.open({
            template:
              'js/XG/widgets/utilities/form/views/modal/builderFormEvents.html',
            className: 'ngdialog-theme-plain width1000 miniclose nopadding',
            closeByDocument: false,
            scope: scope,
          });
        };

        // ----------------------------------------
        // Tree Builder
        // ----------------------------------------

        scope.editMode = '';

        /**
         * Add a tree
         */
        scope.addTree = function() {
          var svgData = angular.copy(scope.templateform.data);
          scope.setDefaultTree();
          scope.editTree[0].children[0].template = svgData;
        };
        /**
         * Add a tree confirmation
         */
        scope.addTreeConfirm = function() {
          var ans = confirm(
            $filter('translate')('tools.builder.tree.confirm_add')
          );
          if (ans) {
            scope.addTree();
          }
        };

        /**
         * Remove the tree
         */
        scope.removeTree = function() {
          var ans = confirm(
            $filter('translate')('tools.builder.tree.confirm_delete')
          );
          if (ans) {
            scope.resetForm();
          }
        };

        /**
         * Removing current tree branch (splice parentData.children)
         * @param parentData
         * @param index
         */
        scope.deleteSelf = function(parentData, index) {
          parentData.children.splice(index, 1);
        };

        /**
         * Removing current tree branch children
         * @param data
         */
        scope.deleteChildrens = function(data) {
          var ans = confirm(
            $filter('translate')(
              'tools.builder.tree.confirm_remove_branch_children'
            )
          );
          if (ans) data.children = [];
        };

        /**
         *
         * @param data
         */
        scope.setTreeViewVisibility = function(data) {
          // handle visibility in the abn treeview
          if (angular.isUndefined(data.classes)) data.classes = [];

          // remove hiddenTreeBranch class
          if (data.classes.indexOf('hiddenTreeBranch') != -1) {
            data.classes.splice(data.classes.indexOf('hiddenTreeBranch'), 1);
          }

          // add hiddenTreeBranch class
          if (data.visible == false) {
            data.classes.push('hiddenTreeBranch');
          }
        };

        /**
         * Toggle visibility of the branch and its children
         * @param data
         * @param visible
         */
        scope.toggleVisiblity = function(data, visible) {
          data.visible = visible;
          scope.setTreeViewVisibility(data);

          if (data.children) {
            data.children.forEach(function(d) {
              scope.toggleVisiblity(d, visible);
            });
          }
        };

        /**
         * Return the text input style according to its level
         * @param level
         * @returns {string}
         */
        var getInputStyle = function(level) {
          return (
            'margin-left: ' +
            (level * 10 + 30) +
            'px; width: ' +
            (175 - level * 10) +
            'px;'
          );
        };

        /**
         * Add a children to the edited Tree
         * @param data
         */
        scope.addChildren = function(data) {
          data.children.push({
            label: '',
            children: [],
            level: data.level + 1,
            style: getInputStyle(data.level + 1),
            template: {
              fields: [],
            },
            visible: data.visible,
          });
        };

        var branchOptionsDialog;

        /**
         * displayBranchOptions
         * Affiche les options disponibles pour la branche en cours
         * @param data
         */
        scope.displayBranchOptions = function(data) {
          scope.currentBranchData = data;
          scope.currentBranchDataSvg = angular.copy(data);

          branchOptionsDialog = ngDialog.open({
            template:
              'js/XG/widgets/utilities/form/views/modal/modal.branch.options.html',
            className:
              'ngdialog-theme-plain width800 nopadding miniclose noclose',
            closeByDocument: false,
            scope: scope,
          });
        };

        /**
         * showSimpleBranchOptionPicker
         */
        scope.showSimpleBranchOptionPicker = function() {
          var show = true;
          var _var = gaJsUtils.checkNestedProperty(
            'cfg.displayCondition.operand',
            scope.currentBranchData
          );
          if (
            _var &&
            (_var.indexOf('boolean_') !== -1 || _var.indexOf('date_') !== -1)
          )
            show = false;
          return show;
        };
        /**
         * showDateBranchOptionPicker
         */
        scope.showDateBranchOptionPicker = function() {
          var show = false;
          var _var = gaJsUtils.checkNestedProperty(
            'cfg.displayCondition.operand',
            scope.currentBranchData
          );
          if (_var && _var.indexOf('date_') !== -1) show = true;
          return show;
        };

        /**
         * saveBranchOptions
         */
        scope.saveBranchOptions = function(data) {
          // var ans = true;
          // if (!data.cfg.displayCondition && angular.isDefined(data.cfg.displayConditionValue)) {
          //     ans = confirm($filter('translate')('tools.builder.tree.confirm_delete_display_condition'));

          // }
          // if (ans) {

          // if (reset) {
          //     delete scope.currentBranchData.cfg.displayCondition;
          //     delete scope.currentBranchData.cfg.displayConditionValue;
          // }

          // if (scope.currentBranchData.cfg.displayConditionValue == "") delete scope.currentBranchData.cfg.displayConditionValue;

          branchOptionsDialog.close();
          //}
        };

        /**
         * restoreBranchOptions
         */
        scope.restoreBranchOptions = function() {
          branchOptionsDialog.close();
          scope.currentBranchData = angular.copy(scope.currentBranchDataSvg);
        };

        /**
         * Switching edit mode (tree/global)
         * @param mode
         */
        scope.switchEditMode = function(mode) {
          scope.editMode = mode;
          // force selection of first tab
          if ((mode === 'global')) {
            scope.feattree.expand_all();
            scope.feattree.select_first_branch();
          }
        };

        /**
         * Switching tree branch
         * @param branch
         */
        scope.tree_handler = function(branch) {
          scope.curtemplate = branch.template;
          if (!angular.isDefined(scope.curtemplate.fieldsUnderTabs)) {
            scope.curtemplate.fieldsUnderTabs = false;
          }

          $timeout(function() {
            if (scope.curtemplate.activeTab == -1) {
              scope.curtemplate.activeTab = 0;
            }
          }, 0);
        };

        scope.togglePopupGeneralOptions = () => {
          scope.isPopupGeneralOptionsVisible =
            scope.isPopupGeneralOptionsVisible ? !scope.isPopupGeneralOptionsVisible : true;
        };

        scope.setFormPosition = (position) => {
          if (!scope.templateform.options) {
            scope.templateform.options = {};
          }
          scope.templateform.options.formPosition = position;
        };

        scope.setFormHeight = () => {
          if (!scope.templateform.options) {
            scope.templateform.options = {};
          }
          if (scope.templateform.options.formHeight < 0) {
            scope.templateform.options.formHeight = 0;
          } else if (scope.templateform.options.formHeight > 100) {
            scope.templateform.options.formHeight = 100;
          }
        };

        scope.setFormWidth = () => {
          if (!scope.templateform.options) {
            scope.templateform.options = {};
          }
          if (scope.templateform.options.formWidth < 0) {
            scope.templateform.options.formWidth = 0;
          } else if (scope.templateform.options.formWidth > 100) {
            scope.templateform.options.formWidth = 100;
          }
        };

        scope.clearConfigHeight = () => {
          scope.templateform.options.formHeight = undefined;
        }

        scope.clearConfigWidth = () => {
          scope.templateform.options.formWidth = undefined;
        }

        // ----------------------------------------
        // Form Builder
        // ----------------------------------------
        // list of variables that can be used as return object for the form fields
        scope.formvariables = [
          {
            label: 'current',
            description: $filter('translate')('tools.builder.intervention_simple.variable_description.current'),
            fields: [],
          },
          {
            label: 'geometrie_cible',
            description: $filter('translate')('tools.builder.intervention_simple.variable_description.geometrie_cible'),
            fields: [],
          },
          {
            label: 'selection_cible',
            description: $filter('translate')('tools.builder.intervention_simple.variable_description.selection_cible'),
            fields: [],
          },
          {
            label: 'objets_cible',
            description: $filter('translate')('tools.builder.intervention_simple.variable_description.objets_cible'),
            fields: [],
          },
        ];

        scope.setDefaultTree = function() {
          // Init tree and select first branch
          scope.editTree = [
            {
              label: $filter('translate')('tools.builder.tree.root'),
              children: [],
              level: 0,
              style: getInputStyle(0),
              disabled: true,
              visible: true,
            },
          ];

          scope.editTree[0].children.push({
            label: $filter('translate')('tools.builder.tree.first_branch'),
            children: [],
            level: 1,
            style: getInputStyle(1),
            template: {
              fields: [],
            },
            visible: true,
          });
          $timeout(function() {
            scope.feattree.select_first_branch();
          }, 500);
        };
        scope.setDefaultTree();

        /**
         * Add a tab to the current tree branch
         */
        scope.addTabsToBranch = function() {
          if (angular.isUndefined(scope.curtemplate.tabs))
            scope.curtemplate.tabs = [];
          scope.curtemplate.tabs.push({
            title: $filter('translate')(
              'tools.builder.form.tabs.default_title'
            ),
            fields: [],
          });
          if (scope.curtemplate.tabs.length == 1) {
            scope.curtemplate.activeTab = 0;
          }
        };
        /**
         * Remove tabs from current tree branch
         */
        scope.removeTabsFromBranch = function() {
          var ans = confirm($filter('translate')('common.confirm_action'));
          if (ans) {
            scope.curtemplate.tabs.splice(0, scope.curtemplate.tabs.length);
          }
        };

        /**
         * Remove tabs from current tree branch
         */
        scope.moveBranchTabs = function() {
          scope.curtemplate.fieldsUnderTabs = !scope.curtemplate
            .fieldsUnderTabs;
        };

        var setDragAndDropBuilder = function() {
          // ✪ Dragula !
          if (typeof dndrop == 'undefined') {
            dndrop = dragula([document.getElementById('builderPickZone')], {
              //
              revertOnSpill: true,
              // copy of components so they won't disappear from the components list
              copy: true,
              // restrict drag : from components to builder only
              moves: function(el) {
                var isDraggable =
                  el.attributes.class.nodeValue.indexOf('draggable') !== -1;
                if (!isDraggable) {
                  return false;
                }
                return true;
              },
            }).on('drop', function(el, container) {
              // has to be dropped on a container
              if (container == null) {
                return false;
              }

              // is it dropped in a tab ?
              var droppedInATab =
                container.attributes['class'].nodeValue.indexOf('notinatab') ==
                -1;

              // Default template for a new element
              var elementType = el.attributes['data-type'].nodeValue,
                elementTemplate = {
                  label: $filter('translate')(
                    el.attributes['data-label'].nodeValue
                  ),
                  type: elementType,
                  description: '',
                  config: {
                    name: el.attributes['data-name'].nodeValue,
                  },
                };

              switch (elementType) {
                case 'attribute':
                  elementTemplate.title = '';
                  elementTemplate.placeholder = '';
                  elementTemplate.readonly = false;
                  elementTemplate.disabled = false;
                  elementTemplate.config.res = 'current';
                  elementTemplate.config.ftid =
                  el.attributes['data-ftid'].nodeValue;
                  elementTemplate.required =
                  el.attributes['data-mandatory'].nodeValue === 'true';
                  break;
                case 'select':
                  if(elementTemplate.config.name === 'selectsearchdata'){
                    elementTemplate.config.association = 'selection_cible';
                  }else{
                    elementTemplate.config.res = 'selection_cible';
                  }
                  break;
                case 'data':
                  // when dropping a featureattachment, the default value for feature is current
                  if (el.attributes['data-name'].nodeValue == 'dataattachement') {
                    elementTemplate.config.res = 'current';
                  }
                  break;
                case 'component':
                  if (el.attributes['data-name'].nodeValue == 'button') {
                    elementTemplate.cfg = {
                      icon: {
                        name: 'gear',
                      },
                      iconOrLabel: 'icon',
                      size: 'btn-xs',
                      style: 'btn-default',
                    };
                  }
                  break;
                case 'network':
                  if (elementTemplate.config.name === "PathNodetoNode") {
                    elementTemplate.config.result = 'selection_cible';
                  } else if (elementTemplate.config.name === "PathRouteFromNodeToNode") {
                    elementTemplate.config.update_selection = 'selection_cible';
                  }
                  break;
                case 'relation':
                  elementTemplate.config.res = 'current';
                  elementTemplate.config.ftid =
                    el.attributes['data-ftid'].nodeValue;
                  elementTemplate.config.idend =
                    el.attributes['data-idend'].nodeValue;
                  elementTemplate.config.ogcid =
                    el.attributes['data-ogcid'].nodeValue;
                  console.log(elementTemplate.config.idend);
                  break;
                case 'associations':
                  elementTemplate.config.ftiname =
                    el.attributes['data-ftiname'].nodeValue;
                  break;
              }

              var uniqueIdentifier =
                el.attributes['data-type'].nodeValue +
                '#' +
                el.attributes['data-name'].nodeValue;

              // for the attributes, we also need to take into account the ftid cause if we dont
              // some attributes will appear as droppped because they share the name of another feature attribute which was dropped
              if (elementType == 'attribute' || elementType == 'relation') {
                uniqueIdentifier =
                  el.attributes['data-ftid'].nodeValue + '#' + uniqueIdentifier;
              }
              if (!angular.isDefined(scope.droppedElements[uniqueIdentifier])) {
                scope.droppedElements[uniqueIdentifier] = [];
              }

              scope.droppedElements[uniqueIdentifier].push({
                branch: scope.currentBranch,
                tab: droppedInATab ? scope.curtemplate.activeTab : -1,
              });

              try {
                scope.$apply(function() {
                  var droppedIndex = Array.prototype.indexOf.call(
                    container.children,
                    el
                  );
                  if (droppedInATab) {
                    scope.curtemplate.tabs[
                      scope.curtemplate.activeTab
                    ].fields.splice(droppedIndex, 0, elementTemplate);
                  } else {
                    scope.curtemplate.fields.splice(
                      droppedIndex,
                      0,
                      elementTemplate
                    );
                  }
                });
              } catch(err) {}
              // Insert the elementTemplate at the right place (so the reordering works)

              // Remove the now useless dropped element
              el.remove();
            });
          }

          $timeout(function() {
            // Add the dropZones as they are added to the DOM
            Array.prototype.forEach.call(
              document.getElementsByClassName('builderDropZone'),
              function(el) {
                dndrop.containers.push(el);
              }
            );
          }, 0);
        };

        /*
                 -----
                 DragAndDrop Handler
                 */
        var dndrop;
        // this is used to track what is dropped and where
        scope.droppedElements = {};

        /*
         *  on any template/tab change, add the new drop containers if needed
         *  on first iteration, init dragula
         */
        scope.$watch(
          'curtemplate',
          function(n, o) {
            if (typeof n == 'undefined') return;

            if (n.tabs && n.tabs.length > 0) {
              if (isNaN(n.activeTab)) scope.curtemplate.activeTab = 0;
            }
            setDragAndDropBuilder();
          },
          true
        );

        /**
         * Clean the tree before returning the res object
         * Remove useless abntree attributes
         * @param branch object
         * @returns {*}
         */
        var cleanTree = function(branch) {
          branch.forEach(function(b, i) {
            // recursive cleaning of sub-branches
            if (
              branch[i].hasOwnProperty('children') &&
              branch[i].children.length > 0
            ) {
              branch[i].children = cleanTree(branch[i].children);
            }

            // force tab as first
            if (branch[i].template.activeTab) {
              branch[i].template.activeTab = 0;
            }
            delete branch[i]['style'];
            delete branch[i]['uid'];
            delete branch[i]['selected'];
            delete branch[i]['level'];
            delete branch[i]['parent_uid'];
            delete branch[i]['expanded'];
          });

          return branch;
        };

        scope.currentBranch = null;
        // When anything  is edited
        scope.$watch(
          'editTree',
          function(editTree) {
            // tree was removed
            if (typeof editTree == 'undefined') {
              return;
            }

            var getCurrentBranch = scope.feattree.get_selected_branch();
            if (getCurrentBranch != null)
              scope.currentBranch = getCurrentBranch;

            // the tree object has no root (the root is used only in editMode)
            scope.tree = editTree[0].children;
            // -- Update the res object
            // res object = tree object without all the abntree attributes (uid, expanded, style...)
            scope.templateform.data = cleanTree(angular.copy(scope.tree));
            scope.templateform.variables = scope.formvariables;
          },
          1
        );

        // Load a form if loadTemplate is passed
        if (scope.loadTemplate && Object.keys(scope.loadTemplate).length > 0) {
          scope.openForm(scope.loadTemplate);
        }

        /**
         * Intervention simple
         */
        scope.isTabs = [
          {
            title: 'Recherche',
          },
          {
            title: 'Liste',
          },
          {
            title: 'Édition',
          },
        ];
        scope.isTabs.activeTab = 0;

        scope.initLeftTabs = function (hideGeometricComponents) {
          scope.editionLeftTabs = [
            { title: 'Attributs', id: 'Attributs' },
            { title: 'Pièces jointes', id: 'PJ' }
          ];
          if (scope.templateform.formtype === 'intervention_simple') {
            scope.editionLeftTabs.splice(1, 0, { title: 'Objets cibles', id:'objects_cibles'});
          }
          if (hideGeometricComponents == false) {
            scope.editionLeftTabs.splice(1, 0, { title: 'Géométrie', id: 'Géométrie' });
          }
          scope.editionLeftTabs.activeTab = 0;
        };

        scope.resultatElastic = {};

        /**
         * Selection du composant de l'intervention simple
         */
        scope.isComponent = {};
        scope.pickedAttributes = {};
        scope.isComponentPicked = function(changedByUser) {
          if (changedByUser) delete scope.curtemplate.filterCfg;

          $timeout(function() {
            if(!scope.curtemplate.fti_info){
              scope.curtemplate.fti_info = {};
            }
            if(scope.curtemplate.fti_info.name !== scope.isComponent.x.name || !scope.curtemplate.fti_info.attributes){
              scope.pickedAttributes = {};
              scope.allAttributesAreChecked = false;
              scope.toggleCheckedAttributes();
            }
            scope.curtemplate.fti_info = {
              name: scope.isComponent.x.name,
              storeName: scope.isComponent.x.storeName,
              attributes: scope.curtemplate.fti_info.attributes,
              label: scope.curtemplate.fti_info.label?scope.curtemplate.fti_info.label:'Liste des objets',
            };

            if (changedByUser) {
              scope.curtemplate.filterCfg = {
                user: $rootScope.xgos.user.login,
                relations: [
                  {
                    selectGlobaladvancedFti: scope.isComponent.x.alias,
                    filters: [],
                  },
                ],
              };
            }
            setAssociationConfig();
          });
        };

        scope.$watch('curtemplate.fti_info.attributes', function(attributes) {
          if (angular.isArray(attributes)) {
            var tmp = {};
            attributes.forEach(function(attr) {
              tmp[attr] = true;
            });
            scope.pickedAttributes = tmp;
          }
        });

        scope.$watch(
          'pickedAttributes',
          function(attributes) {
            if(angular.isDefined(scope.curtemplate) && angular.isDefined(scope.curtemplate.fti_info)){
              var attz = [];
              for (var i in attributes) {
                if (attributes[i] == true) attz.push(i);
              }
              scope.curtemplate.fti_info.attributes = attz;
              scope.allAttributesAreChecked =
                scope.isComponent.x &&
                scope.isComponent.x.attributes.length > 0 &&
                scope.isComponent.x.attributes.length ==
                scope.curtemplate.fti_info.attributes.length;
            }
          },
          1
        );
        /**
         * Toggle check attributes
         */
        scope.toggleCheckedAttributes = function() {
          scope.allAttributesAreChecked = !scope.allAttributesAreChecked;
          if (scope.allAttributesAreChecked == true) {
            scope.isComponent.x.attributes.forEach(function(attr) {
              scope.pickedAttributes[attr.name] = true;
            });
          } else {
            scope.pickedAttributes = {};
          }
        };

        scope.testActions = [
          {
            label: 'btn',
            btnclass: 'default',
            cfg: {
              iconOrLabel: 'icon',
              label: '',
              size: 'btn-xs',
              style: 'btn-default',
              icon: {
                name: 'adjust',
              },
            },
            config: {
              click: ['alertMessage(\'test\')'],
            },
          },
          {
            label: 'btn',
            btnclass: 'default',
            cfg: {
              iconOrLabel: 'icon',
              label: '',
              size: 'btn-xs',
              style: 'btn-info',
              icon: {
                name: 'adjust',
              },
            },
            config: {
              click: ['alertMessage(\'test\')'],
            },
          },
          {
            label: 'btn',
            btnclass: 'default',
            cfg: {
              iconOrLabel: 'icon',
              label: '',
              size: 'btn-xs',
              style: 'btn-danger',
              icon: {
                name: 'adjust',
              },
            },
            config: {
              click: ['alertMessage(\'test\')'],
            },
          },
        ];

        var debugDialog;
        scope.debug_json = function() {
          scope.editConfig = {
            data: angular.copy(scope.templateform),
            options: {
              mode: 'tree',
            },
          };
          debugDialog = ngDialog.open({
            template:
              'js/XG/widgets/utilities/form/views/modal/modal.form_debug.html',
            className: 'ngdialog-theme-plain width1000 nopadding miniclose',
            closeByDocument: false,
            scope: scope,
          });
        };

        scope.applyDebuggedVersion = function() {
          if (
            confirm(
              $filter('translate')('common.sure_to_apply_modif')
            )
          ) {
            setFormData(angular.copy(scope.editConfig.data));
            checkIfFormWasModified();
            debugDialog.close();
          }
        };

        scope.jsonEditionChangeMode = function () {
          scope.editConfig.options.mode = (scope.editConfig.options.mode === 'code') ? 'tree' : 'code';
        }

        /**
         * reset taOptions of toolbar
         * @type {*[]}
         */
        taOptions.toolbar = [
          [
            'h1',
            'h2',
            'h3',
            'h4',
            'h5',
            'h6',
            'bold',
            'italics',
            'underline',
            'strikeThrough',
          ],
          [
            'ul',
            'ol',
            'insertLink',
            'insertImage',
            'html',
            'justifyLeft',
            'justifyCenter',
            'justifyRight',
            'indent',
            'outdent',
          ],
        ];



        /** INTERVENTION SIMPLE - GESTION DU BOUTON GENERAL "ENREGISTRER" **/

        /**
         * savefield contient les fonctions et le nom de la variable "Objet en relation"
         */
        if (!scope.savefield || !scope.savefield.config) {
          scope.savefield = {
            config: {}
          };
        }

        scope.$watch('savefield.config.mustHaveGeometry', function (newValue, oldValue) {
          checkIfValueHasChange(oldValue, newValue);
        });

        /**
         * Reçoit l'objet en relation du composant formBuilderVariables
         * à chaque modification de la variable res (picked)
         */
        scope.$on('globalSaveRelated', function (e, related) {
          checkIfValueHasChange(scope.savefield.config.related, related)
          scope.savefield.config.related = related;
          scope.$broadcast('resetVariable', 'tools.builder.fields.edit.save.association');
        });

        /**
         * Reçoit l'objet en relation du composant formBuilderVariables
         * à chaque modification de la variable res (picked)
         */
         scope.$on('globalSaveAssociation', function (e, association) {
          checkIfValueHasChange(scope.savefield.config.association, association)
          scope.savefield.config.association = association;
          scope.$broadcast('resetVariable', 'tools.builder.fields.edit.save.linked');
         });

        /**
         * Reçoit la liste de commandes beforeFinish
         * du composant formBuilderFunction
         * à chaque modification de la variable res (eventFunctions.lines)
         */
        scope.$on('globalSaveBeforeFinish', function (e, beforefinish) {
          checkIfValueHasChange(scope.savefield.config.beforefinish, beforefinish);
          scope.savefield.config.beforefinish = beforefinish;
        });

        /**
         * Reçoit la liste de commandes finish
         * du composant formBuilderFunction
         * à chaque modification de la variable res (eventFunctions.lines)
         */
        scope.$on('globalSaveFinish', function (e, finish) {
          checkIfValueHasChange(scope.savefield.config.finish, finish)
          scope.savefield.config.finish = finish;
        });

        /**
         * Gère la détection de modifications portées sur le bouton
         * pour l'affichage du warning "Modifications non enreg."
         * @param oldValue ancienne valeur de related/bfinish/finish
         * @param newValue nouvelle valeur de related/bfinish/finish
         */
        function checkIfValueHasChange(oldValue, newValue) {
          scope.formIsDirty = !scope.formIsDirty ? oldValue !== newValue
            : scope.formIsDirty;
        }

        /**
         * Ajoute les informations du bouton "Enregistrer"
         * dans le contenu du formulaire
         * avant enregistrement du formulaire
         *
         * @see scope.saveForm
         */
        function defineSaveField() {
          if (scope.templateform.data.fti_info
              && scope.templateform.data.fti_info.name) {

            const ftiname = scope.templateform.data.fti_info.name;
            const ftid = FeatureTypeFactory.getFeatureUidByName(ftiname);

            // structure identique au bouton "Enregistrement d'objet"
            scope.templateform.data.savefield = {
              config: {
                mustHaveGeometry: false,
                beforefinish: undefined,
                feats: 'current',
                finish: undefined,
                ftid: ftid,
                ftiname: ftiname,
                name: 'globaladd',
                related: scope.savefield.config.related,
                association: scope.savefield.config.association,
                res: 'current'
              },
              description: '',
              label: '',
              type: 'edit'
            };

            if (scope.savefield && Object.entries(scope.savefield.config).length > 0) {
                scope.templateform.data.savefield.config.mustHaveGeometry = scope.savefield.config.mustHaveGeometry;
                scope.templateform.data.savefield.config.autoCreateGeometry = scope.savefield.config.autoCreateGeometry;
                scope.templateform.data.savefield.config.beforefinish = scope.savefield.config.beforefinish;
                scope.templateform.data.savefield.config.finish = scope.savefield.config.finish;
                scope.templateform.data.savefield.config.related = scope.savefield.config.related;
            }
          }
        }

        /**
         * Affecte les informations du bouton "Enregistrer" à la variable
         * model de l'affichage des informations dans le popover
         * (scope.savefield): alimente la variable res des composants
         * form-builder-variables et form-builder-function
         * du bouton global "Enregistrer" à l'initialisation
         *
         * @param templateform formulaire, retour API de configFactory.get
         * @see scope.openForm
         */
        function setSaveFieldIS(templateform){
          if (templateform && templateform.data && templateform.data.savefield){
            scope.savefield = templateform.data.savefield;
          }
        }

        /**
         * Supprime la propriété form.filterCfg.relations[i].filters[i].selectedfti.isactivepoint
         * Cette propriété pose problème pour la comparaison des form permettant de détecter les modifications
         * portées sur le formulaire: baseFormData contient cette propriété contrairement à cmp
         * @param form formulaire au format json
         * @param propertyName nom de la propriété à supprimer ('isactivepoint' ou 'value')
         * @see scope.checkIfFormWasModified
         */
        function removePropertyInRelationsForm(form, propertyName){
          if (form && form.filterCfg && form.filterCfg.relations && form.filterCfg.relations.length > 0){
            form.filterCfg.relations.forEach(relation => {
              if (relation.filters && relation.filters.length > 0){
                for (const filter of relation.filters){
                  if (propertyName === 'value'){
                    if (filter && filter[propertyName] !== undefined){
                      // si le boolean est défini on le supprime
                      delete filter[propertyName];
                    }
                  }else if (propertyName === 'isactivepoint'){
                    if (filter.selectedfti && filter.selectedfti[propertyName] !== undefined){
                      // si le boolean est défini on le supprime
                      delete filter.selectedfti[propertyName];
                    }
                  }
                }
              }
            });
          }
        }
        let setAssociationConfig = () => {
          if($rootScope.xgos.portal && $rootScope.xgos.portal.parameters &&
            $rootScope.xgos.portal.parameters.mainDB){
              AssociationFactory.getAll($rootScope.xgos.portal.parameters.mainDB).then((res) => {
                let availableAss = res.data.filter(ass=>  ass.type_patrimoine && (
                                              ass.atable===scope.templateform.data.fti_info.name || 
                                              ass.btable===scope.templateform.data.fti_info.name));
                availableAss = availableAss.map(ass =>  Object({"name" : ass.name, "alias":ass.alias, "ID":ass.ID}))
                if(!scope.templateform.data.association){
                  scope.templateform.data.association = {};
                  //set config button default
                  scope.templateform.data.association.associationResultat = 'objets_cible'
                }
                if(!scope.templateform.data.association.associationToLoad){
                  scope.templateform.data.association.associationToLoad = [];
                }else{
                  scope.templateform.data.association.associationToLoad = scope.templateform.data.association.associationToLoad.map(ass =>  Object({"name" : ass.name, "alias":ass.alias, "ID":ass.ID}))
                }
                if(!scope.templateform.data.association.associationLabel){
                  scope.templateform.data.association.associationLabel = 'Liste des objets cibles de l\'intervention';
                }
                scope.associationToLoad = {
                  leftData: scope.templateform.data.association.associationToLoad,
                  leftDisplayAttribute: 'alias',
                  rightData: angular.copy(availableAss),
                  rightDisplayAttribute: 'alias',
                  leftTitle: 'tools.builder.association.associationLoaded',
                  rightTitle: 'tools.builder.association.associationAvailable',
                  source: 'right'
                };
              });
          }
        };

        /**
         * A l'ouverture d'un formulaire IS ou bien après changement du type de formulaire vers IS,
         * créé les variables de la popover de configuration de l'IS.
         * Si l'IS courante ne possède pas de labels customisés alors on charge les labels pré-définis.
         */
        const setIsCfgTabs = () => {
          const labelPrefix = 'tools.builder.intervention_simple.cfgPopover.';
          scope.isCfgTabs = {
            tabs: [
              {title: labelPrefix + 'search.title', html: '<i class="fa fa-search"></i>', id:0},
              {title: labelPrefix + 'list.title', html: '<i class="fa fa-list"></i>', id:1},
              {title: labelPrefix + 'edition.title', html: '<i class="fa fa-pencil-square-o"></i>', id:2}
            ],
            activeTab: 0
          };
        };

        /**
         * Positionne la popover d'un bouton 'bs-popover' au-dessous de celui-ci.<br>
         * Modifie la propriété CSS 'top' dans le style inline de la popover
         */
        scope.adjustPopoverPosition = () => {
          isUtils.adjustPopoverPosition();
        };

        /**
         * Créé la configuration des tabul
         */
        scope.$watch('templateform.formtype', (newVal) => {
          if (ISANDSIMILARFORMTYPES.includes(newVal)) {
            if (!scope.isCfgTabs) {
              setIsCfgTabs();
            }
            if (newVal === 'table_simple' || newVal === 'recherche_donnees') {
              scope.isCfgTabs.tabs = scope.isCfgTabs.tabs.filter(tab => tab.id !== 2);
            }
          }
        });
      },
    };
  };

  gcelement.$inject = [
    'ngDialog',
    '$filter',
    '$timeout',
    'ConfigFactory',
    'hotkeys',
    '$rootScope',
    'FeatureTypeFactory',
    'taOptions',
    'gaJsUtils',
    'AssociationFactory',
    '$window',
    'isUtils'
  ];
  return gcelement;
});
