///| Channel ordering accepted by the analyzer.

///|
/// FivePointZero uses L, R, C, Ls, Rs order; it has no LFE channel.
pub enum ChannelLayout {
  Mono
  Stereo
  FivePointZero
}

///|
/// Returns the layouts implemented by this release.
pub fn ChannelLayout::supported() -> Array[ChannelLayout] {
  [Mono, Stereo, FivePointZero]
}

///|
/// Returns the canonical mono layout.
pub fn ChannelLayout::mono() -> ChannelLayout {
  Mono
}

///|
/// Returns the canonical left-right stereo layout.
pub fn ChannelLayout::stereo() -> ChannelLayout {
  Stereo
}

///|
/// Returns the canonical 5.0 layout in L,R,C,Ls,Rs order.
pub fn ChannelLayout::five_point_zero() -> ChannelLayout {
  FivePointZero
}

///|
/// Returns the canonical short name for a channel index in this layout.
pub fn ChannelLayout::channel_label(
  self : ChannelLayout,
  index : Int32,
) -> String? {
  match (self, index) {
    (Mono, 0) => Some("C")
    (Stereo, 0) => Some("L")
    (Stereo, 1) => Some("R")
    (FivePointZero, 0) => Some("L")
    (FivePointZero, 1) => Some("R")
    (FivePointZero, 2) => Some("C")
    (FivePointZero, 3) => Some("Ls")
    (FivePointZero, 4) => Some("Rs")
    _ => None
  }
}

///|
/// Returns the BS.1770 channel multiplier, or None for an invalid index.
pub fn ChannelLayout::channel_weight(
  self : ChannelLayout,
  index : Int32,
) -> Double? {
  match (self, index) {
    (Mono, 0) | (Stereo, 0) | (Stereo, 1) => Some(1.0)
    (FivePointZero, 0) | (FivePointZero, 1) | (FivePointZero, 2) => Some(1.0)
    (FivePointZero, 3) | (FivePointZero, 4) => Some(1.41)
    _ => None
  }
}

