// Small FFT helpers used by the FFT and waterfall (spectrogram) plot types.
// Scalar live signals are irregularly sampled, so callers first resample onto a
// uniform grid, then take the magnitude spectrum.

function nextPow2(n: number): number {
  let p = 1;
  while (p < n) p <<= 1;
  return p;
}

// In-place iterative Cooley-Tukey FFT. Array length must be a power of two.
function transform(re: number[], im: number[]): void {
  const n = re.length;
  if (n <= 1) return;

  // Bit-reversal permutation.
  for (let i = 1, j = 0; i < n; i++) {
    let bit = n >> 1;
    for (; j & bit; bit >>= 1) j ^= bit;
    j ^= bit;
    if (i < j) {
      const tr = re[i]; re[i] = re[j]; re[j] = tr;
      const ti = im[i]; im[i] = im[j]; im[j] = ti;
    }
  }

  for (let len = 2; len <= n; len <<= 1) {
    const ang = -2 * Math.PI / len;
    const wr = Math.cos(ang), wi = Math.sin(ang);
    for (let i = 0; i < n; i += len) {
      let cr = 1, ci = 0;
      for (let k = 0; k < len >> 1; k++) {
        const a = i + k, b = i + k + (len >> 1);
        const tr = re[b] * cr - im[b] * ci;
        const ti = re[b] * ci + im[b] * cr;
        re[b] = re[a] - tr; im[b] = im[a] - ti;
        re[a] += tr;        im[a] += ti;
        const ncr = cr * wr - ci * wi;
        ci = cr * wi + ci * wr;
        cr = ncr;
      }
    }
  }
}

// Magnitude spectrum (first half, single-sided) of a uniformly sampled signal.
// DC is removed and a Hann window applied to reduce spectral leakage.
export function fftMag(values: number[]): number[] {
  const len = values.length;
  if (len < 2) return [];
  const n = nextPow2(len);
  const re = new Array<number>(n).fill(0);
  const im = new Array<number>(n).fill(0);
  let mean = 0;
  for (let i = 0; i < len; i++) mean += values[i];
  mean /= len;
  for (let i = 0; i < len; i++) {
    const w = 0.5 - 0.5 * Math.cos((2 * Math.PI * i) / (len - 1));
    re[i] = (values[i] - mean) * w;
  }
  transform(re, im);
  const half = n >> 1;
  const mag = new Array<number>(half);
  for (let k = 0; k < half; k++) mag[k] = Math.hypot(re[k], im[k]) / half;
  return mag;
}

// Linearly resample (ts, vals) onto n evenly spaced points spanning the data.
// Returns the sampled series and the uniform sample interval dt (seconds).
export function resampleUniform(ts: number[], vals: number[], n: number): { sampled: number[]; dt: number } {
  if (ts.length < 2) return { sampled: vals.slice(0, n), dt: 0 };
  const t0 = ts[0];
  const t1 = ts[ts.length - 1];
  const dt = (t1 - t0) / (n - 1);
  const sampled = new Array<number>(n);
  let j = 0;
  for (let i = 0; i < n; i++) {
    const t = t0 + i * dt;
    while (j < ts.length - 1 && ts[j + 1] <= t) j++;
    if (j >= ts.length - 1) {
      sampled[i] = vals[ts.length - 1];
    } else {
      const span = ts[j + 1] - ts[j];
      const frac = span > 0 ? (t - ts[j]) / span : 0;
      sampled[i] = vals[j] + (vals[j + 1] - vals[j]) * frac;
    }
  }
  return { sampled, dt };
}
