text: /**
   * @module ol/reproj/Image
   */
  import {ERROR_THRESHOLD} from './common.js';
  import {inherits} from '../index.js';
  import ImageBase from '../ImageBase.js';
  import ImageState from '../ImageState.js';
  import {listen, unlistenByKey} from '../events.js';
  import EventType from '../events/EventType.js';
  import {getCenter, getIntersection, getHeight, getWidth} from '../extent.js';
  import {calculateSourceResolution, render as renderReprojected} from '../reproj.js';
  import Triangulation from '../reproj/Triangulation.js';
  
  /**
   * @classdesc
   * Class encapsulating single reprojected image.
   * See {@link ol.source.Image}.
   *
   * @constructor
   * @extends {module:ol/ImageBase~ImageBase}
   * @param {module:ol/proj/Projection~Projection} sourceProj Source projection (of the data).
   * @param {module:ol/proj/Projection~Projection} targetProj Target projection.
   * @param {module:ol/extent~Extent} targetExtent Target extent.
   * @param {number} targetResolution Target resolution.
   * @param {number} pixelRatio Pixel ratio.
   * @param {ol.ReprojImageFunctionType} getImageFunction
   *     Function returning source images (extent, resolution, pixelRatio).
   */
  const ReprojImage = function(sourceProj, targetProj,
    targetExtent, targetResolution, pixelRatio, getImageFunction) {
  
    /**
     * @private
     * @type {module:ol/proj/Projection~Projection}
     */
    this.targetProj_ = targetProj;
  
    /**
     * @private
     * @type {module:ol/extent~Extent}
     */
    this.maxSourceExtent_ = sourceProj.getExtent();
    const maxTargetExtent = targetProj.getExtent();
  
    const limitedTargetExtent = maxTargetExtent ?
      getIntersection(targetExtent, maxTargetExtent) : targetExtent;
  
    const targetCenter = getCenter(limitedTargetExtent);
    const sourceResolution = calculateSourceResolution(
      sourceProj, targetProj, targetCenter, targetResolution);
  
    const errorThresholdInPixels = ERROR_THRESHOLD;
  
    /**
     * @private
     * @type {!ol.reproj.Triangulation}
     */
    this.triangulation_ = new Triangulation(
      sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_,
      sourceResolution * errorThresholdInPixels);
  
    /**
     * @private
     * @type {number}
     */
    this.targetResolution_ = targetResolution;
  
    /**
     * @private
     * @type {module:ol/extent~Extent}
     */
    this.targetExtent_ = targetExtent;
  
    const sourceExtent = this.triangulation_.calculateSourceExtent();
  
    /**
     * @private
     * @type {module:ol/ImageBase~ImageBase}
     */
    this.sourceImage_ =
        getImageFunction(sourceExtent, sourceResolution, pixelRatio);
  
    /**
     * @private
     * @type {number}
     */
    this.sourcePixelRatio_ =
        this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1;
  
    /**
     * @private
     * @type {HTMLCanvasElement}
     */
    this.canvas_ = null;
  
    /**
     * @private
     * @type {?module:ol/events~EventsKey}
     */
    this.sourceListenerKey_ = null;
  
  
    let state = ImageState.LOADED;
  
    if (this.sourceImage_) {
      state = ImageState.IDLE;
    }
  
    ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, state);
  };
  
  inherits(ReprojImage, ImageBase);
  
  
  /**
   * @inheritDoc
   */
  ReprojImage.prototype.disposeInternal = function() {
    if (this.state == ImageState.LOADING) {
      this.unlistenSource_();
    }
    ImageBase.prototype.disposeInternal.call(this);
  };
  
  
  /**
   * @inheritDoc
   */
  ReprojImage.prototype.getImage = function() {
    return this.canvas_;
  };
  
  
  /**
   * @return {module:ol/proj/Projection~Projection} Projection.
   */
  ReprojImage.prototype.getProjection = function() {
    return this.targetProj_;
  };
  
  
  /**
   * @private
   */
  ReprojImage.prototype.reproject_ = function() {
    const sourceState = this.sourceImage_.getState();
    if (sourceState == ImageState.LOADED) {
      const width = getWidth(this.targetExtent_) / this.targetResolution_;
      const height = getHeight(this.targetExtent_) / this.targetResolution_;
  
      this.canvas_ = renderReprojected(width, height, this.sourcePixelRatio_,
        this.sourceImage_.getResolution(), this.maxSourceExtent_,
        this.targetResolution_, this.targetExtent_, this.triangulation_, [{
          extent: this.sourceImage_.getExtent(),
          image: this.sourceImage_.getImage()
        }], 0);
    }
    this.state = sourceState;
    this.changed();
  };
  
  
  /**
   * @inheritDoc
   */
  ReprojImage.prototype.load = function() {
    if (this.state == ImageState.IDLE) {
      this.state = ImageState.LOADING;
      this.changed();
  
      const sourceState = this.sourceImage_.getState();
      if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) {
        this.reproject_();
      } else {
        this.sourceListenerKey_ = listen(this.sourceImage_,
          EventType.CHANGE, function(e) {
            const sourceState = this.sourceImage_.getState();
            if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) {
              this.unlistenSource_();
              this.reproject_();
            }
          }, this);
        this.sourceImage_.load();
      }
    }
  };
  
  
  /**
   * @private
   */
  ReprojImage.prototype.unlistenSource_ = function() {
    unlistenByKey(/** @type {!module:ol/events~EventsKey} */ (this.sourceListenerKey_));
    this.sourceListenerKey_ = null;
  };
  export default ReprojImage;
  