///|
/// Clamp a channel count to the minimum usable value.
fn min_channels(value : Int) -> Int {
  if value < 1 {
    1
  } else {
    value
  }
}

///|
/// Clamp a sample rate to the minimum supported voice-capture rate.
fn min_sample_rate(value : Int) -> Int {
  if value < 8_000 {
    8_000
  } else {
    value
  }
}

///|
/// Create a capture configuration.
///
/// Every argument has a conservative default: mono, 48 kHz, floating-point
/// audio with voice-processing flags disabled. Callers can override only the
/// fields they care about, then call `normalized` before sizing buffers or
/// opening a native capture stream. This keeps common code short while still
/// making platform-sensitive options explicit at the call site.
///
/// # Example
/// ```mbt check
/// test "CaptureConfig::new applies conservative defaults" {
///   let config = CaptureConfig::new(sample_rate_hz=44_100)
///   assert_eq(config.channels, 1)
///   assert_eq(config.sample_rate_hz, 44_100)
/// }
/// ```
pub fn CaptureConfig::new(
  channels? : Int = 1,
  sample_rate_hz? : Int = 48_000,
  sample_format? : SampleFormat = F32,
  echo_cancellation? : Bool = false,
  noise_suppression? : Bool = false,
) -> CaptureConfig {
  {
    channels,
    sample_rate_hz,
    sample_format,
    echo_cancellation,
    noise_suppression,
  }
}

///|
/// Return the number of bytes occupied by one sample.
///
/// This is useful when translating frame counts into byte sizes for native
/// buffers. `I16` and `U16` occupy two bytes per channel, while `F32` occupies
/// four bytes per channel.
///
/// # Example
/// ```mbt check
/// test "bytes_per_sample reflects the format width" {
///   assert_eq(SampleFormat::I16.bytes_per_sample(), 2)
///   assert_eq(SampleFormat::F32.bytes_per_sample(), 4)
/// }
/// ```
pub fn SampleFormat::bytes_per_sample(self : SampleFormat) -> Int {
  match self {
    I16 => 2
    U16 => 2
    F32 => 4
  }
}

///|
/// Normalize a capture configuration into safe runtime bounds.
///
/// A configuration with fewer than one channel is clamped to mono, and a sample
/// rate below 8 kHz is clamped to 8 kHz. The sample format and
/// voice-processing flags are preserved so callers do not lose intent when
/// normalizing user-provided settings.
///
/// # Example
/// ```mbt check
/// test "normalized clamps channels and sample rate to safe bounds" {
///   let config = CaptureConfig::new(channels=0, sample_rate_hz=4_000).normalized()
///   assert_eq(config.channels, 1)
///   assert_eq(config.sample_rate_hz, 8_000)
/// }
/// ```
pub fn CaptureConfig::normalized(self : CaptureConfig) -> CaptureConfig {
  {
    channels: min_channels(self.channels),
    sample_rate_hz: min_sample_rate(self.sample_rate_hz),
    sample_format: self.sample_format,
    echo_cancellation: self.echo_cancellation,
    noise_suppression: self.noise_suppression,
  }
}

///|
/// Return the preferred frame count for one responsive capture chunk.
///
/// The recommendation is ten milliseconds of audio after normalization. For
/// example, 48 kHz audio yields 480 frames, while an invalid 4 kHz input first
/// normalizes to 8 kHz and then yields 80 frames.
///
/// # Example
/// ```mbt check
/// test "recommended_chunk_frames is ten milliseconds of audio" {
///   assert_eq(
///     CaptureConfig::new(sample_rate_hz=48_000).recommended_chunk_frames(),
///     480,
///   )
/// }
/// ```
pub fn CaptureConfig::recommended_chunk_frames(self : CaptureConfig) -> Int {
  self.normalized().sample_rate_hz / 100
}

///|
/// Whether a config requests voice-processing behavior.
///
/// This helper only checks caller intent. It does not promise that the current
/// operating system or selected device can actually provide echo cancellation
/// or noise suppression.
///
/// # Example
/// ```mbt check
/// test "uses_voice_processing reflects requested flags" {
///   assert_false(CaptureConfig::new().uses_voice_processing())
///   assert_true(
///     CaptureConfig::new(noise_suppression=true).uses_voice_processing(),
///   )
/// }
/// ```
pub fn CaptureConfig::uses_voice_processing(self : CaptureConfig) -> Bool {
  self.echo_cancellation || self.noise_suppression
}