text: import { uiModules } from 'ui/modules';
  import _ from 'lodash';
  import MarkdownIt from 'markdown-it';
  import { modifyUrl } from 'ui/url';
  
  const markdownIt = new MarkdownIt({
    html: false,
    linkify: true
  });
  
  uiModules.get('kibana')
    .service('serviceSettings', function ($http, $sanitize, mapConfig, tilemapsConfig, kbnVersion) {
  
  
      const attributionFromConfig = $sanitize(markdownIt.render(tilemapsConfig.deprecated.config.options.attribution || ''));
      const tmsOptionsFromConfig = _.assign({}, tilemapsConfig.deprecated.config.options, { attribution: attributionFromConfig });
  
      const extendUrl = (url, props) => (
        modifyUrl(url, parsed => _.merge(parsed, props))
      );
  
      /**
       *  Unescape a url template that was escaped by encodeURI() so leaflet
       *  will be able to correctly locate the varables in the template
       *  @param  {String} url
       *  @return {String}
       */
      const unescapeTemplateVars = url => {
        const ENCODED_TEMPLATE_VARS_RE = /%7B(\w+?)%7D/g;
        return url.replace(ENCODED_TEMPLATE_VARS_RE, (total, varName) => `{${varName}}`);
      };
  
  
  
      class ServiceSettings {
  
        constructor() {
          this._queryParams = {
            my_app_version: kbnVersion
          };
  
          this._loadCatalogue = null;
          this._loadFileLayers = null;
          this._loadTMSServices = null;
  
          this._invalidateSettings();
        }
        _invalidateSettings() {
  
          this._loadCatalogue = _.once(async() => {
            try {
              const response = await this._getManifest(mapConfig.manifestServiceUrl, this._queryParams);
              return response.data;
            } catch (e) {
              if (!e) {
                e = new Error('Unkown error');
              }
              if (!(e instanceof Error)) {
                e = new Error(e.data || `status ${e.statusText || e.status}`);
              }
              throw new Error(`Could not retrieve manifest from the tile service: ${e.message}`);
            }
          });
  
  
          this._loadFileLayers = _.once(async() => {
            const catalogue = await this._loadCatalogue();
            const fileService = catalogue.services.filter((service) => service.type === 'file')[0];
            const manifest = await this._getManifest(fileService.manifest, this._queryParams);
            const layers = manifest.data.layers.filter(layer => layer.format === 'geojson');
            layers.forEach((layer) => {
              layer.url = this._extendUrlWithParams(layer.url);
              layer.attribution = $sanitize(markdownIt.render(layer.attribution));
            });
            return layers;
          });
  
          this._loadTMSServices = _.once(async() => {
  
            if (tilemapsConfig.deprecated.isOverridden) {//use settings from yml (which are overridden)
              const tmsService = _.cloneDeep(tmsOptionsFromConfig);
              tmsService.url = tilemapsConfig.deprecated.config.url;
              return tmsService;
            }
  
            const catalogue = await this._loadCatalogue();
            const tmsService = catalogue.services.filter((service) => service.type === 'tms')[0];
            const manifest = await this._getManifest(tmsService.manifest, this._queryParams);
            const services = manifest.data.services;
  
            const firstService = _.cloneDeep(services[0]);
            if (!firstService) {
              throw new Error('Manifest response does not include sufficient service data.');
            }
  
  
            firstService.attribution = $sanitize(markdownIt.render(firstService.attribution));
            firstService.subdomains = firstService.subdomains || [];
            firstService.url = this._extendUrlWithParams(firstService.url);
            return firstService;
          });
  
        }
  
        _extendUrlWithParams(url) {
          return unescapeTemplateVars(extendUrl(url, {
            query: this._queryParams
          }));
        }
  
        async _getManifest(manifestUrl) {
          return $http({
            url: extendUrl(manifestUrl, { query: this._queryParams }),
            method: 'GET'
          });
        }
  
  
        async getFileLayers() {
          return await this._loadFileLayers();
        }
  
        async getTMSService() {
  
          const tmsService = await this._loadTMSServices();
  
          return {
            getUrl: function () {
              return tmsService.url;
            },
            getMinMaxZoom: (isWMSEnabled) => {
              if (isWMSEnabled) {
                return {
                  minZoom: 0,
                  maxZoom: 18
                };
              }
              //Otherwise, we use the settings from the yml.
              //note that it is no longer possible to only override the zoom-settings, since all options are read from the manifest
              //by default.
              //For a custom configuration, users will need to override tilemap.url as well.
              return {
                minZoom: tmsService.minZoom,
                maxZoom: tmsService.maxZoom
              };
            },
            getTMSOptions: function () {
              return tmsService;
            }
          };
        }
  
        getFallbackZoomSettings(isWMSEnabled) {
          return (isWMSEnabled) ? { minZoom: 0, maxZoom: 18 } : { minZoom: 0, maxZoom: 10 };
        }
  
        /**
         * Add optional query-parameters to all requests
         *
         * @param additionalQueryParams
         */
        addQueryParams(additionalQueryParams) {
          for (const key in additionalQueryParams) {
            if (additionalQueryParams.hasOwnProperty(key)) {
              if (additionalQueryParams[key] !== this._queryParams[key]) {
                //changes detected.
                this._queryParams = _.assign({}, this._queryParams, additionalQueryParams);
                this._invalidateSettings();
                break;
              }
            }
          }
        }
      }
  
      return new ServiceSettings();
    });
  