// apev2.mbt
//
// APEv2 标签的读写。
//
// APEv2 位于文件**尾部**(与 ID3v2 在头部相反),布局:
//   32 字节 footer:  "APETAGEX" + version(u32 LE) + size(u32 LE)
//                     + item_count(u32 LE) + flags(u32 LE) + reserved(8)
//   size 字节的条目区(在 footer 之前)
//
// 每个条目:
//   value_size  u32 LE
//   flags       u32 LE
//   key         ASCII,以 0 结尾
//   value       value_size 字节
//
// 与 ID3v2 的差异:APEv2 的键值语义是"扁平字典"(无帧 ID 分级),
// 且整数/二进制值由 flags 的 bit0 区分。

///|
/// APEv2 中的一个条目。
pub struct ApeItem {
  /// 键名(大写 ASCII)。
  key : String
  /// 是否为二进制值(flags bit0)。
  is_binary : Bool
  /// 值字节;文本值按 UTF-8 解码展示。
  value : Bytes
}

///|
/// 解析出的 APEv2 标签。
pub struct ApeTag {
  /// 条目列表,保持文件内顺序。
  items : Array[ApeItem]
  /// 标签总字节数(条目区 + footer,不含 header)。
  size : Int32
  /// 标签在文件中的起始偏移。
  offset : Int32
}

///|
/// 从文件尾部定位并解析 APEv2 标签;没有则返回 `None`。
pub fn parse_ape(data : BytesView) -> ApeTag? raise TagError {
  // footer 可能紧贴文件尾,也可能在 ID3v1(128 字节)之前
  let candidates = [0, 128]
  for back in candidates {
    let footer_pos = data.length() - 32 - back
    if footer_pos < 0 {
      continue
    }
    if !is_ape_footer(data, footer_pos) {
      continue
    }
    return Some(read_ape_at(data, footer_pos))
  }
  None
}

///|
/// 判断指定位置是否是一个 APEv2 footer。
fn is_ape_footer(data : BytesView, pos : Int32) -> Bool {
  if data.length() < pos + 32 {
    return false
  }
  let magic = "APETAGEX"
  for i in 0..<8 {
    if data.at(pos + i).to_int() != magic.at(i).to_int() {
      return false
    }
  }
  true
}

///|
/// 在已知 footer 位置的前提下解析整个标签。
fn read_ape_at(data : BytesView, footer_pos : Int32) -> ApeTag raise TagError {
  // version 在 magic 之后 4 字节,这里只接受 2000(APEv2)
  let version = read_le32(data, footer_pos + 8)
  if version != 2000 {
    raise Unsupported(what="APEv1(version \{version})标签")
  }
  let size = read_le32(data, footer_pos + 12)
  let count = read_le32(data, footer_pos + 16)
  // size 包含 footer 自身;条目区在 footer 之前
  if size < 32 || size > footer_pos + 32 {
    raise BadHeader(reason="APEv2 size 字段 \{size} 越界")
  }
  let items_start = footer_pos + 32 - size
  let items_end = footer_pos
  let items : Array[ApeItem] = []
  let mut pos = items_start
  for _ in 0.. String {
  let buf = StringBuilder()
  for i in start.. Bytes? {
  for item in self.items {
    if item.key.equal_ignore_ascii_case(key) {
      return Some(item.value)
    }
  }
  None
}

///|
/// 取某个键的文本值。
pub fn ApeTag::text(self : ApeTag, key : String) -> String? {
  match self.get(key) {
    None => None
    Some(v) => Some(@utf8.decode_lossy(v.exact_view()))
  }
}

///|
/// 设置一个键:替换同名的全部旧条目,或追加。
///
/// `value` 为空表示删除该键。
pub fn ApeTag::set(
  self : ApeTag,
  key : String,
  value : Bytes,
) -> ApeTag raise TagError {
  let upper = key.to_upper()
  if !is_valid_ape_key(upper) {
    raise BadFrameData(frame_id="APEv2", reason="非法的键名:\{key}")
  }
  let kept : Array[ApeItem] = []
  for item in self.items {
    if !item.key.equal_ignore_ascii_case(key) {
      kept.push(item)
    }
  }
  if value.length() > 0 {
    kept.push({ key: upper, is_binary: false, value, })
  }
  { items: kept, size: self.size, offset: self.offset, }
}

///|
/// APEv2 键名规则:2..255 字符,ASCII 0x20..0x7E,不含 '='。
fn is_valid_ape_key(key : String) -> Bool {
  let n = key.char_length()
  if n < 2 || n > 255 {
    return false
  }
  for c in key {
    let cp = c.to_int()
    if cp < 0x20 || cp > 0x7E || c == '=' {
      return false
    }
  }
  true
}

///|
/// 构造空的 APEv2 标签。
pub fn empty_ape_tag() -> ApeTag {
  { items: [], size: 0, offset: 0, }
}

///|
/// 序列化条目区 + footer。
fn serialize_ape(tag : ApeTag) -> Bytes raise TagError {
  let items = @buffer.Buffer()
  for item in tag.items {
    write_le32(items, item.value.length())
    write_le32(items, if item.is_binary { 1 } else { 0 })
    for c in item.key {
      let cp = c.to_int()
      if cp > 0x7E {
        raise BadFrameData(frame_id="APEv2", reason="键名含非 ASCII 字符")
      }
      items.write_byte(cp.to_byte())
    }
    items.write_byte(b'\x00')
    items.write_bytes(item.value)
  }
  let item_bytes = items.to_bytes()
  let total = item_bytes.length() + 32
  let out = @buffer.Buffer()
  out.write_bytes(item_bytes)
  // footer:magic + version + size + count + flags + reserved(8)
  for c in "APETAGEX" {
    out.write_byte(c.to_int().to_byte())
  }
  write_le32(out, 2000) // version
  write_le32(out, total) // size(含 footer)
  write_le32(out, tag.items.length()) // item count
  // flags:bit31 = 含 header(本实现写纯 footer 形态,不置位)
  write_le32(out, 0)
  for _ in 0..<8 {
    out.write_byte(b'\x00')
  }
  out.to_bytes()
}

///|
/// 用新的 APEv2 标签重写文件:去掉旧的 APEv2(若有),再追加新标签。
///
/// 文件其余部分(含 ID3v2 头部标签与音频)原样保留。
pub fn set_ape_tag(data : BytesView, tag : ApeTag) -> Bytes raise TagError {
  let existing = parse_ape(data)
  // 原 APEv2 占据的尾部区间
  let cut_end = match existing {
    None => data.length()
    Some(t) => t.offset
  }
  let body = slice_bytes(data, 0, cut_end)
  let ape = serialize_ape(tag)
  body.add(ape)
}