import {
  hzToZeroOne,
  multiplierToDb,
} from '@suno/studiokit/audioEngineeringUtils';

export const getSpectrumFrequency = (
  index: number,
  fftSize: number,
  sampleRate: number
) => {
  return ((index / (fftSize / 2)) * (sampleRate / 2)) / 2;
};

const traceSpectrum = (
  ctx: CanvasRenderingContext2D,
  analysis: Float32Array,
  fftSize: number,
  sampleRate: number,
  width: number,
  height: number,
  mode = 'line'
) => {
  let lastX = 0;
  let lastY = height;
  let skippedDb = -Infinity;
  let hadLoudBin = false;
  for (let i = 0; i < analysis.length; i++) {
    if (analysis[i] > 0.01) {
      hadLoudBin = true;
    }
  }
  if (!hadLoudBin) {
    return;
  }

  for (let i = 0; i < analysis.length; i++) {
    const f = getSpectrumFrequency(i, fftSize, sampleRate);
    const xProportion = hzToZeroOne(f);
    const x = xProportion * width;
    const octave = Math.log2(f) - 10;
    const db = Math.max(skippedDb, multiplierToDb(analysis[i]) + octave * 3);
    if (x - lastX < 1) {
      skippedDb = db;
      continue;
    }
    skippedDb = -Infinity;
    const value = 1 - db / -90 - 0.6;
    const verticalScale = 2.0;
    const y = (1 - value * verticalScale) * height;
    if (i === 0) {
      ctx.moveTo(x, y);
    } else if (mode === 'line') {
      ctx.lineTo(x, y);
    } else if (mode === 'quadratic') {
      ctx.quadraticCurveTo((lastX + x) / 2, y > lastY ? lastY : y, x, y);
    } else if (mode === 'bezier') {
      ctx.bezierCurveTo((lastX + x) / 2, lastY, (lastX + x) / 2, y, x, y);
    }
    lastX = x;
    lastY = y;
  }
};

export default traceSpectrum;
