import { applyMatrixOnPath, pathToVectorPath } from './vectorUtils.js';
import { Matrix } from 'cal';
import TraceWorker from '../../workers/trace.worker.js';
import { getPixel } from './colorUtils.js';
import memoize from 'memoizee';

const TRACE_WORKER = new TraceWorker();

export const prepareImageData = memoize(prepareImageDataRaw, { max: 1 });
function prepareImageDataRaw(canvas, x, y) {
  const { width, height } = canvas;
  const imageData = canvas.getContext('2d').getImageData(0, 0, width, height);

  const compairValue = getPixel(imageData, y * width + x);

  const data = new Uint8Array(width * height);
  for (let i = 0; i < data.length; i ++) {
    const pixel = getPixel(imageData, i);
    const r = Math.abs(pixel.r - compairValue.r);
    const g = Math.abs(pixel.g - compairValue.g);
    const b = Math.abs(pixel.b - compairValue.b);

    const value = Math.max(r, g, b);

    data[i] = value;
  }

  return { width, height, data };
}

const FLOOD_FILL_MULTIPLIER = 0.4;
export function calculateTolerance(start, current) {
  const tolerance = start.distanceTo(current) * FLOOD_FILL_MULTIPLIER;

  return tolerance;
}

export function floodFill(tolerance, image, start) {
  const imageData = prepareImageData(image, start.x, start.y);

  return new Promise((resolve, reject) => {
    const callback = ({ data }) => {
      switch (data.msg) {
        case 'FLOOD_FILL':
          TRACE_WORKER.removeEventListener('message', callback);

          if (data.status === 'OK') {
            resolve(data.traceData);
          } else {
            reject(data.error);
          }
          break;

        default:
          break;
      }
    };
    TRACE_WORKER.addEventListener('message', callback);

    TRACE_WORKER.postMessage({
      msg: 'FLOOD_FILL',
      tolerance, imageData, start
    });
  });
}

export function traceFloodFill(traceData, shapeData) {
  const { fill } = traceData;
  const { width, height } = shapeData.imageData;

  return new Promise((resolve, reject) => {
    const callback = ({ data }) => {
      switch (data.msg) {
        case 'TRACE':
          TRACE_WORKER.removeEventListener('message', callback);

          const transform = new Matrix()
            .translate(width / -2, height / -2)
            .multiplyMatrix(shapeData.transform);

          const paths = data.paths
            .map(pathToVectorPath)
            .map(path => applyMatrixOnPath(path, transform));

          if (data.status === 'OK') {
            resolve(paths);
          } else {
            reject(data.error);
          }
          break;

        default:
          break;
      }
    };
    TRACE_WORKER.addEventListener('message', callback);

    TRACE_WORKER.postMessage({
      msg: 'TRACE',
      fill, width, height
    });
  });
}
