///|
pub(all) struct Keyframe {
  at : Int32
  direction : Direction
}

///|
pub(all) struct SceneResult {
  audio : Stereo
  peak : Double
  clipped_samples : Int32
}

///|
fn append_audio(target : Stereo, source : Stereo) -> Unit {
  for x in source.left {
    target.left.push(x)
  }
  for x in source.right {
    target.right.push(x)
  }
}

// A bounded offline scene renderer. Directions are piecewise measured nearest
// neighbours; filter changes crossfade, not interpolation of unmeasured HRTFs.

///|
pub fn render_scene(
  input : Array[Double],
  measurements : Array[Measurement],
  keyframes : Array[Keyframe],
  sample_rate : Int32,
  hrir_rate : Int32,
  fade_samples : Int32,
) -> SceneResult raise AudioError {
  if sample_rate < 1 ||
    sample_rate > 192000 ||
    sample_rate != hrir_rate ||
    input.is_empty() ||
    input.length() > 240000 ||
    keyframes.is_empty() ||
    keyframes.length() > 512 ||
    keyframes[0].at != 0 ||
    fade_samples < 1 ||
    fade_samples > 480000 ||
    measurements.is_empty() ||
    measurements.length() > 4096 {
    raise InvalidAudio
  }
  let taps = measurements[0].hrir.left.length()
  if input.length().to_int64() * taps.to_int64() > 64000000L {
    raise InvalidAudio
  }
  for m in measurements {
    if m.hrir.left.length() != taps {
      raise InvalidAudio
    }
    ignore(normalized(m.direction))
  }
  for x in input {
    if !finite(x) || x.abs() > 1.0e6 {
      raise InvalidAudio
    }
  }
  for i in 0..= input.length() {
      raise InvalidDirection
    }
    if i > 0 && f.at <= keyframes[i - 1].at {
      raise InvalidDirection
    }
    if i > 1 && f.at - keyframes[i - 1].at < fade_samples {
      raise TransitionBusy
    }
  }
  let renderer = Renderer::new(select(measurements, keyframes[0].direction))
  let output : Stereo = { left: [], right: [], }
  for f in 0.. 0 {
      renderer.transition(
        select(measurements, keyframes[f].direction),
        fade_samples,
      )
    }
    let end = if f + 1 < keyframes.length() {
      keyframes[f + 1].at
    } else {
      input.length()
    }
    let mut start = keyframes[f].at
    while start < end {
      let stop = if start + 1024 < end { start + 1024 } else { end }
      let block : Array[Double] = []
      for i in start.. peak {
        peak = x.abs()
      }
      if x.abs() > 1.0 {
        clipped += 1
      }
    }
  }
  { audio: output, peak, clipped_samples: clipped, }
}