global.THREE = require("three");

const canvasSketch = require('canvas-sketch');
const Random = require('canvas-sketch-util/random');

const gradientHeight = 512;
const settings = {
  dimensions: [ 2048, gradientHeight * 2 ]
};

const sketch = (props) => {
  return ({ context, width, height }) => {
    context.fillStyle = 'white';
    context.fillRect(0, 0, width, height);

    // your stepped color data, to be filled in
    const colorsAsHexList = [ /* '#ff0000' */ ];
    const colorsAsLabList = [ /* [ 100, 0, 0 ] */ ];

    const grd = context.createLinearGradient(0, 0, width, 0);
    colorsAsHexList.forEach((hex, i, list) => {
      const t = i / (list.length - 1);
      grd.addColorStop(t, hex);
    });
    context.fillStyle = grd;
    context.fillRect(0, 0, width, gradientHeight);

    img = context.createImageData(width, gradientHeight);
    // draw curve
    const labPositions = colorsAsLabList.map(lab => {
      return new THREE.Vector3().fromArray(lab);
    });
    const curve = new THREE.CatmullRomCurve3(labPositions);
    curve.curveType = 'catmullrom';

    // can play with tension to make sharper or softer gradients
    curve.tension = 0.5;

    // uncomment to make a seamless gradient that wraps around
    // curve.closed = true;

    const samples = getCurveDivisions(curve, img.width, false)
      .map(p => p.toArray())
    for (let y = 0; y < img.height; y++) {
      for (let x = 0; x < img.width; x++) {
        const i = x + y * img.width;
        let Lab = labJitter(samples[x], 0.5);
        // lab2rgb not implemented here
        const [ R, G, B ] = YourColorConverter.lab2rgb(Lab);
        img.data[i * 4 + 0] = R;
        img.data[i * 4 + 1] = G;
        img.data[i * 4 + 2] = B;
        img.data[i * 4 + 3] = 255;
      }
    }
    context.putImageData(img, 0, gradientHeight);
  };

  // Entirely optional, but as we are doing this per-pixel we may as well
  // add subtle noise to reduce banding.
  function labJitter (lab, s = 1) {
    // fudged jittering in L*a*b* space
    const lw = 100 / 200;
    const K = 2.624;
    const [ x, y, z ] = Random.insideSphere(Random.gaussian(0, s * K))
    const [ L, a, b ] = lab;
    return [ L + x * lw, a + y, b + z ];
  }

  function getCurveDivisions (curve, n, spaced = false) {
    const points = [];
    for (let i = 0; i < n; i++) {
      const t = curve.closed ? (i / n) : (i / (n - 1));
      let p = spaced ? curve.getPointAt(t) : curve.getPoint(t);
      points.push(p);
    }
    return points;
  }
};

canvasSketch(sketch, settings);