///|
/// Represents the fundamental wave shapes used for sound synthesis.
#derive(Eq, Debug)
pub(all) enum Waveform {
  Sine
  Square(Double) // Duty cycle, typically 0.5 (50%)
  Sawtooth
  Triangle
  Noise
}

///|
/// A mono linear floating-point audio buffer holding raw PCM samples normalized between -1.0 and 1.0.
#derive(Eq)
pub struct AudioBuffer {
  sample_rate : Int32
  samples : Array[Double]
}

///|
/// Creates a new empty AudioBuffer with specified sample rate and reserved capacity.
pub fn AudioBuffer::new(sample_rate : Int32, _capacity : Int32) -> AudioBuffer {
  { sample_rate, samples: Array::make(0, 0.0), }
}

///|
/// Creates an AudioBuffer prefilled with silent samples (0.0).
pub fn AudioBuffer::with_silence(
  sample_rate : Int32,
  count : Int32,
) -> AudioBuffer {
  let cnt = if count < 0 { 0 } else { count }
  { sample_rate, samples: Array::make(cnt, 0.0), }
}

///|
/// Returns the sample rate of the audio buffer in Hz.
pub fn AudioBuffer::sample_rate(self : AudioBuffer) -> Int32 {
  self.sample_rate
}

///|
/// Returns the raw samples array.
pub fn AudioBuffer::samples(self : AudioBuffer) -> Array[Double] {
  self.samples
}

///|
/// Returns the number of samples in the buffer.
pub fn AudioBuffer::length(self : AudioBuffer) -> Int32 {
  self.samples.length()
}

///|
/// Returns total duration of the buffer in seconds.
pub fn AudioBuffer::duration(self : AudioBuffer) -> Double {
  if self.sample_rate <= 0 {
    0.0
  } else {
    self.samples.length().to_double() / self.sample_rate.to_double()
  }
}

///|
/// Gets sample at index with bounds checking, returning 0.0 if out of bounds.
pub fn AudioBuffer::get(self : AudioBuffer, index : Int32) -> Double {
  if index >= 0 && index < self.samples.length() {
    self.samples[index]
  } else {
    0.0
  }
}

///|
/// Sets sample at index if within bounds.
pub fn AudioBuffer::set(
  self : AudioBuffer,
  index : Int32,
  value : Double,
) -> Unit {
  if index >= 0 && index < self.samples.length() {
    self.samples[index] = value
  }
}

///|
/// Appends a sample to the end of the buffer.
pub fn AudioBuffer::push(self : AudioBuffer, sample : Double) -> Unit {
  self.samples.push(sample)
}

///|
/// Hard clamps all samples to the range [min, max].
pub fn AudioBuffer::clamp(
  self : AudioBuffer,
  min : Double,
  max : Double,
) -> Unit {
  for i = 0; i < self.samples.length(); i = i + 1 {
    let s = self.samples[i]
    if s < min {
      self.samples[i] = min
    } else if s > max {
      self.samples[i] = max
    }
  }
}

///|
/// Normalizes peak amplitude to `target_peak` (usually 0.95 or 1.0) to prevent clipping.
pub fn AudioBuffer::normalize(self : AudioBuffer, target_peak : Double) -> Unit {
  let mut max_abs = 0.0
  for i = 0; i < self.samples.length(); i = i + 1 {
    let a = self.samples[i].abs()
    if a > max_abs {
      max_abs = a
    }
  }
  if max_abs > 0.000001 {
    let scale = target_peak / max_abs
    for i = 0; i < self.samples.length(); i = i + 1 {
      self.samples[i] = self.samples[i] * scale
    }
  }
}

///|
/// Multiplies all samples by a gain factor.
pub fn AudioBuffer::amplify(self : AudioBuffer, gain : Double) -> Unit {
  for i = 0; i < self.samples.length(); i = i + 1 {
    self.samples[i] = self.samples[i] * gain
  }
}