///|
/// Finds the exact canonical channel label in a layout.
pub fn ChannelLayout::index_of(self : ChannelLayout, label : String) -> Int32? {
  for index in 0.. Int32 {
  match self {
    Mono => 1
    Stereo => 2
    FivePointZero => 5
  }
}

///|
/// Typed configuration and input errors.
pub enum MeterError {
  UnsupportedSampleRate(Int32)
  IncompleteFrame(Int32, Int32)
  NonFiniteSample
  SampleMagnitudeTooLarge(Double)
  CounterOverflow
  InvalidHistoryCapacity(Int32)
  AnalyzerFinished
}

///|
/// Immutable configuration for one 48 kHz PCM stream.
pub struct MeterConfig {
  priv sample_rate : Int32
  priv layout : ChannelLayout
}

///|
/// Creates a configuration. Only the published 48 kHz coefficient set is enabled.
pub fn MeterConfig::new(
  sample_rate : Int32,
  layout : ChannelLayout,
) -> Result[MeterConfig, MeterError] {
  if sample_rate != 48000 {
    Err(UnsupportedSampleRate(sample_rate))
  } else {
    Ok({ sample_rate, layout, })
  }
}

///|
/// Returns the configured sample rate in samples per second.
pub fn MeterConfig::sample_rate(self : MeterConfig) -> Int32 {
  self.sample_rate
}

///|
/// Returns the interleaved channel layout.
pub fn MeterConfig::layout(self : MeterConfig) -> ChannelLayout {
  self.layout
}

///|
/// Allocates independent K-weighting state for every configured channel.
fn new_filter_states(layout : ChannelLayout) -> Array[KWeightingState] {
  let states : Array[KWeightingState] = []
  for _ in 0.. Array[Double] {
  let ring : Array[Double] = []
  for _ in 0.. Array[Array[Double]] {
  [
    [
      0.001708984375, 0.010986328125, -0.0196533203125, 0.033203125, -0.0594482421875,
      0.1373291015625, 0.97216796875, -0.102294921875, 0.047607421875, -0.026611328125,
      0.014892578125, -0.00830078125,
    ],
    [
      -0.0291748046875, 0.029296875, -0.0517578125, 0.089111328125, -0.16650390625,
      0.465087890625, 0.77978515625, -0.2003173828125, 0.1015625, -0.0582275390625,
      0.0330810546875, -0.0189208984375,
    ],
    [
      -0.0189208984375, 0.0330810546875, -0.0582275390625, 0.1015625, -0.2003173828125,
      0.77978515625, 0.465087890625, -0.16650390625, 0.089111328125, -0.0517578125,
      0.029296875, -0.0291748046875,
    ],
    [
      -0.00830078125, 0.014892578125, -0.026611328125, 0.047607421875, -0.102294921875,
      0.97216796875, 0.1373291015625, -0.0594482421875, 0.033203125, -0.0196533203125,
      0.010986328125, 0.001708984375,
    ],
  ]
}

///|
/// Stateful analyzer for interleaved PCM frames at 48 kHz.
pub struct Analyzer {
  priv config : MeterConfig
  priv mut filters : Array[KWeightingState]
  priv mut sample_peak : Double
  priv channel_sample_peaks : Array[Double]
  priv peak_coefficients : Array[Array[Double]]
  priv mut true_peak_ring : Array[Array[Double]]
  priv mut true_peak_cursor : Int32
  priv mut true_peak_max : Double
  priv mut sample_count : Int32
  priv mut frame_count : Int32
  priv momentary_window : EnergyWindow
  priv short_window : EnergyWindow
  priv mut latest_momentary_energy : Double?
  priv mut latest_short_energy : Double?
  priv integrated_distribution : LoudnessHistogram
  priv short_distribution : LoudnessHistogram
  priv mut finished : Bool
  priv mut cached_report : MeasurementReport?
  priv history : MeasurementHistory
}

///|
/// Starts an empty analyzer with separate filter and window history.
pub fn Analyzer::new(config : MeterConfig) -> Analyzer {
  Analyzer::new_internal(config, 600)
}

///|
/// Creates an analyzer with a bounded history capacity (zero disables retention).
pub fn Analyzer::new_with_history_capacity(
  config : MeterConfig,
  capacity : Int32,
) -> Result[Analyzer, MeterError] {
  if capacity < 0 || capacity > 100000 {
    Err(InvalidHistoryCapacity(capacity))
  } else {
    Ok(Analyzer::new_internal(config, capacity))
  }
}

///|
fn Analyzer::new_internal(
  config : MeterConfig,
  history_capacity : Int32,
) -> Analyzer {
  let peak_ring : Array[Array[Double]] = []
  for _ in 0.. Result[Unit, MeterError] {
  self.push_view(samples[:])
}

///|
/// Consumes a borrowed PCM view synchronously without copying or retaining it.
/// Validation remains atomic; the caller must not mutate the view during this call.
pub fn Analyzer::push_view(
  self : Analyzer,
  samples : ArrayView[Double],
) -> Result[Unit, MeterError] {
  if self.finished {
    return Err(AnalyzerFinished)
  }
  let channels = self.config.layout.channels()
  if samples.length() % channels != 0 {
    return Err(IncompleteFrame(samples.length(), channels))
  }
  for sample in samples {
    if sample - sample != 0.0 {
      return Err(NonFiniteSample)
    }
  }
  let input_frames = samples.length() / channels
  if input_frames > 2147483647 - self.frame_count ||
    samples.length() > 2147483647 - self.sample_count {
    return Err(CounterOverflow)
  }
  for sample in samples {
    if abs_value(sample) > 1.0e+150 {
      return Err(SampleMagnitudeTooLarge(sample))
    }
  }
  for frame in 0.. self.sample_peak {
        self.sample_peak = magnitude
      }
      if magnitude > self.channel_sample_peaks[channel] {
        self.channel_sample_peaks[channel] = magnitude
      }
      let weighted = self.filters[channel].process(sample)
      let channel_weight = match self.config.layout.channel_weight(channel) {
        Some(weight) => weight
        None => 0.0
      }
      frame_energy = frame_energy + weighted * weighted * channel_weight
    }
    self.push_true_peak_frame(samples, offset, true)
    self.sample_count = self.sample_count + channels
    self.record_frame_energy(frame_energy)
  }
  Ok(())
}

///|
/// Absolute value without introducing a dependency on a platform-specific intrinsic.
fn abs_value(value : Double) -> Double {
  if value < 0.0 {
    0.0 - value
  } else {
    value
  }
}

///|
/// Adds one frame to the FIR delay line and evaluates its four interpolation phases.
fn Analyzer::push_true_peak_frame(
  self : Analyzer,
  samples : ArrayView[Double],
  offset : Int32,
  has_input : Bool,
) -> Unit {
  let channels = self.config.layout.channels()
  for channel in 0.. self.true_peak_max {
        self.true_peak_max = magnitude
      }
    }
  }
  self.true_peak_cursor = (self.true_peak_cursor + 1) % 12
}

