// header.mbt
//
// 明文头数据库(kHeader)解析:属性树 → StreamsInfo(PackInfo / UnpackInfo /
// SubStreamsInfo)+ FilesInfo。
//
// 依据 py7zr《.7z format specification》"Header" 一节:
//   Header       := kHeader(0x01) [MainStreamsInfo] [FilesInfo] kEnd(0x00)
//   StreamsInfo  := kPackInfo(0x06) [kUnpackInfo(0x07)] [kSubStreamsInfo(0x08)] kEnd
//   FilesInfo    := kFilesInfo(0x05) numFiles 属性列表 kEnd
//   FilesInfo 的每个属性都是 `  `
//
// 与 libarchive 的一处兼容差异(实测 bsdtar 写出的归档):
// kName 块的内容比"所有名字的 UTF-16LE 字节数"多一个前导 0x00 字节。
// 因此这里先按规范的偏移 0 严格解析,名字个数不符时再按偏移 1 重试。

///|
/// 归档中的一个条目。
pub struct Entry {
  /// 归档内路径(kName 解码后的字符串)。
  name : String
  /// 解包后大小(字节);目录为 0。
  size : Int64
  /// 内容 CRC-32;归档未声明时为 `None`,目录恒为 `None`。
  crc : Int64?
  /// 是否为目录(无数据流且不属于"空文件")。
  is_dir : Bool
}

///|
/// 单个 coder(解压 / 过滤算子)。
pub struct Coder {
  /// coder 标识:COPY=0x00、LZMA=0x030101、LZMA2=0x21、BCJ=0x04 等。
  id : Int64
  /// coder 属性(LZMA 为 5 字节 props,LZMA2 为 1 字节字典大小)。
  props : Bytes
  num_in : Int32
  num_out : Int32
}

///|
/// Folder:一组 coder 组成的解压单元,也是 Solid 压缩的边界。
pub struct Folder {
  coders : Array[Coder]
  /// 每个输出流的解压大小。
  unpack_sizes : Array[Int64]
  num_sub_streams : Int32
  sub_sizes : Array[Int64]
  sub_crcs : Array[Int64?]
}

///|
/// 归档的流信息(PackInfo + UnpackInfo + SubStreamsInfo)。
pub struct Streams {
  /// 打包数据区相对签名头末尾的偏移(绝对位置 = 32 + pack_pos)。
  pack_pos : Int64
  pack_sizes : Array[Int64]
  pack_crcs : Array[Int64?]
  folder_crcs : Array[Int64?]
  folders : Array[Folder]
}

///|
/// 文件表(与流信息合并前)。
priv struct FileTable {
  names : Array[String]
  empty_stream : Array[Bool]
  empty_file : Array[Bool]
}

///|
/// 头数据库的解析结果:条目表 + 流信息 + "条目 → 子流"的映射。
priv struct Plan {
  entries : Array[Entry]
  streams : Streams
  /// 与 `entries` 等长;有数据流的条目给出 `(folder 下标, 子流下标)`,目录为 `None`。
  mapping : Array[(Int32, Int32)?]
}

///|
/// 空的流信息(归档没有 kMainStreamsInfo 时使用)。
fn empty_streams() -> Streams {
  { pack_pos: 0L, pack_sizes: [], pack_crcs: [], folder_crcs: [], folders: [], }
}

///|
/// 解析头数据库,返回归档中的条目列表。
///
/// 输入:`SevenZ`(起始头已校验)。
/// 输出:条目数组,顺序与归档内文件表一致。
/// 失败:编码头(F5 尚未实现)/ 属性树非法 / 截断 / 名字数与文件数不符。
pub fn SevenZ::entries(self : SevenZ) -> Array[Entry] raise SevenZError {
  let plan = self.parse_plan()
  plan.entries
}

///|
/// 归档的流信息(打包位置与大小、Folder / coder 链、子流大小与 CRC)。
///
/// 输入:`SevenZ`(起始头已校验)。
/// 输出:`Streams`,供解压(后续增量)与压缩方式检查使用。
pub fn SevenZ::streams(self : SevenZ) -> Streams raise SevenZError {
  let plan = self.parse_plan()
  plan.streams
}