///|
/// Smoothly soft-limits samples exceeding `threshold` using an algebraic saturation curve.
/// Prevents harsh digital clipping when multiple sounds or loud gains are summed together.
pub fn AudioBuffer::soft_limit(self : AudioBuffer, threshold : Double) -> Unit {
  let th = if threshold <= 0.1 {
    0.1
  } else if threshold > 0.95 {
    0.95
  } else {
    threshold
  }
  let headroom = 1.0 - th
  for i = 0; i < self.samples.length(); i = i + 1 {
    let s = self.samples[i]
    if s > th {
      let diff = s - th
      self.samples[i] = th + headroom * (diff / (1.0 + diff))
    } else if s < -th {
      let diff = -s - th
      self.samples[i] = -(th + headroom * (diff / (1.0 + diff)))
    }
  }
}

///|
/// A stereo audio buffer containing separate left and right PCM audio channels.
#derive(Eq)
pub struct StereoBuffer {
  sample_rate : Int32
  left : AudioBuffer
  right : AudioBuffer
}

///|
/// Creates a new StereoBuffer with specified sample rate and capacity.
pub fn StereoBuffer::new(sample_rate : Int32, capacity : Int32) -> StereoBuffer {
  {
    sample_rate,
    left: AudioBuffer::new(sample_rate, capacity),
    right: AudioBuffer::new(sample_rate, capacity),
  }
}

///|
/// Creates a StereoBuffer from two individual mono channels.
pub fn StereoBuffer::from_channels(
  left : AudioBuffer,
  right : AudioBuffer,
) -> StereoBuffer {
  let sr = left.sample_rate
  { sample_rate: sr, left, right, }
}

///|
/// Converts a mono AudioBuffer into a StereoBuffer using the Constant-Power Panning Law.
/// `pan` parameter: -1.0 = 100% Left, 0.0 = Center (balanced), 1.0 = 100% Right.
pub fn AudioBuffer::to_stereo(self : AudioBuffer, pan : Double) -> StereoBuffer {
  let p = if pan < -1.0 { -1.0 } else if pan > 1.0 { 1.0 } else { pan }
  let (gain_left, gain_right) = if p <= -0.9999 {
    (1.0, 0.0)
  } else if p >= 0.9999 {
    (0.0, 1.0)
  } else {
    // Constant-power panning angle: [-1.0, 1.0] -> [0, pi/2]
    let pi_fourth = 3.141592653589793 / 4.0
    let angle = (p + 1.0) * pi_fourth
    (@math.cos(angle), @math.sin(angle))
  }

  let left_samples = Array::make(self.samples.length(), 0.0)
  let right_samples = Array::make(self.samples.length(), 0.0)
  for i = 0; i < self.samples.length(); i = i + 1 {
    let s = self.samples[i]
    left_samples[i] = s * gain_left
    right_samples[i] = s * gain_right
  }

  {
    sample_rate: self.sample_rate,
    left: { sample_rate: self.sample_rate, samples: left_samples, },
    right: { sample_rate: self.sample_rate, samples: right_samples, },
  }
}

///|
/// Convenience helper to create a StereoBuffer from a mono buffer and pan value.
pub fn StereoBuffer::from_mono(
  mono : AudioBuffer,
  pan : Double,
) -> StereoBuffer {
  mono.to_stereo(pan)
}

///|
/// Returns the sample rate of the stereo buffer.
pub fn StereoBuffer::sample_rate(self : StereoBuffer) -> Int32 {
  self.sample_rate
}

///|
/// Returns the left channel AudioBuffer.
pub fn StereoBuffer::left(self : StereoBuffer) -> AudioBuffer {
  self.left
}

///|
/// Returns the right channel AudioBuffer.
pub fn StereoBuffer::right(self : StereoBuffer) -> AudioBuffer {
  self.right
}

