import ol_control_WMSCapabilities from './WMSCapabilities.js';

import ol_View from 'ol/View.js'
import ol_tilegrid_WMTS from 'ol/tilegrid/WMTS.js'
import ol_format_WMTSCapabilities from 'ol/format/WMTSCapabilities.js'
import ol_layer_Tile from 'ol/layer/Tile.js'
import ol_source_WMTS from 'ol/source/WMTS.js'
import { get as ol_proj_get } from 'ol/proj.js'
import { transformExtent as ol_proj_transformExtent } from 'ol/proj.js'
import { getWidth as ol_extent_getWidth } from 'ol/extent.js'

/** WMTSCapabilities
 * @constructor
 * @fires load
 * @fires capabilities
 * @extends {ol_control_WMSCapabilities}
 * @param {*} options
 *  @param {string|Element} [options.target] the target to set the dialog, use document.body to have fullwindow dialog
 *  @param {string} [options.proxy] proxy to use when requesting Getcapabilites, default none (suppose the service use CORS)
 *  @param {string} [options.placeholder='service url...'] input placeholder, default 'service url...'
 *  @param {string} [options.title=WMTS] dialog title, default 'WMTS'
 *  @param {string} [options.searchLabel='search'] Label for search button, default 'search'
 *  @param {string} [options.loadLabel='load'] Label for load button, default 'load'
 *  @param {Array<string>} [options.srs] an array of supported srs, default map projection code or 'EPSG:3857'
 *  @param {number} [options.timeout=1000] Timeout for getCapabilities request, default 1000
 *  @param {boolean} [options.cors=false] Use CORS, default false
 *  @param {string} [options.optional] a list of optional url properties (when set in the request url), separated with ','
 *  @param {boolean} [options.trace=false] Log layer info, default false
 *  @param {*} [options.services] a key/url object of services for quick access in a menu
 */