///|
/// 解析头数据库,一次得到条目表、流信息与条目到子流的映射。
fn SevenZ::parse_plan(self : SevenZ) -> Plan raise SevenZError {
  let sh = self.start_header
  let mut header = copy_range(
    self.data,
    sh.absolute_offset().to_int(),
    sh.absolute_end().to_int(),
  )
  if header.length() == 0 {
    raise Malformed(reason="头数据库为空")
  }
  if header.unsafe_get(0) == 0x17 {
    header = self.decode_encoded_header(header)
  }
  let r = ByteReader::new(header)
  let first = r.read_byte()
  if first != 0x01 {
    raise Malformed(
      reason="明文头应以 kHeader(0x01) 开头,实际 " + hex_byte(first),
    )
  }
  let mut streams = empty_streams()
  let mut table : FileTable? = None
  while true {
    let pid = r.read_byte()
    if pid == 0x00 {
      break
    }
    if pid == 0x04 {
      streams = parse_streams(r)
    } else if pid == 0x05 {
      table = Some(parse_files(r))
    } else if pid == 0x02 {
      // kArchiveProperties:本实现用不到,按声明长度整段跳过
      let size = r.read_number()
      r.skip(size.to_int())
    } else {
      raise Malformed(reason="Header 中出现未知属性 " + hex_byte(pid))
    }
  }
  match table {
    Some(t) => {
      let (entries, mapping) = build_plan(t, streams)
      { entries, streams, mapping, }
    }
    None => raise Malformed(reason="缺少 kFilesInfo(0x05)")
  }
}

///|
/// 解码被压缩的头数据库(kEncodedHeader 0x17)。
///
/// 编码头自己带一段 StreamsInfo,用它的 PackInfo / Folder 指向文件里的 packed
/// 头流;解出来就是明文头(kHeader 0x01)。默认档 7z(bsdtar 默认压缩)的头
/// 就是这种形态,所以这一步是"普通 7z 可列可解"的必要条件。
fn SevenZ::decode_encoded_header(
  self : SevenZ,
  encoded : Bytes,
) -> Bytes raise SevenZError {
  let r = ByteReader::new(encoded)
  let id = r.read_byte()
  if id != 0x17 {
    raise Malformed(reason="不是 kEncodedHeader:" + hex_byte(id))
  }
  let streams = parse_streams(r)
  if streams.folders.length() != 1 || streams.pack_sizes.length() != 1 {
    raise Unsupported(what="编码头包含多个 Folder / pack stream")
  }
  let start = 32L + streams.pack_pos
  let end = start + streams.pack_sizes[0]
  if end > self.data.length().to_int64() {
    raise Malformed(reason="编码头的 packed 区间越界")
  }
  let packed = copy_range(self.data, start.to_int(), end.to_int())
  let plain = decode_folder_bytes(streams.folders[0], packed)
  if streams.folder_crcs.length() > 0 {
    match streams.folder_crcs[0] {
      Some(expected) => {
        let actual = crc32(plain, 0, plain.length())
        if actual != expected {
          raise BadCrc(what="encoded header", expected~, actual~)
        }
      }
      None => ()
    }
  }
  plain
}