///|
/// Returns the number of sample frames in the stereo buffer.
pub fn StereoBuffer::length(self : StereoBuffer) -> Int32 {
  let len_l = self.left.length()
  let len_r = self.right.length()
  if len_l > len_r {
    len_l
  } else {
    len_r
  }
}

///|
/// Returns the duration of the stereo buffer in seconds.
pub fn StereoBuffer::duration(self : StereoBuffer) -> Double {
  self.left.duration()
}

///|
/// Normalizes both channels of the stereo buffer relative to the highest peak.
pub fn StereoBuffer::normalize(
  self : StereoBuffer,
  target_peak : Double,
) -> Unit {
  let mut max_abs = 0.0
  for i = 0; i < self.left.samples.length(); i = i + 1 {
    let a = self.left.samples[i].abs()
    if a > max_abs {
      max_abs = a
    }
  }
  for i = 0; i < self.right.samples.length(); i = i + 1 {
    let a = self.right.samples[i].abs()
    if a > max_abs {
      max_abs = a
    }
  }
  if max_abs > 0.000001 {
    let scale = target_peak / max_abs
    self.left.amplify(scale)
    self.right.amplify(scale)
  }
}

///|
/// Amplifies both channels by a gain multiplier.
pub fn StereoBuffer::amplify(self : StereoBuffer, gain : Double) -> Unit {
  self.left.amplify(gain)
  self.right.amplify(gain)
}

///|
/// Deep copies a StereoBuffer.
pub fn StereoBuffer::clone(self : StereoBuffer) -> StereoBuffer {
  {
    sample_rate: self.sample_rate,
    left: self.left.clone(),
    right: self.right.clone(),
  }
}

///|
/// Mixes another audio buffer into this buffer starting at `offset_samples` with volume scaling.
pub fn AudioBuffer::mix(
  self : AudioBuffer,
  other : AudioBuffer,
  offset_samples : Int32,
  volume : Double,
) -> Unit {
  let offset = if offset_samples < 0 { 0 } else { offset_samples }
  let needed_len = offset + other.samples.length()
  while self.samples.length() < needed_len {
    self.samples.push(0.0)
  }
  for i = 0; i < other.samples.length(); i = i + 1 {
    let target_idx = offset + i
    self.samples[target_idx] = self.samples[target_idx] +
      other.samples[i] * volume
  }
}

///|
/// Creates a deep copy of this AudioBuffer.
pub fn AudioBuffer::clone(self : AudioBuffer) -> AudioBuffer {
  let copied = Array::make(self.samples.length(), 0.0)
  for i = 0; i < self.samples.length(); i = i + 1 {
    copied[i] = self.samples[i]
  }
  { sample_rate: self.sample_rate, samples: copied, }
}

///|
/// Converts float samples in [-1.0, 1.0] to signed 16-bit integers [-32768, 32767].
pub fn AudioBuffer::to_16bit_pcm(self : AudioBuffer) -> Array[Int32] {
  let result = Array::make(self.samples.length(), 0)
  for i = 0; i < self.samples.length(); i = i + 1 {
    let mut s = self.samples[i]
    if s > 1.0 {
      s = 1.0
    } else if s < -1.0 {
      s = -1.0
    }
    let pcm = (s * 32767.0).to_int()
    result[i] = pcm
  }
  result
}

///|
/// Converts float samples in [-1.0, 1.0] to unsigned 8-bit bytes [0, 255] with center at 128.
pub fn AudioBuffer::to_8bit_pcm(self : AudioBuffer) -> Array[Byte] {
  let result = Array::make(self.samples.length(), b'\x00')
  for i = 0; i < self.samples.length(); i = i + 1 {
    let mut s = self.samples[i]
    if s > 1.0 {
      s = 1.0
    } else if s < -1.0 {
      s = -1.0
    }
    let pcm = ((s + 1.0) * 127.5).to_int()
    let clamped = if pcm < 0 { 0 } else if pcm > 255 { 255 } else { pcm }
    result[i] = clamped.to_byte()
  }
  result
}