var ol_control_WMTSCapabilities = class olcontrolWMTSCapabilities extends ol_control_WMSCapabilities {
  constructor(options) {
    options = options || {};
    options.title = options.title || 'WMTS';

    super(options);

    this.getDialog().set('className', this.getDialog().get('className') + ' ol-wmtscapabilities' );
  }
  /** Get service parser
   * @private
   */
  _getParser() {
    var pars = new ol_format_WMTSCapabilities();
    return {
      read: function (data) {
        var resp = pars.read(data);
        resp.Capability = {
          Layer: resp.Contents,
        };
        // Generic attribution for layers
        resp.Capability.Layer.Attribution = {
          Title: resp.ServiceProvider.ProviderName
        };
        // Remove non image format
        var layers = [];
        resp.Contents.Layer.forEach(function (l) {
          if (l.Format && /jpeg|png/.test(l.Format[0])) {
            layers.push(l);
          }
        });
        resp.Contents.Layer = layers;
        return resp;
      }.bind(this)
    };
  }
  /** Get Capabilities request parameters
   * @param {*} options
   */
  getRequestParam(options) {
    return {
      SERVICE: 'WMTS',
      REQUEST: 'GetCapabilities',
      VERSION: options.version || '1.0.0'
    };
  }
  /** Get tile grid options only for EPSG:3857 projection
   * @returns {*}
   * @private
   */
  _getTG(tileMatrixSet, minZoom, maxZoom, tilePrefix) {
    var matrixIds = new Array();
    var resolutions = new Array();
    var size = ol_extent_getWidth(ol_proj_get('EPSG:3857').getExtent()) / 256;
    for (var z = 0; z <= (maxZoom ? maxZoom : 20); z++) {
      var id = tilePrefix ? tileMatrixSet + ':' + z : z;
      matrixIds[z] = id;
      resolutions[z] = size / Math.pow(2, z);
    }
    return {
      origin: [-20037508, 20037508],
      resolutions: resolutions,
      matrixIds: matrixIds,
      minZoom: (minZoom ? minZoom : 0)
    };
  }
  /** Get WMTS tile grid (only EPSG:3857)
   * @param {sting} tileMatrixSet
   * @param {number} minZoom
   * @param {number} maxZoom
   * @param {boolean} tilePrefix
   * @returns {ol_tilegrid_WMTS}
   * @private
   */
  getTileGrid(tileMatrixSet, minZoom, maxZoom, tilePrefix) {
    return new ol_tilegrid_WMTS(this._getTG(tileMatrixSet, minZoom, maxZoom, tilePrefix));
  }
  /** Check if the TileMatrixSet is supported
   * @param {Object} tm
   * @returns {boolean}
   */
  isSupportedSet(tm) {
    if (/^PM_.*/.test(tm.TileMatrixSet)) return true;
    return this.supportedSets.indexOf(tm.TileMatrixSet) >= 0;
  }
  /** Return a WMTS options for the given capabilities
   * @param {*} caps layer capabilities (read from the capabilities)
   * @param {*} parent capabilities
   * @return {*} options
   */
  getOptionsFromCap(caps, parent) {
    var bbox = caps.WGS84BoundingBox;
    if (bbox) bbox = ol_proj_transformExtent(bbox, 'EPSG:4326', this.getMap().getView().getProjection());

    // Tilematrix zoom
    var minZoom = Infinity, maxZoom = -Infinity;
    var tmatrix;
    caps.TileMatrixSetLink.forEach(function (tm) {
      if (this.isSupportedSet(tm)) {
        tmatrix = tm;
        caps.TileMatrixSet = tm.TileMatrixSet;
      }
    }.bind(this));
    if (!tmatrix) {
      this.showError({ type: 'TileMatrix' });
      return;
    }
    if (tmatrix.TileMatrixSetLimits) {
      var tilePrefix = tmatrix.TileMatrixSetLimits[0].TileMatrix.split(':').length > 1;
      tmatrix.TileMatrixSetLimits.forEach(function (tm) {
        var zoom = tm.TileMatrix.split(':').pop();
        minZoom = Math.min(minZoom, parseInt(zoom));
        maxZoom = Math.max(maxZoom, parseInt(zoom));
      });
    } else {
      minZoom = 0;
      maxZoom = 20;
    }

    var view = new ol_View();
    view.setZoom(minZoom);
    var layer_opt = {
      title: caps.Title,
      extent: bbox,
      abstract: caps.Abstract,
      maxResolution: view.getResolution()
    };

    var source_opt = {
      url: parent.url,
      layer: caps.Identifier,
      matrixSet: caps.TileMatrixSet,
      format: caps.Format[0] || 'image/jpeg',
      projection: 'EPSG:3857',
      //tileGrid: tg,
      tilePrefix: tilePrefix,
      minZoom: minZoom,
      maxZoom: maxZoom,
      style: caps.Style ? caps.Style[0].Identifier : 'normal',
      attributions: caps.Attribution.Title,
      crossOrigin: this.get('cors') ? 'anonymous' : null,
      wrapX: (this.get('wrapX') !== false),
    };

    // Fill form
    this._fillForm({
      title: layer_opt.title,
      layers: source_opt.layer,
      style: source_opt.style,
      format: source_opt.format,
      minZoom: minZoom,
      maxZoom: maxZoom,
      extent: bbox ? bbox.join(',') : '',
      projection: source_opt.projection,
      attribution: source_opt.attributions || '',
      version: '1.0.0'
    });

    // Trace
    if (this.get('trace')) {
      // Source
      source_opt.tileGrid = 'TILEGRID';
      var tso = JSON.stringify([source_opt], null, "\t").replace(/\\"/g, '"');
      tso = tso.replace('"TILEGRID"', 'new ol_tilegrid_WMTS('
        + JSON.stringify(this._getTG(source_opt.matrixSet, source_opt.minZoom, source_opt.maxZoom, source_opt.tilePrefix), null, '\t').replace(/\n/g, '\n\t\t')
        + ')'
      );
      delete source_opt.tileGrid;
      // Layer
      layer_opt.source = "SOURCE";
      var t = "new ol.layer.Tile (" + JSON.stringify(layer_opt, null, "\t") + ")";
      t = t.replace(/\\"/g, '"')
        .replace('"SOURCE"', "new ol.source.WMTS(" + tso + ")")
        .replace(/\\t/g, "\t").replace(/\\n/g, "\n")
        .replace(/"tileGrid": {/g, '"tileGrid": new ol.tilegrid.WMTS({')
        .replace(/},\n(\t*)"style"/g, '}),\n$1"style"')
        .replace("([\n\t", "(")
        .replace("}\n])", "})");
      console.log(t);
      delete layer_opt.source;
    }
    var returnedLegend = undefined;
    if (caps.Style && caps.Style[0] && caps.Style[0].LegendURL && caps.Style[0].LegendURL[0]) {
      returnedLegend = [ caps.Style[0].LegendURL[0].href ];
    }
    return ({
      layer: layer_opt,
      source: source_opt,
      data: {
        title: caps.Title,
        abstract: caps.Abstract,
        legend: returnedLegend,
      }
    });
  }
  /** Get WMS options from control form
   * @return {*} original original options
   * @return {*} options
   * @private
   */
  _getFormOptions() {
    var options = this._currentOptions || {};
    if (!options.layer)
      options.layer = {};
    if (!options.source)
      options.source = {};
    if (!options.data)
      options.data = {};

    var minZoom = parseInt(this._elements.formMinZoom.value) || 0;
    var maxZoom = parseInt(this._elements.formMaxZoom.value) || 20;
    var ext = [];
    if (this._elements.formExtent.value) {
      this._elements.formExtent.value.split(',').forEach(function (b) {
        ext.push(parseFloat(b));
      });
    }
    if (ext.length !== 4)
      ext = undefined;
    var attributions = [];
    if (this._elements.formAttribution.value)
      attributions.push(this._elements.formAttribution.value);

    var view = new ol_View({
      projection: this.getMap().getView().getProjection()
    });
    view.setZoom(minZoom);
    var layer_opt = {
      title: this._elements.formTitle.value,
      extent: ext,
      abstract: options.layer.abstract || '',
      maxResolution: view.getResolution()
    };

    var source_opt = {
      url: this._elements.input.value,
      layer: this._elements.formLayer.value,
      matrixSet: options.source.matrixSet || 'PM',
      format: this._elements.formFormat.options[this._elements.formFormat.selectedIndex].value,
      projection: 'EPSG:3857',
      minZoom: minZoom,
      maxZoom: maxZoom,
      // tileGrid: this._getTG(options.source.matrixSet || 'PM', minZoom, maxZoom),
      style: this._elements.formStyle.value || 'normal',
      attributions: attributions,
      crossOrigin: this._elements.formCrossOrigin.checked ? 'anonymous' : null,
      wrapX: (this.get('wrapX') !== false),
    };

    return ({
      layer: layer_opt,
      source: source_opt,
      data: {
        title: this._elements.formTitle.value,
        abstract: options.data.abstract,
        legend: options.data.legend,
      }
    });
  }
  /** Create a new layer using options received by getOptionsFromCap method
   * @param {*} options
   */
  getLayerFromOptions(options) {
    if (!options)
      return;
    options.source.tileGrid = this.getTileGrid(options.source.matrixSet, options.source.minZoom, options.source.maxZoom, options.source.tilePrefix);
    options.layer.source = new ol_source_WMTS(options.source);
    var layer = new ol_layer_Tile(options.layer);
    // Restore options
    delete options.layer.source;
    delete options.source.tileGrid;
    return layer;
  }
}

/** An array of supported sets (basically EPSG:3857)
 * @api
 */
ol_control_WMSCapabilities.prototype.supportedSets = [
  'PM',
  '3857',
  'EPSG:3857',
  'EPSG:900913',
  'webmercator',
  'GoogleMapsCompatible'
]

export default ol_control_WMTSCapabilities
