/**
 * @module ol/source/ImageStatic
 */

import EventType from '../events/EventType.js';
import ImageSource, {defaultImageLoadFunction} from './Image.js';
import ImageState from '../ImageState.js';
import ImageWrapper from '../Image.js';
import {assign} from '../obj.js';
import {createCanvasContext2D} from '../dom.js';
import {getHeight, getWidth, intersects} from '../extent.js';
import {get as getProjection} from '../proj.js';

/**
 * @typedef {Object} Options
 * @property {import("./Source.js").AttributionLike} [attributions] Attributions.
 * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images.  Note that
 * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
 * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
 * @property {import("../extent.js").Extent} [imageExtent] Extent of the image in map coordinates.
 * This is the [left, bottom, right, top] map coordinates of your image.
 * @property {import("../Image.js").LoadFunction} [imageLoadFunction] Optional function to load an image given a URL.
 * @property {boolean} [imageSmoothing=true] Enable image smoothing.
 * @property {import("../proj.js").ProjectionLike} [projection] Projection. Default is the view projection.
 * @property {import("../size.js").Size} [imageSize] Size of the image in pixels. Usually the image size is auto-detected, so this
 * only needs to be set if auto-detection fails for some reason.
 * @property {string} url Image URL.
 */

/**
 * @classdesc
 * A layer source for displaying a single, static image.
 * @api
 */
class Static extends ImageSource {
  /**
   * @param {Options} options ImageStatic options.
   */
  constructor(options) {
    const crossOrigin =
      options.crossOrigin !== undefined ? options.crossOrigin : null;

    const /** @type {import("../Image.js").LoadFunction} */ imageLoadFunction =
        options.imageLoadFunction !== undefined
          ? options.imageLoadFunction
          : defaultImageLoadFunction;

    super({
      attributions: options.attributions,
      imageSmoothing: options.imageSmoothing,
      projection: getProjection(options.projection),
    });

    /**
     * @private
     * @type {string}
     */
    this.url_ = options.url;

    /**
     * @private
     * @type {import("../extent.js").Extent}
     */
    this.imageExtent_ = options.imageExtent;

    /**
     * @private
     * @type {import("../Image.js").default}
     */
    this.image_ = new ImageWrapper(
      this.imageExtent_,
      undefined,
      1,
      this.url_,
      crossOrigin,
      imageLoadFunction
    );

    /**
     * @private
     * @type {import("../size.js").Size}
     */
    this.imageSize_ = options.imageSize ? options.imageSize : null;

    this.image_.addEventListener(
      EventType.CHANGE,
      this.handleImageChange.bind(this)
    );
  }

  /**
   * Returns the image extent
   * @return {import("../extent.js").Extent} image extent.
   * @api
   */
  getImageExtent() {
    return this.imageExtent_;
  }

  /**
   * @param {import("../extent.js").Extent} extent Extent.
   * @param {number} resolution Resolution.
   * @param {number} pixelRatio Pixel ratio.
   * @param {import("../proj/Projection.js").default} projection Projection.
   * @return {import("../Image.js").default} Single image.
   */
  getImageInternal(extent, resolution, pixelRatio, projection) {
    if (intersects(extent, this.image_.getExtent())) {
      return this.image_;
    }
    return null;
  }

  /**
   * Return the URL used for this image source.
   * @return {string} URL.
   * @api
   */
  getUrl() {
    return this.url_;
  }

  /**
   * @param {import("../events/Event.js").default} evt Event.
   */
  handleImageChange(evt) {
    if (this.image_.getState() == ImageState.LOADED) {
      const imageExtent = this.image_.getExtent();
      const image = this.image_.getImage();
      let imageWidth, imageHeight;
      if (this.imageSize_) {
        imageWidth = this.imageSize_[0];
        imageHeight = this.imageSize_[1];
      } else {
        imageWidth = image.width;
        imageHeight = image.height;
      }
      const extentWidth = getWidth(imageExtent);
      const extentHeight = getHeight(imageExtent);
      const xResolution = extentWidth / imageWidth;
      const yResolution = extentHeight / imageHeight;
      let targetWidth = imageWidth;
      let targetHeight = imageHeight;
      if (xResolution > yResolution) {
        targetWidth = Math.round(extentWidth / yResolution);
      } else {
        targetHeight = Math.round(extentHeight / xResolution);
      }
      if (targetWidth !== imageWidth || targetHeight !== imageHeight) {
        const context = createCanvasContext2D(targetWidth, targetHeight);
        assign(context, this.getContextOptions());
        const canvas = context.canvas;
        context.drawImage(
          image,
          0,
          0,
          imageWidth,
          imageHeight,
          0,
          0,
          canvas.width,
          canvas.height
        );
        this.image_.setImage(canvas);
      }
    }
    super.handleImageChange(evt);
  }
}

export default Static;
