///|
/// Filter type for digital biquad filtering.
#derive(Eq, Debug)
pub(all) enum FilterType {
  LowPass
  HighPass
  BandPass
}

///|
/// Digital biquad IIR filter implementing Robert Bristow-Johnson Audio EQ Cookbook algorithms.
pub struct BiquadFilter {
  mut b0 : Double
  mut b1 : Double
  mut b2 : Double
  mut a1 : Double
  mut a2 : Double
  mut x1 : Double
  mut x2 : Double
  mut y1 : Double
  mut y2 : Double
}

///|
/// Creates a new BiquadFilter with given filter type, cutoff frequency, and resonance Q factor.
pub fn BiquadFilter::new(
  filter_type : FilterType,
  cutoff_freq : Double,
  resonance_q : Double,
  sample_rate : Int32,
) -> BiquadFilter {
  let sr = if sample_rate <= 0 { 44100.0 } else { sample_rate.to_double() }
  let nyquist = sr * 0.5
  let cutoff = if cutoff_freq <= 10.0 {
    10.0
  } else if cutoff_freq >= nyquist - 100.0 {
    nyquist - 100.0
  } else {
    cutoff_freq
  }
  let q = if resonance_q <= 0.1 { 0.1 } else { resonance_q }

  let w0 = two_pi * cutoff / sr
  let cos_w0 = @math.cos(w0)
  let sin_w0 = @math.sin(w0)
  let alpha = sin_w0 / (2.0 * q)

  let mut b0 = 0.0
  let mut b1 = 0.0
  let mut b2 = 0.0
  let a0 = 1.0 + alpha
  let a1 = -2.0 * cos_w0
  let a2 = 1.0 - alpha

  match filter_type {
    LowPass => {
      b0 = (1.0 - cos_w0) * 0.5
      b1 = 1.0 - cos_w0
      b2 = (1.0 - cos_w0) * 0.5
    }
    HighPass => {
      b0 = (1.0 + cos_w0) * 0.5
      b1 = -(1.0 + cos_w0)
      b2 = (1.0 + cos_w0) * 0.5
    }
    BandPass => {
      b0 = alpha
      b1 = 0.0
      b2 = -alpha
    }
  }

  // Normalize by a0
  b0 = b0 / a0
  b1 = b1 / a0
  b2 = b2 / a0
  let norm_a1 = a1 / a0
  let norm_a2 = a2 / a0

  { b0, b1, b2, a1: norm_a1, a2: norm_a2, x1: 0.0, x2: 0.0, y1: 0.0, y2: 0.0, }
}

///|
/// Dynamically updates the cutoff frequency and recalculates filter coefficients.
/// Enables continuous dynamic filter sweeping during audio rendering without allocating new objects.
pub fn BiquadFilter::update_cutoff(
  self : BiquadFilter,
  filter_type : FilterType,
  cutoff_freq : Double,
  resonance_q : Double,
  sample_rate : Int32,
) -> Unit {
  let sr = if sample_rate <= 0 { 44100.0 } else { sample_rate.to_double() }
  let nyquist = sr * 0.5
  let cutoff = if cutoff_freq <= 10.0 {
    10.0
  } else if cutoff_freq >= nyquist - 100.0 {
    nyquist - 100.0
  } else {
    cutoff_freq
  }
  let q = if resonance_q <= 0.1 { 0.1 } else { resonance_q }

  let w0 = two_pi * cutoff / sr
  let cos_w0 = @math.cos(w0)
  let sin_w0 = @math.sin(w0)
  let alpha = sin_w0 / (2.0 * q)

  let mut b0 = 0.0
  let mut b1 = 0.0
  let mut b2 = 0.0
  let a0 = 1.0 + alpha
  let a1 = -2.0 * cos_w0
  let a2 = 1.0 - alpha

  match filter_type {
    LowPass => {
      b0 = (1.0 - cos_w0) * 0.5
      b1 = 1.0 - cos_w0
      b2 = (1.0 - cos_w0) * 0.5
    }
    HighPass => {
      b0 = (1.0 + cos_w0) * 0.5
      b1 = -(1.0 + cos_w0)
      b2 = (1.0 + cos_w0) * 0.5
    }
    BandPass => {
      b0 = alpha
      b1 = 0.0
      b2 = -alpha
    }
  }

  self.b0 = b0 / a0
  self.b1 = b1 / a0
  self.b2 = b2 / a0
  self.a1 = a1 / a0
  self.a2 = a2 / a0
}

