/*global define, X2JS */
define(function() {
  'use strict';

  /**
   * Sld Utils
   */
  var sldUtils = function() {
    let geoserver_rest_url = '';

    /**
     * Pour les styles d’objets ponctuels ayant une symbologie “image” ou “police”,
     * quand on supprime une ou les deux valeurs de “déplacement horizontal/vertical”,
     * les valeurs passent à “null” et les objets ne s’affichent plus car style HS.<br>
     * <a href="https://altereo-informatique.atlassian.net/browse/KIS-3231">KIS-3231|[SLD] Si le déplacementX/Y == "null" alors le style est HS</a>
     * @param object sld au format json
     * @param propertyNames nom des propriétés à supprimer si "null"
     */
    const deleteNullProperties = (object, propertyNames) => {
      const deleteNullProperty = (object, propertyName, parentObject, parentKey) => {
        if (object.hasOwnProperty(propertyName) && object[propertyName] === "null") {
          delete object[propertyName];
          if (Object.keys(object).length === 0 && parentObject && parentKey) {
            delete parentObject[parentKey];
          }
        } else if (typeof object === 'object') {
          for (const key of Object.keys(object)) {
            if (typeof object[key] === 'object') {
              deleteNullProperty(object[key], propertyName, object, key);
            }
          }
        }
      }
      for (const propertyName of propertyNames) {
        deleteNullProperty(object, propertyName);
      }
    };

    this.$get = function($http, $q, gaJsUtils, StyleFactory, $filter, attributeRestrictionsUtils, QueryFactory) {
      // Recursive filter function for SLD tags
      // Returns true to keep the tag, false to delete it
      function filterTags(tag, name, parent) {
        // Empty attributes and internal objects
        if (
          tag === null ||
          tag === undefined ||
          (name[0] === '_' && tag === '') ||
          name === 'kse'
        ) {
          return false;
        }
        if (typeof tag !== 'object') {
          return true;
        }
        if (Array.isArray(tag)) {
          parent[name] = tag.filter(filterTags);
          // Don't remove the Rule array!
          return (
            String(name).toLowerCase() === 'rule' || parent[name].length > 0
          );
        }
        var childs = Object.keys(tag).filter(function(key) {
          return tag.hasOwnProperty(key) && typeof tag[key] !== 'function';
        });
        childs.forEach(function(child) {
          if (!filterTags(tag[child], child, tag)) {
            delete tag[child];
          }
        });
        if (String(name).toLowerCase() === 'cssparameter') {
          // Remove CssParameters with no __text
          // __text may contain "0", which evaluates to false, but we must keep it
          return (
            tag._name &&
            tag.__text !== undefined &&
            tag.__text !== null &&
            tag.__text !== ''
          );
        }
        return true;
      }

      /*
       * Our own instance of X2JS.
       */
      var x2js = new X2JS();

      function cleanSLD(json) {

        // KIS-3231: si le déplacement vertical et/ou déplacement horizontal contient valeur "null" alors le style est HS
        deleteNullProperties(json, ['DisplacementX', 'DisplacementY']);

        var parent = { prop: 'json', json: json };
        filterTags(parent.json, 'json', parent);
      }

      /**
       * setGeoserverRestUrl
       * @returns {Deferred}
       */
      var setGeoserverRestUrl = () => {
        var def = $q.defer();
        if (geoserver_rest_url !== '') {
          def.resolve();
        } else {
          StyleFactory.getgeoserverresturl().then(function(res) {
            geoserver_rest_url = res.data;
            def.resolve();
          });
        }
        return def.promise;
      };

      /**
       * Recherche la règle d'un style catégorisé ayant pour valeur/Titre "Représentation par défaut", la règle par défaut.
       * Habituellement, il s'agit de la 1ère règle dans le tableau scope.rules.
       * Cependant, pour ne pas se baser sur le rang, on recherche la règle par défaut dans tout le tableau de règles en se basant sur le Titre de règle.
       * @param {object[]} rules tableau de règles de la directive
       * @return {null|object} règle de représentation par défaut
       */
      const findCategorizedStyleDefaultRule = (rules) => {
        if (Array.isArray(rules) && rules.length > 0) {

          // recherche la règle dont le titre est "Représentation par défaut"
          return rules.find(rule => {
            if (rule.hasOwnProperty('Title')) {
              let title;
              if (Array.isArray(rule.Title) && rule.Title.length > 0) {
                title = rule.Title[0];
              } else if (typeof rule.Title === 'string'){
                title = rule.Title;
              } else {
                return false;
              }
              return title === $filter('translate')('model.styles.editor.category.default_value');
            } else {
              return false;
            }
          });
        }
        return null;
      };

      return {
        x2js: x2js,
        cleanSLD: cleanSLD,

        /**
         * get list of styles currently associated to the layer in geoserver
         * @param name
         * @returns {*}
         */
        getLayerStyles: function(name) {
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE

          return $http
            .get('/geoserver/rest/layers/' + name + '/defaultstyle.json')
            .then(function(res) {
              var styles = [];
              // Only if we get a layer attribute
              if (res.data.layer) {
                // Default style
                styles.push({
                  name: res.data.layer.defaultStyle.name,
                  label:
                    '<span class="label label-default"><translate>Default</translate></span> ' +
                    res.data.layer.defaultStyle.name,
                  default: true,
                });

                // If others styles are associated with that layer
                if (res.data.layer.styles && res.data.layer.styles.style) {
                  res.data.layer.styles.style.forEach(function(s) {
                    // if the layer isn't already loaded as default...
                    // ------------------------------------------------
                    // GeoServer allows to set a style as default and also as extra
                    // Without this check the layer may be loaded twice
                    if (res.data.layer.defaultStyle.name !== s.name) {
                      styles.push({
                        name: s.name,
                        label: s.name,
                        default: false,
                      });
                    }
                  });
                }
              }

              return { styles: styles };
            });
        },

        /**
         * Load SLD
         * @param name
         */
        loadSLD: function(name) {
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE

          return $http.get('/geoserver/rest/styles/' + name + '.sld');
        },

        /**
         * Get all the styles available in geoserver
         */
        getAllStyles: function() {
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE

          return $http.get('/geoserver/rest/styles.json');
        },

        /**
         * Transform the typeinfo to a SymbolizerName
         * LINE -> LineSymbolizer, etc..
         * @param type
         * @returns {string}
         */
        getSymbolizerName: function(type) {
          return (
            gaJsUtils.capitalizeFirstLetter(type.toLowerCase()) + 'Symbolizer'
          );
        },

        /**
         *
         */
        getAvailableFonts: function() {
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE
          // /!\ NOT USED ANYMORE AS WE DONT USE GEOSERVER TO GET THE STYLES ANYMORE

          return $http.get('/geoserver/rest/fonts.json');
        },

        /**
         * Add a component to the current Symbolizer
         * @param type
         * @param wellKnowName
         */
        getSimpleSymbolizerForType: function(type, wellKnowName) {
          //                    console.log(type);

          wellKnowName = wellKnowName || false;

          const defaultLine = {
            LineSymbolizer: [
              x2js.xml_str2json(
                '<Stroke>' +
                  '<CssParameter name="stroke">#000000</CssParameter>' +
                  '<CssParameter name="stroke-width">1</CssParameter>' +
                  '<CssParameter name="stroke-linejoin">bevel</CssParameter>' +
                  '<CssParameter name="stroke-linecap">square</CssParameter>' +
                  '</Stroke>'
              ),
            ],
          };

          let defaultPoint = {
            PointSymbolizer: [
              x2js.xml_str2json(
                '<Graphic>' +
                  '<Mark>' +
                  '<WellKnownName> circle </WellKnownName>' +
                  '<Fill><CssParameter name="fill">#000000</CssParameter></Fill>' +
                  '</Mark>' +
                  '<Size>15</Size>' +
                  '<Rotation>0</Rotation>' +
                  '</Graphic>'
              ),
            ],
          };
          if (wellKnowName === 'shape://oarrow' || wellKnowName === 'shape://carrow') {
            defaultPoint = {
              PointSymbolizer: [
                x2js.xml_str2json(
                  '<Graphic>' +
                    '<Mark>' +
                    '<WellKnownName> circle </WellKnownName>' +
                    '<Fill><CssParameter name="fill">#ffffff</CssParameter></Fill>' +
                    '<Stroke>' +
                    '<CssParameter name="stroke">#000000</CssParameter>' +
                    '<CssParameter name="stroke-width">1</CssParameter>' +
                    '</Stroke>' +
                    '</Mark>' +
                    '<Size>25</Size>' +
                    '<Rotation>0</Rotation>' +
                    '</Graphic>'
                ),
              ],
            };
          }
          const defaultPolygon = {
            PolygonSymbolizer: [
              x2js.xml_str2json(
                '<Fill>' +
                  '<CssParameter name="fill">#f00000</CssParameter>' +
                  '</Fill>'
              ),
            ],
          };
          let component = {};
          switch (type) {
            case 'LINE':
              component = defaultLine;
              break;
            case 'POINT':
              component = defaultPoint;
              break;
            case 'POLYGON':
              component = defaultPolygon;
              break;
          }

          return component;

          /*scope.symbolizer.data.push({
                     label: 'Symbol '+(scope.symbolizer.data.length+1),
                     sld : component,
                     type : Object.keys(component)[0]
                     });*/
        },

        /**
         * Add a symbolizer to a rule
         * @param rule
         * @param symbolizer
         */
        addSymbolizerToRule: function(rule, symbolizer) {
          const symbolizerType = Object.keys(symbolizer)[0];
          if (rule && rule.hasOwnProperty(symbolizerType)) {
            console.log(rule[symbolizerType]);
            if (!angular.isArray(rule[symbolizerType])) {
              rule[symbolizerType] = [rule[symbolizerType]];
            }
            rule[symbolizerType].push(symbolizer[symbolizerType]);
          } else if (rule) {
            rule[symbolizerType] = symbolizer[symbolizerType];
          }
        },

        /**
         * Remove a symbolizer from a rule
         * @param rule
         * @param type
         * @param index
         * @returns {boolean}
         */
        removeSymbolizerFromRule: function(rule, type, index) {
          if (angular.isArray(rule[type]) && rule[type].length > 1) {
            rule[type].splice(index, 1);
          } else {
            delete rule[type];
          }
          return Object.keys(rule).length === 0;
        },

        /**
         * Set a new rule when the symbol type is changed
         * @param type
         * @returns {*}
         */
        switchRuleSymbolType: function(type) {
          var self = this;

          return setGeoserverRestUrl().then(function() {
            const kisBaseImgFolder = geoserver_rest_url + '/styles/kis/common/images/base';
            let newRule = {};
            switch (type) {
              /* POINTS */
              case 'point-simple':
                newRule = self.getSimpleSymbolizerForType('POINT');
                break;
              case 'point-image':
                newRule = {
                  PointSymbolizer: [
                    x2js.xml_str2json(
                      '<Graphic>' +
                        '<ExternalGraphic>' +
                        '<OnlineResource type="simple" href="' +
                        kisBaseImgFolder +
                        '/burg02.svg"></OnlineResource>' +
                        '<Format>image/svg+xml</Format>' +
                        '</ExternalGraphic>' +
                        '<Size>30</Size>' +
                        '<Rotation>0</Rotation>' +
                        '</Graphic>'
                    ),
                  ],
                };
                break;
              case 'point-font':
                newRule = {
                  PointSymbolizer: [
                    x2js.xml_str2json(
                      '<Graphic>' +
                        '<Mark>' +
                        '<WellKnownName>ttf://Webdings#0x0064</WellKnownName>' +
                        '<Stroke>' +
                        '<CssParameter name="stroke">#000000</CssParameter>' +
                        '</Stroke>' +
                        '<Fill>' +
                        '<CssParameter name="fill">#d50000</CssParameter>' +
                        '</Fill>' +
                        '</Mark>' +
                        '<Size>20</Size>' +
                        '<Rotation>0</Rotation>' +
                        '</Graphic>'
                    ),
                  ],
                };
                break;
              /* LINES */
              case 'line-simple':
                newRule = self.getSimpleSymbolizerForType('LINE');
                break;
              case 'line-symbols':
                newRule = {
                  LineSymbolizer: [
                    x2js.xml_str2json(
                      '<Stroke>' +
                        '<GraphicStroke>' +
                        '<Graphic>' +
                        '<Mark>' +
                        '<WellKnownName>circle</WellKnownName>' +
                        '<Fill>' +
                        '<CssParameter name="fill">#000000</CssParameter>' +
                        '</Fill>' +
                        '<Stroke>' +
                        '<CssParameter name="stroke">#ff0000</CssParameter>' +
                        '<CssParameter name="stroke-width">0</CssParameter>' +
                        '</Stroke>' +
                        '</Mark>' +
                        '<Size>5</Size>' +
                        '</Graphic>' +
                        '</GraphicStroke>' +
                        '<CssParameter name="stroke-dasharray">4</CssParameter>' +
                        '</Stroke>'
                    ),
                  ],
                };

                break;
              /* POLYGONS */
              case 'polygon-simple':
                newRule = self.getSimpleSymbolizerForType('POLYGON');
                break;

              case 'polygon-lines':
                newRule = {
                  PolygonSymbolizer: [
                    x2js.xml_str2json(
                      '<Fill>' +
                        '<GraphicFill>' +
                        '<Graphic>' +
                        '<Mark>' +
                        '<WellKnownName>shape://times</WellKnownName>' +
                        '<Stroke>' +
                        '<CssParameter name="stroke">#000000</CssParameter>' +
                        '<CssParameter name="stroke-width">1</CssParameter>' +
                        '</Stroke>' +
                        '</Mark>' +
                        '<Size>15</Size>' +
                        '<Rotation>' +
                        '<Filter>' +
                        '<Literal>45</Literal>' +
                        '</Filter>' +
                        '</Rotation>' +
                        '</Graphic>' +
                        '</GraphicFill>' +
                        '</Fill>'
                    ),
                  ],
                };
                break;

              case 'polygon-symbols':
                newRule = {
                  PolygonSymbolizer: [
                    x2js.xml_str2json(
                      '<Fill>' +
                        '<GraphicFill>' +
                        '<Graphic>' +
                        '<Mark>' +
                        '<WellKnownName>circle</WellKnownName>' +
                        '<Fill>' +
                        '<CssParameter name="fill">#ff0000</CssParameter>' +
                        '</Fill>' +
                        '<Stroke>' +
                        '<CssParameter name="stroke">#000000</CssParameter>' +
                        '</Stroke>' +
                        '</Mark>' +
                        '<Size>5</Size>' +
                        '</Graphic>' +
                        '</GraphicFill>' +
                        '</Fill>'
                    ),
                  ],
                };
                break;
              case 'polygon-images':
                newRule = {
                  PolygonSymbolizer: [
                    x2js.xml_str2json(
                      '<Fill>' +
                        '<GraphicFill>' +
                        '<Graphic>' +
                        '<ExternalGraphic>' +
                        '<OnlineResource type="simple" href="' +
                        kisBaseImgFolder +
                        '/burg02.svg"></OnlineResource>' +
                        '<Format>image/svg+xml</Format>' +
                        '</ExternalGraphic>' +
                        '<Size>5</Size>' +
                        '<CssParameter name="stroke">#33a02c</CssParameter>' +
                        '<CssParameter name="stroke-width">5</CssParameter>' +
                        '<Rotation>0</Rotation>' +
                        '</Graphic>' +
                        '</GraphicFill>' +
                        '</Fill>'
                    ),
                  ],
                };
                break;

              case 'polygon-border-symbols':
                newRule = {
                  PolygonSymbolizer: [
                    x2js.xml_str2json(
                      '<Stroke>' +
                        '<GraphicStroke>' +
                        '<Graphic>' +
                        '<Mark>' +
                        '<WellKnownName>circle</WellKnownName>' +
                        '<Fill>' +
                        '<CssParameter name="fill">#ff0000</CssParameter>' +
                        '</Fill>' +
                        '<Stroke>' +
                        '<CssParameter name="stroke">#000000</CssParameter>' +
                        '</Stroke>' +
                        '</Mark>' +
                        '<Size>2</Size>' +
                        '</Graphic>' +
                        '</GraphicStroke>' +
                        '</Stroke>'
                    ),
                  ],
                };
                break;
            }

            return newRule;
          });
        },

        /**
         * Try to check what is the symbol type of component
         * @param type (POINT/LINE/POLYGON)
         * @param component
         * @returns {*}
         */
        getSubTypeOfSymbolizer: function(type, component) {
          var symbolType;

          let check;
          let externalGraphic;
          switch (type) {
            case 'LineSymbolizer':
            case 'LINE':
              check = component.LineSymbolizer ? component.LineSymbolizer[0] : component;
              const graphicStroke = gaJsUtils.checkNestedProperty('Stroke.GraphicStroke', check);

              symbolType = 'line-simple';
              if (graphicStroke) {
                symbolType = 'line-symbols';
              }
              break;
            case 'PointSymbolizer':
            case 'POINT':
              check = component.PointSymbolizer ? component.PointSymbolizer[0] : component;
              const wellKnowname = gaJsUtils.checkNestedProperty('Graphic.Mark.WellKnownName', check);
              externalGraphic = gaJsUtils.checkNestedProperty('Graphic.ExternalGraphic', check);

              symbolType = 'point-simple';

              if (wellKnowname && wellKnowname.indexOf('ttf') !== -1) {
                symbolType = 'point-font';
              }
              if (externalGraphic) {
                symbolType = 'point-image';
              }
              break;
            case 'PolygonSymbolizer':
            case 'POLYGON':
              check = component.PolygonSymbolizer ? component.PolygonSymbolizer[0] : component;
              const wellKnownameFilling = gaJsUtils.checkNestedProperty('Fill.GraphicFill.Graphic.Mark.WellKnownName', check);
              const wellKnownameBorder = gaJsUtils.checkNestedProperty('Stroke.GraphicStroke.Graphic.Mark.WellKnownName', check);
              externalGraphic = gaJsUtils.checkNestedProperty('Fill.GraphicFill.Graphic.ExternalGraphic', check);

              symbolType = 'polygon-simple';

              if (wellKnownameFilling) {
                symbolType = 'polygon-symbols';
                if (wellKnownameFilling.indexOf('shape://') !== -1) {
                  symbolType = 'polygon-lines';
                }
              }
              if (wellKnownameBorder) {
                symbolType = 'polygon-border-symbols';
                if (wellKnownameBorder.indexOf('shape://') !== -1) {
                  symbolType = 'polygon-border-lines';
                }
              }

              if (externalGraphic) {
                symbolType = 'polygon-images';
              }
              break;
          }

          return symbolType;
        },

        /**
         * Get a simple point sld with a specific wellKnowName
         * @param wellKnowName
         * @returns {*}
         */
        getSimplePointSymbol: function(wellKnowName) {
          const sld = this.getSimpleSymbolizerForType('POINT', wellKnowName);
          sld.PointSymbolizer[0].Graphic.Mark.WellKnownName = wellKnowName;
          return sld;
        },

        /**
         * Ajoute un critère qui accepte une valeur vide pour l'attribut de classification
         * dans la règle par défaut d'un style catégorisé (règle ayant pour valeur "Représentattion par défaut").<br>
         * Cela permet à un objet dont l'attribut est une string vide d'avoir la représentation par défaut au lieu d'être invisible.<br>
         * Le tableau de règles de la directive est ainsi modifié : <ul><li>
         * à l'initialisation d'un style catégorisé </li><li>
         * à la ré-initialisation du style </li><li>
         * au changement d'attribut de base de la classification</li></ul>
         *
         * @param {object[]} rules tableau de règles de la directive styleeditor
         * @param {string} attribute nom de l'attribut sur lequel est basé la classification/catégorisation
         */
        addCategorizedDefaultRuleEmptyFilter: (rules, attribute) => {
          if (Array.isArray(rules) && rules.length > 0) {

            // recherche la règle de représentation par défaut du style catégorisé
            const defaultRule = findCategorizedStyleDefaultRule(rules);

            if (defaultRule && defaultRule.hasOwnProperty('Filter')) {
              let logicalOperatorBody;

              // objet à ajouter
              const emptyValueFilter = {PropertyName: attribute, literal: ''};

              // ajoute l'objet si le filtre n'a pas qu'un seul critère ou si ce filtre n'a pas d'opérateur logique
              if (Object.keys(defaultRule.Filter).length !== 1
                  || (!defaultRule.Filter.hasOwnProperty('Or') && !defaultRule.Filter.hasOwnProperty('OR'))) {

                if (defaultRule.Filter.hasOwnProperty('PropertyIsNull')) {

                  // récupère le critère "PropertyIsNull" existant du filtre et créé le critère "PropertyIsEqualTo"
                  logicalOperatorBody = {PropertyIsNull: defaultRule.Filter.PropertyIsNull, PropertyIsEqualTo: emptyValueFilter};
                } else {

                  // créé les 2 critères "PropertyIsNull" et "PropertyIsEqualTo"
                  logicalOperatorBody = {PropertyIsNull: {PropertyName: attribute}, PropertyIsEqualTo: emptyValueFilter};
                }
                // englobe les critères dans un objet ayant pour clé l'opérateur logique "Or"
                // (structure requise par Geoserver lorsqu'un filtre possède plusieurs critères)
                defaultRule.Filter = {Or: logicalOperatorBody};
              }
            }
          }
        },
        /**
         * Dans le cas d'une règle SLD d'un style catégorisé basé sur un attribut ayant une restriction DOMAIN/TABLE,
         * A partir de la propriété "value" de la règle SLD qui affiche la clé de la restriction
         * on récupère la valeur de la restriction pour alimenter la propriété "label" de la règle SLD
         * @param {object} fti feature type info
         * @param {string} attributeName nom de l'attribut sur qui est catégorisé dans le style SLD
         * @param {string} ruleValue propriété "value" de la règle SLD qui correspond à la clé d'une restriction
         */
        getSldCategorizedRuleLabelByValue: (fti, attributeName, ruleValue) => {
          const defer = $q.defer();
          const isDomainRestriction = attributeRestrictionsUtils.attributeHasRestrictionType(fti, attributeName, attributeRestrictionsUtils.DOMAIN);
          if (isDomainRestriction) {
            const attributeRestricted = fti.attributes.find(attr => attr.name === attributeName);
            if (Object.keys(attributeRestricted.restrictions[0].listofValues).includes(ruleValue)) {
              // la clé de la base fait partie du domaine de restriction alors on renvoie la valeur de la restriction
              defer.resolve(attributeRestricted.restrictions[0].listofValues[ruleValue]);
            } else {
              // si la clé de la base ne fait pas partie du domaine de restriction alors on renvoie la clé de la restriction
              defer.resolve(ruleValue);
            }
          } else {
            QueryFactory.getValueRestrictionTable(fti.uid, attributeName, ruleValue).then(
                res => {
                  if (res.data && typeof res.data === 'string') {
                    defer.resolve(res.data);
                  } else if (res.data.hasOwnProperty('value') && typeof res.data.value === 'string') {
                    defer.resolve(res.data.value);
                  }
                },
                () => {
                  defer.resolve(ruleValue);
                }
            )
          }
          return defer.promise;
        }
      };
    };
    this.$get.$inject = ['$http', '$q', 'gaJsUtils', 'StyleFactory', '$filter', 'attributeRestrictionsUtils', 'QueryFactory'];
  };
  return sldUtils;
});
