// codebook.mbt
//
// Vorbis codebook 解析。
//
// codebook 是 Vorbis setup header 里最复杂的数据结构,用于 Huffman 解码与
// 向量量化(VQ)。包含:头部字段、每个 entry 的码长、以及可选的 lookup table。

///|
/// `ilog(v)`:表示 `v` 所需的位数,即 `floor(log2(v)) + 1`(`v > 0` 时)。
/// 用于 ordered codebook 的游程长度读取。
fn ilog(v : Int) -> Int {
  let mut ret = 0
  let mut x = v
  while x > 0 {
    ret += 1
    x = x >> 1
  }
  ret
}

///|
/// 整数幂 `base^exp`。
fn pow_int(base : Int, exp : Int) -> Int {
  let mut result = 1
  for _ in 0.. Int {
  let mut r = 0
  while pow_int(r + 1, dim) <= entries {
    r += 1
  }
  r
}

///|
/// Vorbis 专用的 32 位浮点解包:1 位符号 + 10 位指数 + 21 位尾数。
/// 值为 `mantissa * 2^(exponent - 788)`,与 IEEE 754 不同,不能按位重解释。
fn float32_unpack(x : UInt) -> Float {
  let mantissa = (x & 0x1fffffU).reinterpret_as_int()
  let sign = (x & 0x80000000U) != 0U
  let exp = ((x & 0x7fe00000U) >> 21).reinterpret_as_int()
  let res = if sign {
    Float::from_int(0 - mantissa)
  } else {
    Float::from_int(mantissa)
  }
  @math.scalbnf(res, exp - 788)
}

///|
/// 解析后的 codebook(含码长与 lookup table)。
///
/// 字段含义:
/// * `lengths` : 每个 entry 的码长,hole(sparse 中未使用)用 0 表示;
/// * `lookup_type` : 0(无 lookup)、1 或 2;
/// * `multiplicands` : lookup table 的量化值(lookup type > 0 时非空)。
pub struct Codebook {
  dimensions : Int
  entries : Int
  lengths : Array[Int]
  lookup_type : Int
  min_value : Float
  delta_value : Float
  value_bits : Int
  sequence_flag : Int
  multiplicands : Array[Int]
  multiplicands_f : Array[Float]
  huffman : HuffmanTable
}

///|
/// 从 `br` 的当前位置解析一个 codebook。
///
/// 失败返回错误描述(sync 不符 / entry 溢出 / 不支持的 lookup 类型或 lattice 编码)。
pub fn Codebook::parse(br : BitReader) -> Result[Codebook, String] {
  // sync pattern 必须为 0x564342("BCV" 的小端字节序)
  let sync = br.read_bits(24)
  if sync != 0x564342 {
    return Err("bad codebook sync")
  }
  let dimensions = br.read_bits(16)
  let entries = br.read_bits(24)
  let ordered = br.read_bits(1)
  let sparse = if ordered == 1 { 0 } else { br.read_bits(1) }

  let lengths : Array[Int] = []
  if ordered == 1 {
    // ordered codebook:码长按游程编码,每次游程后 current_length 递增 1
    let mut current_entry = 0
    let mut current_length = br.read_bits(5) + 1
    while current_entry < entries {
      let n = br.read_bits(ilog(entries - current_entry))
      if current_entry + n > entries {
        return Err("codebook entry overflow")
      }
      for _ in 0.. h
    Err(e) => return Err(e)
  }

  Ok({
    dimensions,
    entries,
    lengths,
    lookup_type,
    min_value,
    delta_value,
    value_bits,
    sequence_flag,
    multiplicands,
    multiplicands_f,
    huffman,
  })
}

///|
/// 标量解码一个码字,返回 entry 索引。
pub fn Codebook::decode_scalar(
  self : Codebook,
  br : BitReader,
) -> Result[Int, String] {
  self.huffman.decode(br)
}

///|
/// VQ 解码:解码一个 entry,把 lookup 值累加到 target[offset..offset+len]。
pub fn Codebook::decode_vq(
  self : Codebook,
  br : BitReader,
  target : Array[Float],
  offset : Int,
  len : Int,
) -> Result[Unit, String] {
  if self.lookup_type == 0 {
    return Err("codebook has no lookup table")
  }
  let z = match self.decode_scalar(br) {
    Ok(z) => z
    Err(e) => return Err(e)
  }
  let n = if len > self.dimensions { self.dimensions } else { len }
  let base = z * self.dimensions
  for i in 0.. Result[Unit, String] {
  if self.lookup_type == 0 {
    return Err("codebook has no lookup table")
  }
  let mut i = 0
  while i < len {
    let z = match self.decode_scalar(br) {
      Ok(z) => z
      Err(e) => return Err(e)
    }
    let base = z * self.dimensions
    let mut k = 0
    while i < len && k < self.dimensions {
      target[offset + i] = self.multiplicands_f[base + k]
      i += 1
      k += 1
    }
  }
  Ok(())
}

///|
/// 同 decode_vq,但分量之间隔着 step 个位置摆放,用于 residue type 0 的交织写法。
pub fn Codebook::decode_vq_step(
  self : Codebook,
  br : BitReader,
  target : Array[Float],
  offset : Int,
  len : Int,
  step : Int,
) -> Result[Unit, String] {
  if self.lookup_type == 0 {
    return Err("codebook has no lookup table")
  }
  let z = match self.decode_scalar(br) {
    Ok(z) => z
    Err(e) => return Err(e)
  }
  let n = if len > self.dimensions { self.dimensions } else { len }
  let base = z * self.dimensions
  for i in 0..= 0 && at < target.length() {
      target[at] = target[at] + self.multiplicands_f[base + i]
    }
  }
  Ok(())
}