import {startsWith, getFileExtension} from '../utils/string.js';
import {getUrlFromUri} from '../utils/uri.js';
import {getViewFromDOMImage} from '../image/domReader.js';
import {fileContentTypes} from './filesLoader.js';
import {urlContentTypes} from './urlsLoader.js';

/**
 * Raw image loader.
 */
export class RawImageLoader {

  /**
   * If abort is triggered, all image.onload callbacks have to be cancelled.
   *
   * @type {boolean}
   */
  #aborted = false;

  /**
   * Set the loader options.
   *
   * @param {object} _opt The input options.
   */
  setOptions(_opt) {
    // does nothing
  }

  /**
   * Is the load ongoing? TODO...
   *
   * @returns {boolean} True if loading.
   */
  isLoading() {
    return true;
  }

  /**
   * Create a Data URI from an HTTP request response.
   *
   * @param {ArrayBuffer} response The HTTP request response.
   * @param {string} dataType The data type.
   * @returns {string} The data URI.
   */
  #createDataUri(response, dataType) {
    // image type
    let imageType = dataType;
    if (!imageType || imageType === 'jpg') {
      imageType = 'jpeg';
    }
    // create uri
    const file = new Blob([response], {type: 'image/' + imageType});
    return window.URL.createObjectURL(file);
  }

  /**
   * Load data.
   *
   * @param {ArrayBuffer|string} buffer The read data.
   * @param {string|File} origin The data origin.
   * @param {number} index The data index.
   */
  load(buffer, origin, index) {
    this.#aborted = false;
    // create a DOM image
    const image = new Image();
    // triggered by ctx.drawImage
    image.onload = (/*event*/) => {
      try {
        if (!this.#aborted) {
          this.onprogress({
            lengthComputable: true,
            loaded: 100,
            total: 100,
            index: index,
            source: origin
          });
          const data = getViewFromDOMImage(image, origin, index);
          // only expecting one item
          this.onloaditem(data);
          this.onload(data);
        }
      } catch (error) {
        this.onerror({
          error: error,
          source: origin
        });
      } finally {
        this.onloadend({
          source: origin
        });
      }
    };
    // storing values to pass them on
    if (typeof buffer === 'string') {
      // file case
      image.src = buffer;
    } else if (typeof origin === 'string') {
      // url case
      const ext = origin.split('.').pop().toLowerCase();
      image.src = this.#createDataUri(buffer, ext);
    }
  }

  /**
   * Abort load.
   */
  abort() {
    this.#aborted = true;
    this.onabort({});
    this.onloadend({});
  }

  /**
   * Check if the loader can load the provided file.
   * True for files with type 'image.*'.
   *
   * @param {File} file The file to check.
   * @returns {boolean} True if the file can be loaded.
   */
  canLoadFile(file) {
    return (typeof file.type !== 'undefined' &&
      file.type.match('image.*') !== null);
  }

  /**
   * Check if the loader can load the provided url.
   * True if one of the folowing conditions is true:
   * - the `options.forceLoader` is 'rawimage',
   * - the `options.requestHeaders` contains an item
   *   starting with 'Accept: image/'.
   * - the url has a 'contentType' and it is 'image/jpeg', 'image/png'
   *   or 'image/gif' (as in wado urls),
   * - the url has no 'contentType' and the extension is 'jpeg', 'jpg',
   *   'png' or 'gif'.
   *
   * @param {string} url The url to check.
   * @param {object} [options] Optional url request options.
   * @returns {boolean} True if the url can be loaded.
   */
  canLoadUrl(url, options) {
    // check options
    if (typeof options !== 'undefined') {
      // check options.forceLoader
      if (typeof options.forceLoader !== 'undefined' &&
        options.forceLoader === 'rawimage') {
        return true;
      }
      // check options.requestHeaders for 'Accept'
      if (typeof options.requestHeaders !== 'undefined') {
        const isNameAccept = function (element) {
          return element.name === 'Accept';
        };
        const acceptHeader = options.requestHeaders.find(isNameAccept);
        if (typeof acceptHeader !== 'undefined') {
          // starts with 'image/'
          return startsWith(acceptHeader.value, 'image/');
        }
      }
    }

    const urlObjext = getUrlFromUri(url);
    // extension
    const ext = getFileExtension(urlObjext.pathname);
    const hasImageExt = (ext === 'jpeg') || (ext === 'jpg') ||
      (ext === 'png') || (ext === 'gif');
    // content type (for wado url)
    const contentType = urlObjext.searchParams.get('contentType');
    const hasContentType = contentType !== null &&
      typeof contentType !== 'undefined';
    const hasImageContentType = (contentType === 'image/jpeg') ||
      (contentType === 'image/png') ||
      (contentType === 'image/gif');

    return hasContentType ? hasImageContentType : hasImageExt;
  }

  /**
   * Check if the loader can load the provided memory object.
   *
   * @param {object} mem The memory object.
   * @returns {boolean} True if the object can be loaded.
   */
  canLoadMemory(mem) {
    if (typeof mem.filename !== 'undefined') {
      const tmpFile = new File(['from memory'], mem.filename);
      return this.canLoadFile(tmpFile);
    }
    return false;
  }

  /**
   * Get the file content type needed by the loader.
   *
   * @returns {number} One of the 'fileContentTypes'.
   */
  loadFileAs() {
    return fileContentTypes.DataURL;
  }

  /**
   * Get the url content type needed by the loader.
   *
   * @returns {number} One of the 'urlContentTypes'.
   */
  loadUrlAs() {
    return urlContentTypes.ArrayBuffer;
  }

  /**
   * Handle a load start event.
   * Default does nothing.
   *
   * @param {object} _event The load start event.
   */
  onloadstart(_event) {}

  /**
   * Handle a progress event.
   * Default does nothing.
   *
   * @param {object} _event The progress event.
   */
  onprogress(_event) {}

  /**
   * Handle a load item event.
   * Default does nothing.
   *
   * @param {object} _event The load item event fired
   *   when a file item has been loaded successfully.
   */
  onloaditem(_event) {}

  /**
   * Handle a load event.
   * Default does nothing.
   *
   * @param {object} _event The load event fired
   *   when a file has been loaded successfully.
   */
  onload(_event) {}

  /**
   * Handle an load end event.
   * Default does nothing.
   *
   * @param {object} _event The load end event fired
   *  when a file load has completed, successfully or not.
   */
  onloadend(_event) {}

  /**
   * Handle an error event.
   * Default does nothing.
   *
   * @param {object} _event The error event.
   */
  onerror(_event) {}

  /**
   * Handle an abort event.
   * Default does nothing.
   *
   * @param {object} _event The abort event.
   */
  onabort(_event) {}

} // class RawImageLoader