"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.supportedParsers = exports.supportedFetchers = exports.default = void 0;
var _Extent = _interopRequireDefault(require("../Core/Geographic/Extent"));
var _GeoJsonParser = _interopRequireDefault(require("../Parser/GeoJsonParser"));
var _KMLParser = _interopRequireDefault(require("../Parser/KMLParser"));
var _GDFParser = _interopRequireDefault(require("../Parser/GDFParser"));
var _GpxParser = _interopRequireDefault(require("../Parser/GpxParser"));
var _GTXParser = _interopRequireDefault(require("../Parser/GTXParser"));
var _ISGParser = _interopRequireDefault(require("../Parser/ISGParser"));
var _VectorTileParser = _interopRequireDefault(require("../Parser/VectorTileParser"));
var _Fetcher = _interopRequireDefault(require("../Provider/Fetcher"));
var _Cache = _interopRequireDefault(require("../Core/Scheduler/Cache"));
var _Crs = _interopRequireDefault(require("../Core/Geographic/Crs"));
const supportedFetchers = new Map([['image/x-bil;bits=32', _Fetcher.default.textureFloat], ['geojson', _Fetcher.default.json], ['application/json', _Fetcher.default.json], ['application/kml', _Fetcher.default.xml], ['application/gpx', _Fetcher.default.xml], ['application/x-protobuf;type=mapbox-vector', _Fetcher.default.arrayBuffer], ['application/gtx', _Fetcher.default.arrayBuffer], ['application/isg', _Fetcher.default.text], ['application/gdf', _Fetcher.default.text]]);
exports.supportedFetchers = supportedFetchers;
const supportedParsers = new Map([['geojson', _GeoJsonParser.default.parse], ['application/json', _GeoJsonParser.default.parse], ['application/kml', _KMLParser.default.parse], ['application/gpx', _GpxParser.default.parse], ['application/x-protobuf;type=mapbox-vector', _VectorTileParser.default.parse], ['application/gtx', _GTXParser.default.parse], ['application/isg', _ISGParser.default.parse], ['application/gdf', _GDFParser.default.parse]]);
exports.supportedParsers = supportedParsers;
const noCache = {
  getByArray: () => {},
  setByArray: a => a,
  clear: () => {}
};

/**
 * @property {string} crs - data crs projection.
 * @property {boolean} isInverted - This option is to be set to the
 * correct value, true or false (default being false), if the computation of
 * the coordinates needs to be inverted to same scheme as OSM, Google Maps
 * or other system. See [this link]{@link
 * https://alastaira.wordpress.com/2011/07/06/converting-tms-tile-coordinates-to-googlebingosm-tile-coordinates}
 * for more informations.
 *
 */
class InformationsData {
  constructor(options) {
    /* istanbul ignore next */
    if (options.projection) {
      console.warn('Source projection parameter is deprecated, use crs instead.');
      options.crs = options.crs || options.projection;
    }
    if (options.crs) {
      _Crs.default.isValid(options.crs);
    }
    this.crs = options.crs;
  }
}
/**
 * This class describes parsing options.
 * @property {InformationsData|Source} in - data informations contained in the file.
 * @property {FeatureBuildingOptions|Layer} out - options indicates how the features should be built.
 */
// eslint-disable-next-line
class /* istanbul ignore next */ParsingOptions {}
function fetchSourceData(source, extent) {
  const url = source.urlFromExtent(extent);
  return source.fetcher(url, source.networkOptions).then(f => {
    f.extent = extent;
    return f;
  }, err => source.handlingError(err));
}
let uid = 0;

/**
 * @classdesc
 * Sources are object containing informations on how to fetch resources, from a
 * set source.
 *
 * To extend a Source, it is necessary to implement two functions:
 * `urlFromExtent` and `extentInsideLimit`.
 *
 * @property {boolean} isSource - Used to checkout whether this source is a
 * Source. Default is true. You should not change this, as it is used internally
 * for optimisation.
 * @property {number} uid - Unique uid mainly used to store data linked to this
 * source into Cache.
 * @property {string} url - The url of the resources that are fetched.
 * @property {string} format - The format of the resources that are fetched.
 * @property {function} fetcher - The method used to fetch the resources from
 * the source. iTowns provides some methods in {@link Fetcher}, but it can be
 * specified a custom one. This method should return a `Promise` containing the
 * fetched resource. If this property is set, it overrides the chosen fetcher
 * method with `format`.
 * @property {Object} networkOptions - Fetch options (passed directly to
 * `fetch()`), see [the syntax for more information]{@link
 * https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax}.
 * By default, set to `{ crossOrigin: 'anonymous' }`.
 * @property {string} crs - The crs projection of the resources.
 * @property {string} attribution - The intellectual property rights for the
 * resources.
 * @property {Extent} extent - The extent of the resources.
 * @property {function} parser - The method used to parse the resources attached
 * to the layer. iTowns provides some parsers, visible in the `Parser/` folder.
 * If the method is custom, it should return a `Promise` containing the parsed
 * resource. If this property is set, it overrides the default selected parser
 * method with `source.format`. If `source.format` is also empty, no parsing
 * action is done.
 * <br><br>
 * When calling this method, two parameters are passed:
 * <ul>
 *  <li>the fetched data, i.e. the data to parse</li>
 *  <li>an {@link ParsingOptions}  containing severals properties, set when this method is
 *  called: it is specific to each call, so the value of each property can vary
 *  depending on the current fetched tile for example</li>
 * </ul>
 */
