const Bitcrush = ({ input, output, bitDepth, targetSampleRate, gain }) => {
  const lastHeldVal = useVal(0);
  const samplesHeld = useVal(0);
  
  const gained = useMonoSignal(input.length);
  multiply(input, gain, gained);
  clamp(gained, gained, -1, 1); 

  for (let i = 0; i < input.length; i ++) {
    samplesHeld.current++
    const segment = (sampleRate / targetSampleRate[i])
    if (samplesHeld.current >= segment) {
      const step = 2 * Math.pow(0.5, bitDepth[i]);
      const val = step * ~~(gained[i] / step);
      lastHeldVal.current = val
      samplesHeld.current -= segment
    }
    output[i] = lastHeldVal.current;
  }
}

const Visualizer = (ctx, { bitDepth, targetSampleRate }) => {
  ctx.fillStyle = gray200;
  const width = ctx.canvas.width;
  const height = ctx.canvas.height;
  ctx.fillRect(0, 0, width, height);
  ctx.lineWidth = 4
  ctx.beginPath();
  ctx.moveTo(0, height/2);
  let heldValue = 0;
  let sampleRateRatio = 40000 / targetSampleRate;
  let sampledIndex = 0;
  let crossoverX = width/2;
  for (let x = 0; x < width; x ++) {
    sampledIndex ++;
    if (sampledIndex >= sampleRateRatio) {
      sampledIndex -= sampleRateRatio
      const originalValue = Math.sin((x / (width - 1)) * Math.PI * 2);
      const step = 2 * Math.pow(0.5, bitDepth);
      const newValue = step * ~~(originalValue / step);
      if (newValue < 0 && heldValue >= 0) {
        crossoverX = x;
      }
      heldValue = newValue;
    }
    ctx.lineTo(x, (height/2) + (heldValue * ((height/2) - 5)));
  }
  ctx.lineTo(width, height/2);
  ctx.lineTo(0, height/2);
  // ctx.stroke();
  ctx.fillStyle = blue400;
  ctx.fill();
  ctx.save();
  ctx.clip();
  ctx.fillStyle = blue500;
  ctx.fillRect(0, 0, width, height/2);
  ctx.restore();
  ctx.strokeStyle = blue400;
  ctx.beginPath();
  ctx.moveTo(0, height/2);
  ctx.lineTo(crossoverX, height/2);
  ctx.stroke();
  ctx.strokeStyle = blue500;
  ctx.beginPath();
  ctx.moveTo(crossoverX, height/2);
  ctx.lineTo(width, height/2);
  ctx.stroke();
}

this.constructDevice = () => constructPerChannelDevice({
  implementation: Bitcrush,
  inputs: {
    input: stereo('In'),
    gain: dbSetting('Gain', { defaultValue: 0, min: -24, max: 24 }),
    bitDepth: numberSetting('Bit Depth', { defaultValue: 6, min: 1, max: 16, curve: Curve.Log2 }),
    targetSampleRate: numberSetting('Sample Rate', { curve: Curve.Log2, defaultValue: 8000, min: 20, max: 40000 }),
  },
  outputs: {
    output: stereo('Out'),
  },
  layout: themed(
    DeviceLayoutTheme.Skeuomorphic,
    col([
      canvas(
        'visualizer',
        Visualizer,
        300,
        150
      ),
      row([
        spacer(5),
        titled.knob('gain', 'Gain', 50),
        spacer(90),
        titled.knob('bitDepth', 'Bits', 50),
        titled.knob('targetSampleRate', 'Samples', 50),
        spacer(5),
      ])
    ])
  )
});