import { TypedArrayWithDimensions } from './geotiff.js';

export function fromWhiteIsZero(
  raster: TypedArrayWithDimensions,
  max: number
): Uint8Array {
  const { width, height } = raster;
  const rgbRaster = new Uint8Array(width * height * 3);
  let value: number;
  for (let i = 0, j = 0; i < raster.length; ++i, j += 3) {
    value = 256 - (raster[i] / max) * 256;
    rgbRaster[j] = value;
    rgbRaster[j + 1] = value;
    rgbRaster[j + 2] = value;
  }
  return rgbRaster;
}

export function fromBlackIsZero(
  raster: TypedArrayWithDimensions,
  max: number
): Uint8Array {
  const { width, height } = raster;
  const rgbRaster = new Uint8Array(width * height * 3);
  let value: number;
  for (let i = 0, j = 0; i < raster.length; ++i, j += 3) {
    value = (raster[i] / max) * 256;
    rgbRaster[j] = value;
    rgbRaster[j + 1] = value;
    rgbRaster[j + 2] = value;
  }
  return rgbRaster;
}

export function fromPalette(
  raster: TypedArrayWithDimensions,
  colorMap: Uint16Array
): Uint8Array {
  const { width, height } = raster;
  const rgbRaster = new Uint8Array(width * height * 3);
  const greenOffset = colorMap.length / 3;
  const blueOffset = (colorMap.length / 3) * 2;
  for (let i = 0, j = 0; i < raster.length; ++i, j += 3) {
    const mapIndex = raster[i];
    rgbRaster[j] = (colorMap[mapIndex] / 65536) * 256;
    rgbRaster[j + 1] = (colorMap[mapIndex + greenOffset] / 65536) * 256;
    rgbRaster[j + 2] = (colorMap[mapIndex + blueOffset] / 65536) * 256;
  }
  return rgbRaster;
}

export function fromCMYK(cmykRaster: TypedArrayWithDimensions): Uint8Array {
  const { width, height } = cmykRaster;
  const rgbRaster = new Uint8Array(width * height * 3);
  for (let i = 0, j = 0; i < cmykRaster.length; i += 4, j += 3) {
    const c = cmykRaster[i];
    const m = cmykRaster[i + 1];
    const y = cmykRaster[i + 2];
    const k = cmykRaster[i + 3];

    rgbRaster[j] = 255 * ((255 - c) / 256) * ((255 - k) / 256);
    rgbRaster[j + 1] = 255 * ((255 - m) / 256) * ((255 - k) / 256);
    rgbRaster[j + 2] = 255 * ((255 - y) / 256) * ((255 - k) / 256);
  }
  return rgbRaster;
}

export function fromYCbCr(
  yCbCrRaster: TypedArrayWithDimensions
): Uint8ClampedArray {
  const { width, height } = yCbCrRaster;
  const rgbRaster = new Uint8ClampedArray(width * height * 3);
  for (let i = 0, j = 0; i < yCbCrRaster.length; i += 3, j += 3) {
    const y = yCbCrRaster[i];
    const cb = yCbCrRaster[i + 1];
    const cr = yCbCrRaster[i + 2];

    rgbRaster[j] = y + 1.402 * (cr - 0x80);
    rgbRaster[j + 1] = y - 0.34414 * (cb - 0x80) - 0.71414 * (cr - 0x80);
    rgbRaster[j + 2] = y + 1.772 * (cb - 0x80);
  }
  return rgbRaster;
}

const Xn = 0.95047;
const Yn = 1.0;
const Zn = 1.08883;

// from https://github.com/antimatter15/rgb-lab/blob/master/color.js

export function fromCIELab(cieLabRaster: TypedArrayWithDimensions): Uint8Array {
  const { width, height } = cieLabRaster;
  const rgbRaster = new Uint8Array(width * height * 3);

  for (let i = 0, j = 0; i < cieLabRaster.length; i += 3, j += 3) {
    const L = cieLabRaster[i + 0];
    const a_ = (cieLabRaster[i + 1] << 24) >> 24; // conversion from uint8 to int8
    const b_ = (cieLabRaster[i + 2] << 24) >> 24; // same

    let y = (L + 16) / 116;
    let x = a_ / 500 + y;
    let z = y - b_ / 200;
    let r: number;
    let g: number;
    let b: number;

    x = Xn * (x * x * x > 0.008856 ? x * x * x : (x - 16 / 116) / 7.787);
    y = Yn * (y * y * y > 0.008856 ? y * y * y : (y - 16 / 116) / 7.787);
    z = Zn * (z * z * z > 0.008856 ? z * z * z : (z - 16 / 116) / 7.787);

    r = x * 3.2406 + y * -1.5372 + z * -0.4986;
    g = x * -0.9689 + y * 1.8758 + z * 0.0415;
    b = x * 0.0557 + y * -0.204 + z * 1.057;

    r = r > 0.0031308 ? 1.055 * r ** (1 / 2.4) - 0.055 : 12.92 * r;
    g = g > 0.0031308 ? 1.055 * g ** (1 / 2.4) - 0.055 : 12.92 * g;
    b = b > 0.0031308 ? 1.055 * b ** (1 / 2.4) - 0.055 : 12.92 * b;

    rgbRaster[j] = Math.max(0, Math.min(1, r)) * 255;
    rgbRaster[j + 1] = Math.max(0, Math.min(1, g)) * 255;
    rgbRaster[j + 2] = Math.max(0, Math.min(1, b)) * 255;
  }
  return rgbRaster;
}