class Source extends InformationsData {
  /**
   * @param {Object} source - An object that can contain all properties of a
   * Source. Only the `url` property is mandatory.
   *
   * @constructor
   * @extends InformationsData
   */
  constructor(source) {
    super(source);
    this.isSource = true;
    if (!source.url) {
      throw new Error('New Source: url is required');
    }
    this.uid = uid++;
    this.url = source.url;
    this.format = source.format;
    this.fetcher = source.fetcher || supportedFetchers.get(source.format) || _Fetcher.default.texture;
    this.parser = source.parser || supportedParsers.get(source.format) || (d => d);
    this.isVectorSource = (source.parser || supportedParsers.get(source.format)) != undefined;
    this.networkOptions = source.networkOptions || {
      crossOrigin: 'anonymous'
    };
    this.attribution = source.attribution;
    this.whenReady = Promise.resolve();
    this._featuresCaches = {};
    if (source.extent && !source.extent.isExtent) {
      this.extent = new _Extent.default(this.crs, source.extent);
    } else {
      this.extent = source.extent;
    }
  }
  handlingError(err) {
    throw new Error(err);
  }

  /**
   * Generates an url from an extent. This url is a link to fetch the
   * resources inside the extent.
   *
   * @param {Extent} extent - Extent to convert in url.
    * @return {string} The URL constructed from the extent.
   */
  // eslint-disable-next-line
  urlFromExtent() {
    throw new Error('In extended Source, you have to implement the method urlFromExtent!');
  }
  requestToKey(extent) {
    return [extent.zoom, extent.row, extent.col];
  }

  /**
   * Load  data from cache or Fetch/Parse data.
   * The loaded data is a Feature or Texture.
   *
   * @param      {Extent}  extent   extent requested parsed data.
   * @param      {FeatureBuildingOptions|Layer}  out     The feature returned options
   * @return     {FeatureCollection|Texture}  The parsed data.
   */
  loadData(extent, out) {
    const cache = this._featuresCaches[out.crs];
    const key = this.requestToKey(extent);
    // try to get parsed data from cache
    let features = cache.getByArray(key);
    if (!features) {
      // otherwise fetch/parse the data
      features = cache.setByArray(fetchSourceData(this, extent).then(file => this.parser(file, {
        out,
        in: this
      }), err => this.handlingError(err)), key);
      /* istanbul ignore next */
      if (this.onParsedFile) {
        features.then(feat => {
          this.onParsedFile(feat);
          console.warn('Source.onParsedFile was deprecated');
          return feat;
        });
      }
    }
    return features;
  }

  /**
   * Called when layer added.
   *
   * @param {object} options
   */
  onLayerAdded(options) {
    // Added new cache by crs
    if (!this._featuresCaches[options.out.crs]) {
      // Cache feature only if it's vector data, the feature are cached in source.
      // It's not necessary to cache raster in Source,
      // because it's already cached on layer.
      this._featuresCaches[options.out.crs] = this.isVectorSource ? new _Cache.default() : noCache;
    }
  }

  /**
   * Called when layer removed.
   *
   * @param {options}  [options={}] options
   */
  onLayerRemoved() {
    let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    // delete unused cache
    const unusedCache = this._featuresCaches[options.unusedCrs];
    if (unusedCache) {
      unusedCache.clear();
      delete this._featuresCaches[options.unusedCrs];
    }
  }

  /**
   * Tests if an extent is inside the source limits.
   *
   * @param {Extent} extent - Extent to test.
    * @return {boolean} True if the extent is inside the limit, false otherwise.
   */
  // eslint-disable-next-line
  extentInsideLimit() {
    throw new Error('In extented Source, you have to implement the method extentInsideLimit!');
  }
}
var _default = Source;
exports.default = _default;