///|
/// Retains window means and samples the two measurement time scales.
fn Analyzer::record_frame_energy(
  self : Analyzer,
  frame_energy : Double,
) -> Unit {
  self.latest_momentary_energy = self.momentary_window.push(frame_energy)
  self.latest_short_energy = self.short_window.push(frame_energy)
  self.frame_count = self.frame_count + 1
  if self.frame_count >= 19200 && (self.frame_count - 19200) % 4800 == 0 {
    match self.latest_momentary_energy {
      Some(energy) => self.integrated_distribution.push_energy(energy)
      None => ()
    }
    self.record_snapshot()
  }
  if self.frame_count >= 144000 && (self.frame_count - 144000) % 4800 == 0 {
    match self.latest_short_energy {
      Some(energy) => self.short_distribution.push_energy(energy)
      None => ()
    }
  }
}

///|
/// Converts weighted mean-square energy to LUFS/LKFS.
fn energy_to_lufs(energy : Double) -> Double {
  if energy <= 0.0 {
    -1000.0
  } else {
    -0.691 + 10.0 * @math.log10(energy)
  }
}

///|
/// Returns the latest complete 400 ms momentary window when available.
pub fn Analyzer::momentary_lufs(self : Analyzer) -> Double? {
  match self.latest_momentary_energy {
    Some(energy) => Some(energy_to_lufs(energy))
    None => None
  }
}

///|
/// Returns the latest complete 3 s short-term window when available.
pub fn Analyzer::short_term_lufs(self : Analyzer) -> Double? {
  match self.latest_short_energy {
    Some(energy) => Some(energy_to_lufs(energy))
    None => None
  }
}

///|
/// Returns the maximum Annex 2 four-phase FIR estimate in linear amplitude.
pub fn Analyzer::true_peak_linear(self : Analyzer) -> Double {
  if self.sample_peak > self.true_peak_max {
    self.sample_peak
  } else {
    self.true_peak_max
  }
}

///|
/// Returns the largest absolute input sample in linear amplitude.
pub fn Analyzer::sample_peak_linear(self : Analyzer) -> Double {
  self.sample_peak
}

///|
/// Returns one channel's maximum absolute input sample, or None for an invalid index.
pub fn Analyzer::channel_sample_peak_linear(
  self : Analyzer,
  channel : Int32,
) -> Double? {
  if channel < 0 || channel >= self.channel_sample_peaks.length() {
    None
  } else {
    Some(self.channel_sample_peaks[channel])
  }
}

///|
/// Returns a detached copy of the channel-ordered sample-peak array.
pub fn Analyzer::channel_sample_peaks_linear(self : Analyzer) -> Array[Double] {
  let result : Array[Double] = []
  for peak in self.channel_sample_peaks {
    result.push(peak)
  }
  result
}

///|
/// Returns the number of accepted interleaved samples.
pub fn Analyzer::sample_count(self : Analyzer) -> Int32 {
  self.sample_count
}

///|
/// Returns the number of accepted frames.
pub fn Analyzer::frame_count(self : Analyzer) -> Int32 {
  self.frame_count
}

///|
/// Returns the analyzed duration based on complete 48 kHz frames.
pub fn Analyzer::duration_seconds(self : Analyzer) -> Double {
  self.frame_count.to_double() / self.config.sample_rate.to_double()
}