///|
/// Errors returned for invalid configuration, samples, resources or stream state.
pub(all) enum ResampleError {
  InvalidRate(Int)
  UnsupportedRatio(Int, Int)
  InvalidChannels(Int)
  MisalignedInput
  MisalignedOutput
  NonFinite(Int)
  InvalidState
  FrameOverflow
  NumericOverflow
  ResourceLimit
} derive(Debug, Eq)

///|
/// Validated immutable configuration for one interleaved PCM stream.
pub struct Config {
  priv input_rate : Int
  priv output_rate : Int
  priv channels : Int
  priv numerator : Int
  priv denominator : Int
  priv quality : Quality
} derive(Debug, Eq)

///|
/// Accept fixed rates 8000..192000 Hz and 1..8 channels, using Balanced quality.
/// Exact phase/coefficient budgets are checked before construction allocates.
pub fn Config::new(
  input_rate : Int,
  output_rate : Int,
  channels : Int,
) -> Result[Config, ResampleError] {
  Config::with_quality(input_rate, output_rate, channels, Balanced)
}

///|
/// Quality presets with increasing filter support and distinct cutoff rolloffs.
/// Balanced retains the 0.1.0 behavior for every previously supported pair.
pub(all) enum Quality {
  LowLatency
  Balanced
  HighQuality
} derive(Debug, Eq)

///|
pub extend Quality with @debug.Debug::{to_repr}

///|
pub extend Quality with Eq::{equal, not_equal}

///|
fn Quality::base_radius(self : Quality) -> Int {
  match self {
    LowLatency => 32
    Balanced => 96
    HighQuality => 192
  }
}

///|
/// Validate the full configuration without allocation. At most 4096 exact phases
/// and 1048576 coefficient Doubles. Extreme co-prime pairs return ResourceLimit.
pub fn Config::with_quality(
  input_rate : Int,
  output_rate : Int,
  channels : Int,
  quality : Quality,
) -> Result[Config, ResampleError] {
  if input_rate < 8000 || input_rate > 192000 {
    return Err(InvalidRate(input_rate))
  }
  if output_rate < 8000 || output_rate > 192000 {
    return Err(InvalidRate(output_rate))
  }
  if channels < 1 || channels > 8 {
    return Err(InvalidChannels(channels))
  }
  let divisor = gcd(input_rate, output_rate)
  let numerator = output_rate / divisor
  let scale = ((input_rate + 3 * output_rate - 1) / (3 * output_rate)).max(1)
  let radius = if input_rate == output_rate {
    0
  } else {
    quality.base_radius() * scale
  }
  if numerator > 4096 || numerator * (2 * radius + 1) > 1048576 {
    return Err(ResourceLimit)
  }
  Ok({
    input_rate,
    output_rate,
    channels,
    numerator,
    denominator: input_rate / divisor,
    quality,
  })
}

///|
/// Selected immutable quality preset.
pub fn Config::quality(self : Config) -> Quality {
  self.quality
}

///|
/// Input sample rate, in complete frames per second.
pub fn Config::input_rate(self : Config) -> Int {
  self.input_rate
}

///|
/// Output sample rate, in complete frames per second.
pub fn Config::output_rate(self : Config) -> Int {
  self.output_rate
}

///|
/// Number of interleaved samples in each frame.
pub fn Config::channels(self : Config) -> Int {
  self.channels
}

///|
pub extend ResampleError with @debug.Debug::{to_repr}

///|
pub extend ResampleError with Eq::{equal, not_equal}

///|
pub extend Config with @debug.Debug::{to_repr}

///|
pub extend Config with Eq::{equal, not_equal}