///|
/// Processes a single input sample through the biquad filter.
pub fn BiquadFilter::process(self : BiquadFilter, x : Double) -> Double {
  let y = self.b0 * x +
    self.b1 * self.x1 +
    self.b2 * self.x2 -
    self.a1 * self.y1 -
    self.a2 * self.y2
  self.x2 = self.x1
  self.x1 = x
  self.y2 = self.y1
  self.y1 = y
  y
}

///|
/// Applies a biquad filter across all samples in the AudioBuffer.
pub fn AudioBuffer::apply_filter(
  self : AudioBuffer,
  filter_type : FilterType,
  cutoff_freq : Double,
  resonance_q : Double,
) -> Unit {
  let filter = BiquadFilter::new(
    filter_type,
    cutoff_freq,
    resonance_q,
    self.sample_rate,
  )
  for i = 0; i < self.samples.length(); i = i + 1 {
    self.samples[i] = filter.process(self.samples[i])
  }
}

///|
/// Applies warm cubic overdrive distortion to the buffer.
/// `drive` parameter controls intensity (1.0 = gentle saturation, 5.0+ = heavy fuzz).
pub fn AudioBuffer::apply_overdrive(self : AudioBuffer, drive : Double) -> Unit {
  let d = if drive <= 0.0 { 1.0 } else { drive }
  for i = 0; i < self.samples.length(); i = i + 1 {
    let x = self.samples[i] * d
    let y = if x > 1.5 {
      1.0
    } else if x < -1.5 {
      -1.0
    } else {
      x - x * x * x / 3.0
    }
    self.samples[i] = y / (if d > 1.0 { d * 0.7 } else { 1.0 })
  }
}

///|
/// Applies a feedback echo / delay effect to the audio buffer.
pub fn AudioBuffer::apply_echo(
  self : AudioBuffer,
  delay_seconds : Double,
  feedback : Double,
  wet_mix : Double,
) -> Unit {
  if delay_seconds <= 0.001 || feedback <= 0.0 {
    return
  }
  let delay_samples = (delay_seconds * self.sample_rate.to_double()).to_int()
  if delay_samples <= 0 || delay_samples >= self.samples.length() {
    return
  }
  let fb = if feedback > 0.95 { 0.95 } else { feedback }
  let mix = if wet_mix < 0.0 {
    0.0
  } else if wet_mix > 1.0 {
    1.0
  } else {
    wet_mix
  }

  for i = delay_samples; i < self.samples.length(); i = i + 1 {
    let delayed_sample = self.samples[i - delay_samples]
    self.samples[i] = self.samples[i] * (1.0 - mix * 0.5) +
      delayed_sample * fb * mix
  }
}

///|
/// Bitcrushes and downsamples the audio buffer to emulate classic 8-bit / 12-bit lo-fi retro sound chips.
pub fn AudioBuffer::apply_bitcrush(
  self : AudioBuffer,
  bits : Int32,
  downsample_factor : Int32,
) -> Unit {
  let b = if bits < 2 { 2 } else if bits > 16 { 16 } else { bits }
  let factor = if downsample_factor < 1 { 1 } else { downsample_factor }
  let steps = @math.pow(2.0, (b - 1).to_double())

  let mut hold_val = 0.0
  for i = 0; i < self.samples.length(); i = i + 1 {
    if i % factor == 0 {
      let mut s = self.samples[i]
      if s > 1.0 {
        s = 1.0
      } else if s < -1.0 {
        s = -1.0
      }
      // Quantize to discrete steps
      let quantized = (s * steps).round() / steps
      hold_val = quantized
    }
    self.samples[i] = hold_val
  }
}