// id3_apic.mbt
//
// APIC(Attached picture,附加图片)帧的读写。
//
// v2.3 与 v2.4 的 APIC 载荷布局完全一致:
//    文本编码  1 字节
//    MIME       $00            (始终 ISO-8859-1,与编码字节无关)
//    图片类型  1 字节
//    描述       $00 (00)
//    图片数据  
//
// 陷阱:MIME 串**不受**编码字节影响,永远是 Latin-1;只有"描述"跟随编码。

///|
/// 封面图片类型(APIC 的图片类型字节,ID3v2 规范 §4.15)。
///
/// 用 `pub(all)` 让调用方能直接构造枚举值;否则只能通过 `from_code` 拿。
pub(all) enum PictureType {
  Other
  FileIcon
  OtherIcon
  FrontCover
  BackCover
  LeafletPage
  Media
  LeadArtist
  Artist
  Conductor
  Band
  Composer
  Lyricist
  RecordingLocation
  DuringRecording
  DuringPerformance
  MovieScreenCapture
  BrightColouredFish
  Illustration
  BandLogotype
  PublisherLogotype
}

///|
/// 图片类型 → 规范字节值。
pub fn PictureType::code(self : PictureType) -> Int32 {
  match self {
    Other => 0x00
    FileIcon => 0x01
    OtherIcon => 0x02
    FrontCover => 0x03
    BackCover => 0x04
    LeafletPage => 0x05
    Media => 0x06
    LeadArtist => 0x07
    Artist => 0x08
    Conductor => 0x09
    Band => 0x0A
    Composer => 0x0B
    Lyricist => 0x0C
    RecordingLocation => 0x0D
    DuringRecording => 0x0E
    DuringPerformance => 0x0F
    MovieScreenCapture => 0x10
    BrightColouredFish => 0x11
    Illustration => 0x12
    BandLogotype => 0x13
    PublisherLogotype => 0x14
  }
}

///|
/// 规范字节值 → 图片类型。20 及以上的值规范未定义,归入 `Other`
/// 并保留原始字节(见 `Picture::raw_type`)。
pub fn PictureType::from_code(code : Int32) -> PictureType {
  match code {
    0x01 => FileIcon
    0x02 => OtherIcon
    0x03 => FrontCover
    0x04 => BackCover
    0x05 => LeafletPage
    0x06 => Media
    0x07 => LeadArtist
    0x08 => Artist
    0x09 => Conductor
    0x0A => Band
    0x0B => Composer
    0x0C => Lyricist
    0x0D => RecordingLocation
    0x0E => DuringRecording
    0x0F => DuringPerformance
    0x10 => MovieScreenCapture
    0x11 => BrightColouredFish
    0x12 => Illustration
    0x13 => BandLogotype
    0x14 => PublisherLogotype
    _ => Other
  }
}

///|
/// 一张附加图片。
pub struct Picture {
  /// MIME 类型(如 `image/png`)。
  mime : String
  /// 图片类型枚举。
  picture_type : PictureType
  /// 图片类型原始字节(保留未定义值)。
  raw_type : Int32
  /// 描述文本。
  description : String
  /// 图片二进制数据。
  data : Bytes
}

///|
/// 构造一张附加图片(供调用方使用;结构体字段本身是只读的)。
pub fn picture(
  mime : String,
  picture_type : PictureType,
  description : String,
  data : Bytes,
) -> Picture {
  { mime, picture_type, raw_type: picture_type.code(), description, data, }
}

///|
/// 解析 APIC 帧载荷。
pub fn parse_picture(payload : BytesView) -> Picture raise TagError {
  if payload.length() < 4 {
    raise BadFrameData(
      frame_id="APIC",
      reason="载荷长度 \{payload.length()} 不足",
    )
  }
  let code = payload.at(0).to_int()
  if code > 3 {
    raise BadFrameData(
      frame_id="APIC",
      reason="未知的文本编码字节 \{code}",
    )
  }
  let enc = TextEncoding::from_code(code)
  // MIME:Latin-1,$00 终止
  let mime_end = find_terminator(payload, 1, Latin1, payload.length())
  let mime = latin1_str(payload, 1, mime_end)
  let type_pos = mime_end + 1
  if type_pos >= payload.length() {
    raise BadFrameData(frame_id="APIC", reason="缺少图片类型字节")
  }
  let raw_type = payload.at(type_pos).to_int()
  // 描述:跟随编码,按其终止符长度跳过
  let desc_start = type_pos + 1
  let desc_end = find_terminator(payload, desc_start, enc, payload.length())
  let description = enc.decode(
    payload.exact_view(start=desc_start, end=desc_end),
  )
  let data_start = desc_end + enc.terminator_len()
  if data_start > payload.length() {
    raise BadFrameData(frame_id="APIC", reason="描述终止符超出载荷")
  }
  {
    mime,
    picture_type: PictureType::from_code(raw_type),
    raw_type,
    description,
    data: slice_bytes(payload, data_start, payload.length()),
  }
}

///|
/// Latin-1 切片转字符串。
fn latin1_str(data : BytesView, start : Int32, end : Int32) -> String {
  let buf = StringBuilder()
  for i in start.. Bytes raise TagError {
  let enc = if major == 4 { Utf8 } else { Utf16Bom }
  let out = @buffer.Buffer()
  out.write_byte(enc.code().to_byte())
  // MIME 恒为 Latin-1
  for c in pic.mime {
    let cp = c.to_int()
    if cp > 0xFF {
      raise BadFrameData(frame_id="APIC", reason="MIME 含非 Latin-1 字符")
    }
    out.write_byte(cp.to_byte())
  }
  out.write_byte(b'\x00')
  out.write_byte(pic.raw_type.to_byte())
  let desc = encode_string(pic.description, enc)
  out.write_bytes(desc)
  // 描述终止符
  for _ in 0.. Picture? raise TagError {
  match self.frame("APIC") {
    None => None
    Some(f) => Some(parse_picture(f.data.exact_view()))
  }
}

///|
/// 取标签里的全部图片(一个标签可以有多张,如正面 + 背面)。
pub fn Id3Tag::pictures(self : Id3Tag) -> Array[Picture] raise TagError {
  let out : Array[Picture] = []
  for f in self.frames {
    if f.id == "APIC" {
      out.push(parse_picture(f.data.exact_view()))
    }
  }
  out
}