///|
/// 解析 StreamsInfo。
fn parse_streams(r : ByteReader) -> Streams raise SevenZError {
  let pid = r.read_byte()
  if pid != 0x06 {
    raise Malformed(
      reason="StreamsInfo 应以 kPackInfo(0x06) 开头,实际 " +
        hex_byte(pid),
    )
  }
  let pack_pos = r.read_number()
  let num_pack = r.read_number().to_int()
  if num_pack < 0 {
    raise Malformed(reason="kPackInfo 的打包流个数为负")
  }
  let pack_sizes : Array[Int64] = []
  let mut pack_crcs : Array[Int64?] = []
  while true {
    let p = r.read_byte()
    if p == 0x00 {
      break
    }
    if p == 0x09 {
      for _ in 0.. (Array[Folder], Array[Int64?]) raise SevenZError {
  let folders : Array[Folder] = []
  let mut folder_crcs : Array[Int64?] = []
  let mut sizes : Array[Array[Int64]] = []
  while true {
    let p = r.read_byte()
    if p == 0x00 {
      break
    }
    if p == 0x0B {
      let num = r.read_number().to_int()
      if num < 0 {
        raise Malformed(reason="Folder 个数为负")
      }
      let external = r.read_byte()
      if external != 0 {
        raise Unsupported(what="外部 Folder 定义(kFolder external=1)")
      }
      for _ in 0.. Folder raise SevenZError {
  let num_coders = r.read_number().to_int()
  if num_coders <= 0 || num_coders > 64 {
    raise Malformed(reason="coder 个数非法:" + num_coders.to_string())
  }
  let coders : Array[Coder] = []
  let mut total_in = 0
  let mut total_out = 0
  for _ in 0.. 8 {
      raise Malformed(
        reason="coder 标识长度非法:" + id_size.to_string(),
      )
    }
    let id_bytes = r.read_bytes(id_size)
    let mut id = 0L
    for i in 0.. 1 {
    for _ in 0.. Array[Folder] raise SevenZError {
  let n = folders.length()
  let num_sub : Array[Int32] = []
  let sizes : Array[Array[Int64]] = []
  let crcs : Array[Array[Int64?]] = []
  for _ in 0.. 0 {
          folders[i].unpack_sizes[0]
        } else {
          0L
        }
        let mut acc = 0L
        for _ in 0..<(k - 1) {
          let s = r.read_number()
          sizes[i].push(s)
          acc += s
        }
        sizes[i].push(total - acc)
      }
    } else if p == 0x0A {
      // 只为"文件夹级 CRC 未覆盖"的子流读摘要:
      // 当 folder 只有一个子流且 folder CRC 已定义时,该子流的 CRC 就是 folder CRC。
      let needs : Array[(Int32, Int32)] = []
      for i in 0.. Array[Folder] {
  let out : Array[Folder] = []
  for i in 0.. 0 {
          f.unpack_sizes[0]
        } else {
          0L
        },
      )
      // SubStreamsInfo 已给出 CRC 时以它为准;否则回落到 folder 级 CRC
      // (folder 只有一个子流时,规范允许把 CRC 写在 UnpackInfo 里)。
      if sub_crcs.length() == 0 {
        sub_crcs = []
        sub_crcs.push(
          if i < folder_crcs.length() {
            folder_crcs[i]
          } else {
            None
          },
        )
      }
    }
    out.push({
      coders: f.coders,
      unpack_sizes: f.unpack_sizes,
      num_sub_streams: k,
      sub_sizes,
      sub_crcs,
    })
  }
  out
}

///|
/// 解析 FilesInfo(文件名 / 空流 / 空文件标志)。
fn parse_files(r : ByteReader) -> FileTable raise SevenZError {
  let num_files = r.read_number().to_int()
  if num_files < 0 {
    raise Malformed(reason="kFilesInfo 的文件数为负")
  }
  let mut empty_stream : Array[Bool] = []
  for _ in 0.. Array[String] raise SevenZError {
  let (names_zero, used_zero) = split_names(content, 0)
  if names_zero.length() == num_files && used_zero == content.length() {
    return names_zero
  }
  let (names_one, used_one) = split_names(content, 1)
  if names_one.length() == num_files && used_one + 1 == content.length() {
    return names_one
  }
  raise Malformed(
    reason="kName 块无法解析:numFiles=" +
      num_files.to_string() +
      ",偏移 0 得到 " +
      names_zero.length().to_string() +
      " 个名字(消费 " +
      used_zero.to_string() +
      "/" +
      content.length().to_string() +
      " 字节)",
  )
}

///|
/// 从 `start` 偏移起按 UTF-16LE 切分名字,返回(名字列表, 消费字节数)。
fn split_names(content : Bytes, start : Int32) -> (Array[String], Int32) {
  let out : Array[String] = []
  let mut buf = @buffer.Buffer()
  let mut i = start
  while i + 1 < content.length() {
    if content.unsafe_get(i).to_int() == 0 &&
      content.unsafe_get(i + 1).to_int() == 0 {
      out.push(
        @utf16.decode_lossy(buf.to_bytes(), endianness=@utf16.Endian::Little),
      )
      buf = @buffer.Buffer()
    } else {
      buf.write_byte(content.unsafe_get(i))
      buf.write_byte(content.unsafe_get(i + 1))
    }
    i += 2
  }
  let rest = buf.to_bytes()
  if rest.length() > 0 {
    out.push(@utf16.decode_lossy(rest, endianness=@utf16.Endian::Little))
    i += rest.length()
  }
  (out, i - start)
}

///|
/// 把文件表与流信息合并成条目列表,同时给出"条目 → 子流"的映射。
fn build_plan(
  table : FileTable,
  streams : Streams,
) -> (Array[Entry], Array[(Int32, Int32)?]) raise SevenZError {
  let sizes : Array[Int64] = []
  let crcs : Array[Int64?] = []
  let owners : Array[(Int32, Int32)] = []
  for fi in 0..= sizes.length() {
        raise Malformed(
          reason="有数据流的文件数多于子流数(文件 " +
            table.names[i] +
            ")",
        )
      }
      out.push({
        name: table.names[i],
        size: sizes[next],
        crc: crcs[next],
        is_dir: false,
      })
      mapping.push(Some(owners[next]))
      next += 1
    }
  }
  (out, mapping)
}

///|
/// `Folder` 的输出流总数(等于 kCodersUnpackSize 中该 folder 的条目数)。
fn Folder::out_count(self : Folder) -> Int32 {
  let mut total = 0
  for c in self.coders {
    total += c.num_out
  }
  total
}

///|
/// `Int64?` 是否为 `Some`。
fn is_some(value : Int64?) -> Bool {
  match value {
    Some(_) => true
    None => false
  }
}

///|
/// 统计布尔数组里 `true` 的个数。
fn count_true(values : Array[Bool]) -> Int32 {
  let mut n = 0
  for v in values {
    if v {
      n += 1
    }
  }
  n
}