///|
/// Multichannel output on the same timeline; no automatic peak normalization.
pub struct Audio {
  priv samples : Array[Array[Double]]
  sample_rate : Int32
  latency_samples : Int32
}

///|
/// Uses the existing MoonSpectrum FFT. Returns the complete linear-convolution tail.
pub fn convolve(
  response : Response,
  dry : Array[Double],
) -> Result[Audio, String] {
  convolve_channels(
    response.samples,
    response.scene.sample_rate,
    response.scene.half_width,
    dry,
  )
}

///|
/// Apply an exported/reference RIR without changing its timeline convention.
pub fn Audio::convolve(
  self : Audio,
  dry : Array[Double],
) -> Result[Audio, String] {
  convolve_channels(self.samples, self.sample_rate, self.latency_samples, dry)
}

///|
fn convolve_channels(
  responses : Array[Array[Double]],
  sample_rate : Int32,
  latency_samples : Int32,
  dry : Array[Double],
) -> Result[Audio, String] {
  if dry.is_empty() || dry.length() > 262144 {
    return Err("dry signal must contain 1..262144 samples")
  }
  for v in dry {
    if !(v >= -1.0e6 && v <= 1.0e6) {
      return Err("dry samples must be finite and abs <= 1000000")
    }
  }
  let length = dry.length() + responses[0].length() - 1
  if length > 262144 || length * responses.length() > 2000000 {
    return Err("convolution output budget exceeded")
  }
  let fft_length = @spectrum.next_power_of_two(length) catch {
    _ => return Err("FFT length selection failed")
  }
  let input = Array::make(fft_length, @spectrum.Complex::zero())
  for i in 0.. return Err("FFT source transform failed")
  }
  let channels : Array[Array[Double]] = []
  for h in responses {
    let kernel = Array::make(fft_length, @spectrum.Complex::zero())
    for i in 0.. return Err("FFT kernel transform failed")
    }
    for i in 0.. return Err("FFT inverse transform failed")
    }
    channels.push(Array::makei(length, fn(i) { time_domain[i].re }))
  }
  Ok({ samples: channels, sample_rate, latency_samples, })
}

///|
pub fn Audio::channel(
  self : Audio,
  index : Int32,
) -> Result[Array[Double], String] {
  if index < 0 || index >= self.samples.length() {
    Err("audio channel index out of range")
  } else {
    Ok(self.samples[index].copy())
  }
}

///|
pub fn Audio::channel_count(self : Audio) -> Int32 {
  self.samples.length()
}

///|
pub fn Audio::frames(self : Audio) -> Int32 {
  self.samples[0].length()
}