// flac.mbt
//
// 顶层解码编排:元数据 → 帧定位(同步扫描 + 帧头校验)→ 子帧解码 →
// 声道去相关 → 输出。声道去相关公式(left/right/side、mid/side 的
// `mid' = (mid<<1)|(side&1)` 还原)经 ffmpeg 手造帧与真实文件双向验证;
// side 声道位宽恒为 bps+1(bps=32 时 33 位,故子帧域用 Int64)。
// 帧迭代逻辑集中在 `decode_next_block`,整文件 `decode` 与流式
// `FlacStream` 共用同一条路径。

///|
/// 整文件解码结果(planar:每声道一个样本数组,样本为 bps 位宽的有符号值)。
pub struct FlacFile {
  /// 流的 STREAMINFO 等元数据。
  info : StreamInfo
  /// 每声道样本(声道数 = `info.channels`)。
  channels : Array[Array[Int32]]
}

///|
/// 一个完整块的解码产物(已按声道分配去相关,长度 = 帧的 block_size)。
priv struct Block {
  block_size : Int32
  channels : Int32
  samples : Array[Array[Int32]]
}

///|
/// 尝试在读取器当前位置解析帧头:任何帧头级失败(同步码、保留位、
/// 变长帧号、CRC-8、截断)都折叠为 None,交给上层决定是否继续扫描;
/// 帧头之后的子帧/CRC-16 错误则直接上抛,不静默跳过。
fn try_parse_header(br : BitReader, info : StreamInfo) -> FrameHeader? {
  Some(parse_frame_header(br, info)) catch {
    _ => None
  }
}

///|
/// 解码帧体:各声道子帧 + 零填充到字节边界 + CRC-16 校验。
/// 成功后读取器停在下一帧起点(字节边界)。
fn decode_frame_body(
  br : BitReader,
  h : FrameHeader,
  temp : Array[Array[Int64]],
) -> Unit raise FlacError {
  let side_idx = if h.channel_assignment == 8 || h.channel_assignment == 10 {
    1
  } else if h.channel_assignment == 9 {
    0
  } else {
    -1
  }
  for c in 0..> 3
  let expect = br.read_bits64(16).to_int()
  br.check()
  let actual = crc16_range(br.data, h.start_byte, crc_pos)
  if expect != actual {
    raise BadCrc(kind="CRC-16", expected=actual, actual=expect)
  }
}

///|
/// 把子帧域(Int64)的声道矩阵按声道分配去相关成最终 Int 样本。
fn decorrelate(
  h : FrameHeader,
  temp : Array[Array[Int64]],
) -> Array[Array[Int32]] {
  let out : Array[Array[Int32]] = []
  for _ in 0..> 1).to_int())
      out[1].push(((mid2 - side) >> 1).to_int())
    }
  }
  out
}

///|
/// 定位并解码下一个完整块。
/// 帧头级失败向后扫描同步码重同步(容忍尾部杂字节);扫描到尽头返回 None。
/// 子帧或 CRC-16 失败直接上抛,不跳帧。
fn decode_next_block(
  br : BitReader,
  info : StreamInfo,
) -> Block? raise FlacError {
  let mut header : FrameHeader? = None
  let mut scan_done = false
  while header is None && !scan_done {
    let cur = br.pos >> 3
    br.truncated = false
    header = try_parse_header(br, info)
    if header is None {
      let next = scan_sync(br.data, cur + 1)
      if next < 0 {
        scan_done = true
      } else {
        br.seek(next * 8)
      }
    }
  }
  match header {
    None => None
    Some(h) => {
      let temp : Array[Array[Int64]] = []
      for _ in 0.. String {
  let hexs : Array[String] = [
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f",
  ]
  let mut out = ""
  for i in 0..> 4] + hexs[v & 0xF]
  }
  out
}

///|
/// 若 STREAMINFO 写了 MD5(非全零)则校验:交织小端、每样本符号扩展到
/// ceil(bps/8) 字节后紧凑打包(RFC 9639 §8.4,含 4/12/20 位等非 8 倍数
/// 位深;8/16/24/32 位的打包方式此前已与 ffmpeg 写入值交叉验证)。
fn verify_md5(
  info : StreamInfo,
  channels : Array[Array[Int32]],
) -> Unit raise FlacError {
  let mut has_md5 = false
  for i in 0.. 32 {
    return
  }
  // RFC 9639 §8.4:位深非整数字节时,每个样本符号扩展到下一个完整
  // 字节数(ceil(bps/8)),小端存储、跨声道交织。Int32 的补码表示
  // 已天然完成符号扩展,取低 nbytes 字节即可——8/16/24/32 位时
  // ceil(bps/8)==bps/8,与既有验证过的行为完全一致。
  let nbytes = (bps + 7) / 8
  let nch = channels.length()
  if nch == 0 {
    return
  }
  let frames = channels[0].length()
  let ctx = @crypto.MD5::new()
  let buf = @buffer.Buffer()
  for f in 0..> (8 * i)) & 0xFF).to_byte())
      }
    }
    if buf.length() >= 65536 {
      ctx.update(buf.to_bytes())
      buf.reset()
    }
  }
  if buf.length() > 0 {
    ctx.update(buf.to_bytes())
  }
  let fa = ctx.finalize()
  let dbuf = @buffer.Buffer()
  for i in 0.. FlacFile raise FlacError {
  let meta = parse_metadata(data)
  let info = meta.stream_info
  let br = BitReader::new(data)
  br.seek(meta.frame_offset * 8)
  let out : Array[Array[Int32]] = []
  for _ in 0.. 0 && decoded >= total {
      break
    }
    match decode_next_block(br, info) {
      None => eof = true
      Some(b) => {
        // total_samples 已知时截断到总长
        let take64 = if total > 0 &&
          decoded + Int64::from_int(b.block_size) > total {
          total - decoded
        } else {
          Int64::from_int(b.block_size)
        }
        let take = take64.to_int()
        for c in 0.. 0 && decoded < total {
    raise Truncated(
      offset=data.length(),
      need=(total - decoded).to_int(),
      actual=decoded.to_int(),
    )
  }
  verify_md5(info, out)
  { info, channels: out, }
}