// celt_frame.mbt
//
// CELT 帧级解码主流程(RFC 6716 §4.3 频域段)。
//
// 对应参考实现 celt_celt_decoder.c 的 celt_decode_frame:把已完成的各
// 部件按规范顺序串起来——帧间能量状态预处理 → 帧首四符号 → 粗能量 →
// TF → spread → band boost/trim → 预算与反塌缩预留 → 比特分配 →
// 细能量 → 形状解码(quant_all_bands)→ 反塌缩标志 → 收尾细能量 →
// 反塌缩 → 静音置位 → 反归一化 → 帧间状态轮换。
//
// 范围(与实现的注释一致):CELT-only、单声道、无损解码路径。
//   - loss_duration 安全衰减块(参考实现 1387–1415 行)只在丢包恢复期
//     生效,本流程无丢包输入,恒不触发;
//   - backgroundLogE 只被丢包隐藏(PLC)读取,正常解码不写不读,省略;
//   - 参考实现的 `st->end`/`effEBands` 在 48 kHz 模式下都等于 21,
//     effEnd 与 end 恒同,denorm 直接用 end;
//   - 静音置位(old_e = -28)按参考实现保留:帧首静音位在本流程可达
//     (首符号 tell==1,p≈2^-15,金标有定向用例),静音帧的后续符号
//     由各部件自身的 tell 门控自然跳过;
//   - 时域段(IMDCT、post-filter、de-emphasis)不在此文件,接在本流程
//     的 freq 输出之后。
//
// 帧间状态(对应 CELT 解码器 struct 的跨帧字段):
//   - old_e:42 = 2*nbEBands。[0..21) 当前帧能量;[21..42) 上一帧
//     拷贝,供入口 MAXG 保护(静音帧只写前半)与 old_log_e 轮换;
//   - old_log_e/old_log_e2:42,反塌缩的 prev 槽位会读到后半;
//   - rng:跨帧 LCG 种子——初始化为 0,帧末被 `dec.rng`(本帧熵解码
//     器终值)覆盖,即参考实现的 `st->rng = dec->rng`。

///|
/// 跨帧解码状态。初始值为全 0(对应参考实现的 memset 清零)。
pub struct CeltDecoderState {
  /// oldBandE(42 = 2*nbEBands,后半为上一帧拷贝)
  old_e : Array[Double]
  /// oldLogE(42,反塌缩读到后半槽位)
  old_log_e : Array[Double]
  /// oldLogE2(42)
  old_log_e2 : Array[Double]
  /// st->rng:帧首为上一帧熵解码器终值
  mut rng : Int64
}

///|
/// 全零初始状态(rng = 0)。
pub fn CeltDecoderState::new() -> CeltDecoderState {
  CeltDecoderState::{
    old_e: Array::make(42, 0.0),
    old_log_e: Array::make(42, 0.0),
    old_log_e2: Array::make(42, 0.0),
    rng: 0L,
  }
}

///|
/// 一帧的频域解码结果。
pub struct CeltFrameResult {
  /// 帧首四符号(静音/后滤波/瞬态/帧内)
  header : CeltFrameHeader
  /// 归一化频谱 X(长度 120*m,反塌缩就地修改后的值)
  spectrum : Array[Double]
  /// 反归一化频谱(长度 120*m,IMDCT 的输入)
  freq : Array[Double]
  /// 每带 collapse mask(1 = 未塌缩)
  collapse_masks : Array[Int]
  /// quant_all_bands 线程化后的 LCG 种子(反塌缩的输入值)
  seed : Int64
}

///|
/// 解一帧的频域部分。`dec` 是按本帧载荷初始化的范围解码器,
/// `frame_bytes` 是载荷字节数(≥2,与参考实现一致:len≤1 走 PLC,
/// 不在本流程),`start`/`end` 是编码带窗(CELT-only 单声道为 0/21),
/// `lm` 是帧长索引。`state` 跨帧就地更新。
///
/// 顺序与参考实现逐段对齐;返回值含全部可供金标比对的中间产物。
pub fn celt_decode_frame(
  dec : RangeDecoder,
  frame_bytes : Int,
  start : Int,
  end : Int,
  lm : Int,
  state : CeltDecoderState,
) -> CeltFrameResult {
  let m = 1 << lm
  let nb = CEL_NB_EBANDS

  // 参考实现解码入口的 MAXG 保护(C==1):前半与上一帧拷贝取 max,
  // 让静音帧写下的 -28 在下一帧被后半的正常值救回。正常路径下入口处
  // 两半相等(每帧帧末都会拷贝),此步为恒等操作。
  for i in 0.. state.old_e[i] {
      state.old_e[i] = state.old_e[nb + i]
    }
  }

  let header = celt_decode_frame_header(dec, frame_bytes, start, lm)

  cel_unquant_coarse_energy(dec, state.old_e, start, end, header.intra, lm)
  let tf_res = celt_decode_tf(
    dec,
    start,
    end,
    header.is_transient,
    lm,
    frame_bytes,
  )
  let spread = celt_decode_spread(dec, frame_bytes)

  let cap = celt_init_caps(lm, 1)
  let flags = celt_decode_alloc_flags(dec, start, end, cap, frame_bytes, lm)

  // 反塌缩预留:瞬态、LM≥2 且预算充足才读 1 个 raw bit 的空间
  let mut bits = celt_alloc_total(frame_bytes, dec)
  let mut anti_rsv = 0
  if header.is_transient && lm >= 2 && bits >= (lm + 2) << CEL_BITRES {
    anti_rsv = 1 << CEL_BITRES
  }
  bits = bits - anti_rsv

  let alloc = celt_compute_allocation(
    dec,
    start,
    end,
    flags.offsets,
    cap,
    flags.alloc_trim,
    bits,
    lm,
  )
  cel_unquant_fine_energy(dec, state.old_e, start, end, alloc.ebits)

  let x : Array[Double] = Array::make(120 * m, 0.0)
  let masks : Array[Int] = Array::make(nb, 0)
  // 形状解码的预算按参考实现是整帧 1/8 bit 减预留(不扣已消费位数——
  // 消费记账由 balance/budget 机制负责),seed 来自跨帧状态。
  let seed = celt_quant_all_bands(
    dec,
    x,
    start,
    end,
    lm,
    header.is_transient,
    alloc.pulses,
    tf_res,
    alloc.coded_bands,
    frame_bytes * (8 << CEL_BITRES) - anti_rsv,
    alloc.balance,
    spread,
    state.rng,
    masks,
  )

  let mut anti_on = 0
  if anti_rsv > 0 {
    anti_on = celt_decode_anti_collapse_flag(dec, anti_rsv)
  }
  cel_unquant_energy_finalise(
    dec,
    state.old_e,
    start,
    end,
    alloc.ebits,
    alloc.fine_priority,
    frame_bytes * 8 - dec.tell(),
  )
  if anti_on != 0 {
    celt_anti_collapse(
      x,
      masks,
      lm,
      start,
      end,
      state.old_e,
      state.old_log_e,
      state.old_log_e2,
      alloc.pulses,
      seed,
    )
  }

  // 静音帧:当前能量置 -28(只写前半,后半留给入口 MAXG 保护)
  if header.silence {
    for i in 0.. state.old_e[i] {
        state.old_log_e[i] = state.old_e[i]
      }
    }
  }
  // st->rng = dec->rng:下一帧的 LCG 种子是本帧熵解码器终值
  state.rng = dec.rng

  CeltFrameResult::{ header, spectrum: x, freq, collapse_masks: masks, seed, }
}