// moonmp3-encoder — pure MoonBit MP3 encoder.
// Copyright 2026 Sakuraqwq1
// Licensed under the Apache License, Version 2.0.

// Layer III side information: the per-grule header that tells the decoder how
// the frame's main data is laid out. ISO/IEC 11172-3 §3-Annex B, Table B-8
// (the MPEG-1 audio_data layout). Side info is 17 bytes in mono and 32 bytes
// otherwise; the frame's main data follows immediately after it.

///|
/// Layer III side information for one granule of one channel.
pub(all) struct GranuleInfo {
  /// Length in bits of the scalefactor (part 2) plus Huffman (part 3) data.
  part2_3_length : Int32
  /// Number of Huffman code pairs in the "big values" region, times two.
  big_values : Int32
  /// Quantiser step size, in steps of 0.25 dB.
  global_gain : Int32
  /// Which scalefactor bands are coded, and with what precision.
  scalefac_compress : Int32
  /// 1 when the granule uses window switching (short or mixed blocks).
  window_switching_flag : Int32
  /// 0 long, 1 start, 2 short, 3 stop; only with window switching.
  block_type : Int32
  /// Long-block scalefactor bands below the switched window region.
  mixed_block_flag : Int32
  /// Huffman table for region 0.
  table_select0 : Int32
  /// Huffman table for region 1.
  table_select1 : Int32
  /// Huffman table for region 2 (or the second short-block region).
  table_select2 : Int32
  /// Gain offset for short-block subblock 0.
  subblock_gain0 : Int32
  /// Gain offset for short-block subblock 1.
  subblock_gain1 : Int32
  /// Gain offset for short-block subblock 2.
  subblock_gain2 : Int32
  /// One less than the number of scalefactor bands in region 0.
  region0_count : Int32
  /// One less than the number of scalefactor bands in region 1.
  region1_count : Int32
  /// High-frequency amplification of scalefactors (MPEG-1 only).
  preflag : Int32
  /// Scalefactor step size: 0 short (0.5 dB), 1 long (1.0 dB).
  scalefac_scale : Int32
  /// Huffman table for the count1 region: 0 selects table A.
  count1table_select : Int32
}

///| Side information that decodes to digital silence: no scalefactors, no

///|
/// Huffman data, no big values. A frame made of this plus an empty reservoir
/// carries 1152 zero samples and still parses everywhere.
pub fn GranuleInfo::silent() -> GranuleInfo {
  {
    part2_3_length: 0,
    big_values: 0,
    global_gain: 0,
    scalefac_compress: 0,
    window_switching_flag: 0,
    block_type: 0,
    mixed_block_flag: 0,
    table_select0: 0,
    table_select1: 0,
    table_select2: 0,
    subblock_gain0: 0,
    subblock_gain1: 0,
    subblock_gain2: 0,
    region0_count: 0,
    region1_count: 0,
    preflag: 0,
    scalefac_scale: 0,
    count1table_select: 0,
  }
}

///|
/// Write the side information of one frame.
///
/// `scfsi` holds one 4-bit scalefactor selector per channel and `granules`
/// holds `2 * channels` entries ordered granule-major: index
/// `granule * channels + channel`. The writer ends byte aligned, which the
/// spec guarantees because the field total is 136 bits in mono and 256 bits
/// otherwise.
pub fn write_side_info(
  w : @bits.BitWriter,
  channels : Int32,
  main_data_begin : Int32,
  scfsi : Array[Int32],
  granules : Array[GranuleInfo],
) -> Unit {
  w.write_bits(main_data_begin.to_int64(), 9)
  w.write_bits(0L, if channels == 1 { 5 } else { 3 })
  for ch = 0; ch < channels; ch = ch + 1 {
    w.write_bits(scfsi[ch].to_int64(), 4)
  }
  for gr = 0; gr < 2; gr = gr + 1 {
    for ch = 0; ch < channels; ch = ch + 1 {
      let g = granules[gr * channels + ch]
      w.write_bits(g.part2_3_length.to_int64(), 12)
      w.write_bits(g.big_values.to_int64(), 9)
      w.write_bits(g.global_gain.to_int64(), 8)
      w.write_bits(g.scalefac_compress.to_int64(), 4)
      w.write_bits(g.window_switching_flag.to_int64(), 1)
      if g.window_switching_flag == 0 {
        w.write_bits(g.table_select0.to_int64(), 5)
        w.write_bits(g.table_select1.to_int64(), 5)
        w.write_bits(g.table_select2.to_int64(), 5)
        w.write_bits(g.region0_count.to_int64(), 4)
        w.write_bits(g.region1_count.to_int64(), 3)
      } else {
        w.write_bits(g.block_type.to_int64(), 2)
        w.write_bits(g.mixed_block_flag.to_int64(), 1)
        w.write_bits(g.table_select0.to_int64(), 5)
        w.write_bits(g.table_select2.to_int64(), 5)
        w.write_bits(g.subblock_gain0.to_int64(), 3)
        w.write_bits(g.subblock_gain1.to_int64(), 3)
        w.write_bits(g.subblock_gain2.to_int64(), 3)
      }
      w.write_bits(g.preflag.to_int64(), 1)
      w.write_bits(g.scalefac_scale.to_int64(), 1)
      w.write_bits(g.count1table_select.to_int64(), 1)
    }
  }
}