/*	Copyright (c) 2019 Jean-Marc VIGLINO,
  released under the CeCILL-B license (French BSD license)
  (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
*/
import ol_source_WMTS from 'ol/source/WMTS.js'
import {getWidth as ol_extent_getWidth} from 'ol/extent.js'
import ol_tilegrid_WMTS from 'ol/tilegrid/WMTS.js'
import {get as ol_proj_get} from 'ol/proj.js' 
import ol_ext_Ajax from '../util/Ajax.js'

/** IGN's Geoportail WMTS source
 * @constructor
 * @extends {ol.source.WMTS}
 * @param {olx.source.Geoportail=} options WMTS options 
 *  @param {string=} options.layer Geoportail layer name
 *  @param {number} options.minZoom
 *  @param {number} options.maxZoom
 *  @param {string} options.server
 *  @param {string} [options.gppKey] api key, default none
 *  @param {string} options.authentication basic authentication associated with the gppKey as btoa("login:pwd")
 *  @param {string} options.format image format, default 'image/jpeg'
 *  @param {string} options.style layer style, default 'normal'
 *  @param {string} options.crossOrigin default 'anonymous'
 *  @param {string} options.wrapX default true
 */
var ol_source_Geoportail = class olsourceGeoportail extends ol_source_WMTS {
  constructor(layer, options) {
    options = options || {}
    if (layer.layer) {
      options = layer
      layer = options.layer
    }

    var matrixIds = new Array()
    var resolutions = new Array() //[156543.03392804103,78271.5169640205,39135.75848201024,19567.879241005125,9783.939620502562,4891.969810251281,2445.9849051256406,1222.9924525628203,611.4962262814101,305.74811314070485,152.87405657035254,76.43702828517625,38.218514142588134,19.109257071294063,9.554628535647034,4.777314267823517,2.3886571339117584,1.1943285669558792,0.5971642834779396,0.29858214173896974,0.14929107086948493,0.07464553543474241];
    var size = ol_extent_getWidth(ol_proj_get('EPSG:3857').getExtent()) / 256
    for (var z = 0; z <= (options.maxZoom ? options.maxZoom : 20); z++) {
      matrixIds[z] = z
      resolutions[z] = size / Math.pow(2, z)
    }
    var tg = new ol_tilegrid_WMTS({
      origin: [-20037508, 20037508],
      resolutions: resolutions,
      matrixIds: matrixIds
    })
    tg.minZoom = (options.minZoom ? options.minZoom : 0)
    var attr = [ ol_source_Geoportail.defaultAttribution ]
    if (options.attributions) attr = options.attributions
    var server = options.server || 'https://data.geopf.fr/wmts' // 'https://wxs.ign.fr/geoportail/wmts' old version
    var gppKey = options.gppKey || options.key || ''

    var wmts_options = {
      url: ol_source_Geoportail.getServiceURL(server, gppKey),
      layer: layer,
      matrixSet: 'PM',
      format: options.format ? options.format : 'image/jpeg',
      projection: 'EPSG:3857',
      tileGrid: tg,
      style: options.style ? options.style : 'normal',
      attributions: attr,
      crossOrigin: (typeof options.crossOrigin == 'undefined') ? 'anonymous' : options.crossOrigin,
      wrapX: !(options.wrapX === false)
    }

    super(wmts_options)
    this._server = server
    this._gppKey = gppKey
    // Load url using basic authentification
    if (options.authentication) {
      this.setTileLoadFunction(ol_source_Geoportail.tileLoadFunctionWithAuthentication(options.authentication, this.getFormat()))
    }
  }
  /** Get a tile load function to load tiles with basic authentication
   * @param {string} authentication as btoa("login:pwd")
   * @param {string} format mime type
   * @return {function} tile load function to load tiles with basic authentication
   */
  static tileLoadFunctionWithAuthentication(authentication, format) {
    if (!authentication)
      return undefined
    return function (tile, src) {
      var xhr = new XMLHttpRequest()
      xhr.open("GET", src)
      xhr.setRequestHeader("Authorization", "Basic " + authentication)
      xhr.responseType = "arraybuffer"
      xhr.onload = function () {
        var arrayBufferView = new Uint8Array(this.response)
        var blob = new Blob([arrayBufferView], { type: format })
        var urlCreator = window.URL || window.webkitURL
        var imageUrl = urlCreator.createObjectURL(blob)
        tile.getImage().src = imageUrl
      }
      xhr.onerror = function () {
        tile.getImage().src = ""
      }
      xhr.send()
    }
  }
  /** Get service URL according to server url or standard url
   */
  serviceURL() {
    return ol_source_Geoportail.getServiceURL(this._server, this._gppKey)
  }
  /**
   * Return the associated API key of the Map.
   * @function
   * @return the API key.
   * @api stable
   */
  getGPPKey() {
    return this._gppKey
  }
  /**
   * Set the associated API key to the Map.
   * @param {String} key the API key.
   * @param {String} authentication as btoa("login:pwd")
   * @api stable
   */
  setGPPKey(key, authentication) {
    this._gppKey = key
    var serviceURL = this.serviceURL()
    this.setTileUrlFunction(function () {
      var url = ol_source_Geoportail.prototype.getTileUrlFunction().apply(this, arguments)
      if (url) {
        var args = url.split("?")
        return serviceURL + "?" + args[1]
      }
      else
        return url
    })
    // Load url using basic authentification
    if (authentication) {
      this.setTileLoadFunction(ol_source_Geoportail.tileLoadFunctionWithAuthentication(authentication, this.getFormat()))
    }
  }
  /** Return the GetFeatureInfo URL for the passed coordinate, resolution, and
   * projection. Return `undefined` if the GetFeatureInfo URL cannot be
   * constructed.
   * @param {ol.Coordinate} coord
   * @param {Number} resolution
   * @param {ol.proj.Projection} projection default the source projection
   * @param {Object} options
   *  @param {string} options.INFO_FORMAT response format text/plain, text/html, application/json, default text/plain
   * @return {String|undefined} GetFeatureInfo URL.
   */
  getFeatureInfoUrl(coord, resolution, projection, options) {
    options = options || {}
    if (!projection)
      projection = this.getProjection()
    var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution(coord, resolution)
    var ratio = 1
    var url = this.getTileUrlFunction()(tileCoord, ratio, projection)
    if (!url)
      return url

    var tileResolution = this.tileGrid.getResolution(tileCoord[0])
    var tileExtent = this.tileGrid.getTileCoordExtent(tileCoord)
    var i = Math.floor((coord[0] - tileExtent[0]) / (tileResolution / ratio))
    var j = Math.floor((tileExtent[3] - coord[1]) / (tileResolution / ratio))

    return url.replace(/Request=GetTile/i, 'Request=getFeatureInfo')
      + '&INFOFORMAT=' + (options.INFO_FORMAT || 'text/plain')
      + '&I=' + i
      + '&J=' + j
  }
  /** Get feature info
   * @param {ol.Coordinate} coord
   * @param {Number} resolution
   * @param {ol.proj.Projection} projection default the source projection
   * @param {Object} options
   *  @param {string} options.INFO_FORMAT response format text/plain, text/html, application/json, default text/plain
   *  @param {function} options.callback a function that take the response as parameter
   *  @param {function} options.error function called when an error occurred
   */
  getFeatureInfo(coord, resolution, options) {
    var url = this.getFeatureInfoUrl(coord, resolution, null, options)
    ol_ext_Ajax.get({
      url: url,
      dataType: options.format || 'text/plain',
      options: {
        encode: false
      },
      success: function (resp) {
        if (options.callback)
          options.callback(resp)
      },
      error: options.error || function () { }
    })
  }
}

/** Standard IGN-GEOPORTAIL attribution 
 */
ol_source_Geoportail.defaultAttribution = '<a href="https://geoservices.ign.fr/">Géoservices</a> &copy; <a href="http://www.ign.fr/">IGN-France</a>';

/** Get service URL according to server url or standard url
 */
ol_source_Geoportail.getServiceURL = function(server, gppKey) {
  // Old gppkey
  if (gppKey === 'gpf') gppKey = '';
  // Check server
  if (!server) {
    if (gppKey) {
      server = 'https://data.geopf.fr/private/wmts';
    } else {
      server = 'https://data.geopf.fr/wmts';
    }
  } 
  // Add api key
  if (/geopf/.test(server)) {
    if (gppKey) {
      return server + '?apikey=' + gppKey;
    } else {
      return server;
    }
  } else {
    return server.replace(/^(https?:\/\/[^/]*)(.*)$/, "$1/" + gppKey + "$2")
  }
  /*
    return (window.geoportailConfig ? window.geoportailConfig.url : "https://wxs.ign.fr/") + gppKey + "/geoportail/wmts"
  */
}

export default ol_source_Geoportail
