///|
/// Compute Vorbis window function value.
/// w(i) = sin(π/2 · sin²(π(i+0.5)/n))
fn vorbis_window(i : Int, n : Int) -> Float {
  let pi = Float::from_double(@math.PI)
  let half_pi = pi / (2.0 : Float)
  let inner = pi * (Float::from_int(i) + 0.5) / Float::from_int(n)
  let sin_inner = @math.sinf(inner)
  let sin_sq = sin_inner * sin_inner
  @math.sinf(half_pi * sin_sq)
}

///|
/// Apply the Vorbis window function to a buffer in place.
fn apply_vorbis_window(data : Array[Float], n : Int) -> Unit {
  for i in 0.. (Array[Float], Array[Float]) raise AudioError {
  let br = new_bit_reader(packet)
  // Packet type must be 0 (audio)
  let packet_type = read_bit(br)
  if packet_type {
    raise AudioError::DecodeFailed("Vorbis: not an audio packet")
  }
  // Mode number
  let mode_bits = ilog(info.modes.length() - 1)
  let mode_number = read_bits(br, mode_bits)
  if mode_number >= info.modes.length() {
    raise AudioError::DecodeFailed("Vorbis: invalid mode number")
  }
  let mode = info.modes[mode_number]
  let n = if mode.block_flag { info.blocksize_1 } else { info.blocksize_0 }
  let half_n = n / 2
  let mapping = info.mappings[mode.mapping]
  // Decode floors
  let floors_data : Array[Array[Float]?] = []
  let no_residue : Array[Bool] = []
  for ch in 0.. {
          floors_data.push(Some(curve))
          no_residue.push(false)
        }
        None => {
          floors_data.push(None)
          no_residue.push(true)
        }
      }
    } else {
      floors_data.push(None)
      no_residue.push(true)
    }
  }
  // Decode residues
  let residue_output : Array[Array[Float]] = []
  for submap_idx in 0.. 0 {
        let res_data = decode_residue(
          br,
          info.residues[residue_idx],
          info.codebooks,
          half_n,
          ch_count,
          do_not_decode,
        )
        for c in res_data {
          residue_output.push(c)
        }
      }
    }
  }
  // Coupling
  for step in (mapping.coupling_steps - 1)..<=0 {
    if step < 0 {
      break
    }
    let mag_ch = mapping.coupling_magnitude[step]
    let ang_ch = mapping.coupling_angle[step]
    if mag_ch < residue_output.length() && ang_ch < residue_output.length() {
      for i in 0.. 0.0 {
          if a > 0.0 {
            (m, m - a)
          } else {
            (m + a, m)
          }
        } else if a > 0.0 {
          (m, m + a)
        } else {
          (m - a, m)
        }
        residue_output[mag_ch][i] = new_m
        residue_output[ang_ch][i] = new_a
      }
    }
  }
  // Floor multiply + IMDCT + windowing per channel
  let left : Array[Float] = Array::make(n, (0.0 : Float))
  let right : Array[Float] = Array::make(n, (0.0 : Float))
  for ch in 0..
        for i in 0..
        for i in 0..= 0.0 { diff } else { -diff }
    assert_true(abs_diff < 0.001)
  }
}

///|
test "vorbis_window boundary values" {
  let n = 32
  // Window at edges should be near 0
  let w0 = vorbis_window(0, n)
  assert_true(w0 > 0.0 && w0 < 0.2)
  // Window at center should be near 1
  let w_mid = vorbis_window(n / 2, n)
  assert_true(w_mid > 0.8)
}

///|
test "apply_vorbis_window" {
  let data : Array[Float] = Array::make(8, (1.0 : Float))
  apply_vorbis_window(data, 8)
  // After windowing, edges should be attenuated
  assert_true(data[0] < 0.5)
  assert_true(data[7] < 0.5)
  // Middle should be closer to 1
  assert_true(data[3] > 0.5)
  assert_true(data[4] > 0.5)
}