import {ThreadPool, WorkerTask} from '../utils/thread.js';

/**
 * List of compatible typed arrays.
 *
 * @typedef {(
 *   Uint8Array | Int8Array |
 *   Uint16Array | Int16Array |
 *   Uint32Array | Int32Array
 * )} TypedArray
 */

/**
 * Jpeg baseline worker task.
 */
class JpegBaselineWorkerTask extends WorkerTask {
  constructor(message, info) {
    super(message, info);
  }
  getWorker() {
    return new Worker(
      new URL('../decoders/pdfjs/jpegbaseline.worker.js', import.meta.url),
      {
        name: 'jpegbaseline.worker'
      }
    );
  }
}

/**
 * Jpeg lossless worker task.
 */
class JpegLosslessWorkerTask extends WorkerTask {
  constructor(message, info) {
    super(message, info);
  }
  getWorker() {
    return new Worker(
      new URL('../decoders/rii-mango/jpegloss.worker.js', import.meta.url),
      {
        name: 'jpegloss.worker'
      }
    );
  }
}

/**
 * Jpeg 2000 worker task.
 */
class Jpeg2000WorkerTask extends WorkerTask {
  constructor(message, info) {
    super(message, info);
  }
  getWorker() {
    return new Worker(
      new URL('../decoders/pdfjs/jpeg2000.worker.js', import.meta.url),
      {
        name: 'jpeg2000.worker'
      }
    );
  }
}

/**
 * RLE worker task.
 */
class RleWorkerTask extends WorkerTask {
  constructor(message, info) {
    super(message, info);
  }
  getWorker() {
    return new Worker(
      new URL('../decoders/dwv/rle.worker.js', import.meta.url),
      {
        name: 'rle.worker'
      }
    );
  }
}

/**
 * Pixel buffer decoder.
 */
export class PixelBufferDecoder {

  /**
   * The name of the compression algorithm.
   *
   * @type {string}
   */
  #algoName;

  /**
   * Associated thread pool.
   *
   * @type {ThreadPool}
   */
  #pool;

  /**
   * Flag to know if callbacks are set.
   *
   * @type {boolean}
   */
  #areCallbacksSet = false;

  /**
   * @param {string} algoName The compression algorithm name.
   */
  constructor(algoName) {
    this.#algoName = algoName;
    // create pool
    this.#pool = new ThreadPool(10);
  }

  /**
   * Decode a pixel buffer.
   *
   * @param {TypedArray} pixelBuffer The pixel buffer.
   * @param {object} pixelMeta The input meta data.
   * @param {object} info Information object about the input data.
   */
  decode(pixelBuffer, pixelMeta, info) {
    // wait until last minute to set callbacks
    if (!this.#areCallbacksSet) {
      this.#areCallbacksSet = true;
      // set event handlers
      this.#pool.onworkstart = this.ondecodestart;
      this.#pool.onworkitem = this.ondecodeditem;
      this.#pool.onwork = this.ondecoded;
      this.#pool.onworkend = this.ondecodeend;
      this.#pool.onerror = this.onerror;
      this.#pool.onabort = this.onabort;
    }

    const message = {
      buffer: pixelBuffer,
      meta: pixelMeta
    };

    let workerTask;
    if (this.#algoName === 'jpeg-baseline') {
      workerTask = new JpegBaselineWorkerTask(message, info);
    } else if (this.#algoName === 'jpeg-lossless') {
      workerTask = new JpegLosslessWorkerTask(message, info);
    } else if (this.#algoName === 'jpeg2000') {
      workerTask = new Jpeg2000WorkerTask(message, info);
    } else if (this.#algoName === 'rle') {
      workerTask = new RleWorkerTask(message, info);
    }

    // add it the queue and run it
    if (typeof workerTask !== 'undefined') {
      this.#pool.addWorkerTask(workerTask);
    }
  }

  /**
   * Abort decoding.
   */
  abort() {
    // abort the thread pool, will trigger pool.onabort
    this.#pool.abort();
  }

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

  /**
   * Handle a decode item event.
   * Default does nothing.
   *
   * @param {object} _event The decode item event fired
   *   when a decode item ended successfully.
   */
  ondecodeditem(_event) {}

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

  /**
   * Handle a decode end event.
   * Default does nothing.
   *
   * @param {object} _event The decode end event fired
   *  when a file decoding has completed, successfully or not.
   */
  ondecodeend(_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 PixelBufferDecoder
