// 子集化:字形收集、复合字形闭包、表重建与 sfnt 组装。

///|
/// 读取复合字形直接引用的字形编号(非复合字形返回空数组)。
fn component_gids(font : Font, glyph : Int) -> Array[Int] raise FontError {
  let result : Array[Int] = []
  if glyph <= 0 || glyph >= font.num_glyphs {
    return result
  }
  let glyf = require_table(font.tables, "glyf")
  let start = glyf.offset + font.loca[glyph]
  let length = font.loca[glyph + 1] - font.loca[glyph]
  if length < 10 {
    return result
  }
  if ri16(font.bytes, start) >= 0 {
    return result
  }
  let end = start + length
  let mut pos = start + 10
  let mut steps = 0
  while pos + 4 <= end && steps < 1024 {
    let flags = ru16(font.bytes, pos)
    let component = ru16(font.bytes, pos + 2)
    result.push(component)
    pos = pos + 4
    if (flags & 0x0001) != 0 {
      pos = pos + 4
    } else {
      pos = pos + 2
    }
    if (flags & 0x0008) != 0 {
      pos = pos + 2
    } else if (flags & 0x0040) != 0 {
      pos = pos + 4
    } else if (flags & 0x0080) != 0 {
      pos = pos + 8
    }
    steps = steps + 1
    if (flags & 0x0020) == 0 {
      break
    }
  }
  result
}

///|
/// 把原字体中的一个字形追加到新 glyf 数据,并把复合字形里的组件编号
/// 从旧编号改写为新编号。
fn append_glyph(
  out : Array[Byte],
  font : Font,
  start : Int,
  length : Int,
  old_to_new : Array[Int],
) -> Unit raise FontError {
  let out_start = out.length()
  push_range(out, font.bytes, start, length)
  if length < 10 {
    return
  }
  if ri16(font.bytes, start) >= 0 {
    return
  }
  let mut pos = 10
  let mut steps = 0
  while pos + 4 <= length && steps < 1024 {
    let flags = ru16(font.bytes, start + pos)
    let old_glyph = ru16(font.bytes, start + pos + 2)
    let new_glyph = if old_glyph < old_to_new.length() {
      old_to_new[old_glyph]
    } else {
      -1
    }
    if new_glyph < 0 {
      raise FontBadTable("glyf:复合字形引用了未收录的字形")
    }
    out[out_start + pos + 2] = ((new_glyph >> 8) & 0xFF).to_byte()
    out[out_start + pos + 3] = (new_glyph & 0xFF).to_byte()
    pos = pos + 4
    if (flags & 0x0001) != 0 {
      pos = pos + 4
    } else {
      pos = pos + 2
    }
    if (flags & 0x0008) != 0 {
      pos = pos + 2
    } else if (flags & 0x0040) != 0 {
      pos = pos + 4
    } else if (flags & 0x0080) != 0 {
      pos = pos + 8
    }
    steps = steps + 1
    if (flags & 0x0020) == 0 {
      break
    }
  }
}

///|
/// 按字符集合做子集化,返回新的 TTF 字节。
///
/// 收录规则:
///   1. 始终保留 `.notdef`(glyph 0);
///   2. 字符经 cmap 映射到的字形;
///   3. 这些字形通过复合引用到的所有组件(递归闭包)。
///
/// 未收录字符不会出现在新 cmap 中;调用方可以用 `verify_subset` 复核。
pub fn subset_by_chars_with(
  font : Font,
  chars : Array[Int],
  options : SubsetOptions,
) -> Bytes raise FontError {
  let sorted : Array[Int] = []
  for code in chars {
    if code >= 0 && code <= 0x10FFFF && !sorted.contains(code) {
      sorted.push(code)
    }
  }
  sorted.sort()

  // 直接映射的字形
  let pairs : Array[(Int, Int)] = []
  let queue : Array[Int] = []
  let selected : Map[Int, Bool] = Map([])
  selected.set(0, true)
  for code in sorted {
    match font.cmap.get(code) {
      Some(glyph) =>
        if glyph > 0 && glyph < font.num_glyphs {
          pairs.push((code, glyph))
          if !selected.contains(glyph) {
            selected.set(glyph, true)
            queue.push(glyph)
          }
        }
      None => ()
    }
  }

  // 复合字形闭包 + 自定义收集器
  let mut head = 0
  while head < queue.length() {
    let glyph = queue[head]
    head = head + 1
    for component in component_gids(font, glyph) {
      if component > 0 &&
        component < font.num_glyphs &&
        !selected.contains(component) {
        selected.set(component, true)
        queue.push(component)
      }
    }
    match options.collector {
      Some(collector) =>
        for extra in collector(font, glyph) {
          if extra > 0 && extra < font.num_glyphs && !selected.contains(extra) {
            selected.set(extra, true)
            queue.push(extra)
          }
        }
      None => ()
    }
  }

  // 新字形顺序:0 → 字符直接映射的字形(按码点顺序)→ 其余闭包字形(按旧编号)
  let order : Array[Int] = [0]
  let assigned : Map[Int, Bool] = Map([])
  assigned.set(0, true)
  for pair in pairs {
    let glyph = pair.1
    if !assigned.contains(glyph) {
      assigned.set(glyph, true)
      order.push(glyph)
    }
  }
  let rest : Array[Int] = []
  for glyph in queue {
    if !assigned.contains(glyph) {
      rest.push(glyph)
    }
  }
  rest.sort()
  for glyph in rest {
    if !assigned.contains(glyph) {
      assigned.set(glyph, true)
      order.push(glyph)
    }
  }

  // 旧编号 → 新编号
  let old_to_new : Array[Int] = Array::make(font.num_glyphs, -1)
  let mut i = 0
  while i < order.length() {
    old_to_new[order[i]] = i
    i = i + 1
  }

  // glyf + loca
  let glyf_old = require_table(font.tables, "glyf")
  let glyf_out : Array[Byte] = []
  let loca_out : Array[Int] = [0]
  i = 0
  while i < order.length() {
    let old = order[i]
    let start = glyf_old.offset + font.loca[old]
    let length = font.loca[old + 1] - font.loca[old]
    append_glyph(glyf_out, font, start, length, old_to_new)
    while glyf_out.length() % 4 != 0 {
      glyf_out.push(Byte::default())
    }
    loca_out.push(glyf_out.length())
    i = i + 1
  }
  let new_count = order.length()

  // hmtx:统一写成 (advance, lsb),因此 numberOfHMetrics = 新字形数
  let hmtx_out : Array[Byte] = []
  i = 0
  while i < new_count {
    let old = order[i]
    push_u16(hmtx_out, font.advances[old])
    push_u16(hmtx_out, font.lsbs[old] & 0xFFFF)
    i = i + 1
  }

  // loca(固定使用长格式)
  let loca_bytes : Array[Byte] = []
  for offset in loca_out {
    push_u32(loca_bytes, offset)
  }

  // cmap:码点 → 新字形
  let new_pairs : Array[(Int, Int)] = []
  for pair in pairs {
    new_pairs.push((pair.0, old_to_new[pair.1]))
  }
  let cmap_bytes = build_cmap(new_pairs)

  // head:checkSumAdjustment 置零,indexToLocFormat 改为 1
  let head_old = require_table(font.tables, "head")
  let head_out : Array[Byte] = []
  push_range(head_out, font.bytes, head_old.offset, head_old.length)
  head_out[8] = Byte::default()
  head_out[9] = Byte::default()
  head_out[10] = Byte::default()
  head_out[11] = Byte::default()
  head_out[50] = Byte::default()
  head_out[51] = Int::to_byte(1)

  // maxp:numGlyphs
  let maxp_old = require_table(font.tables, "maxp")
  let maxp_out : Array[Byte] = []
  push_range(maxp_out, font.bytes, maxp_old.offset, maxp_old.length)
  maxp_out[4] = ((new_count >> 8) & 0xFF).to_byte()
  maxp_out[5] = (new_count & 0xFF).to_byte()

  // hhea:numberOfHMetrics
  let hhea_old = require_table(font.tables, "hhea")
  let hhea_out : Array[Byte] = []
  push_range(hhea_out, font.bytes, hhea_old.offset, hhea_old.length)
  hhea_out[34] = ((new_count >> 8) & 0xFF).to_byte()
  hhea_out[35] = (new_count & 0xFF).to_byte()

  let tables_out : Array[(String, Bytes)] = []
  tables_out.push(("head", bytes_of(head_out)))
  tables_out.push(("maxp", bytes_of(maxp_out)))
  tables_out.push(("hhea", bytes_of(hhea_out)))
  tables_out.push(("hmtx", bytes_of(hmtx_out)))
  tables_out.push(("loca", bytes_of(loca_bytes)))
  tables_out.push(("glyf", bytes_of(glyf_out)))
  tables_out.push(("cmap", cmap_bytes))

  // 可选表按策略保留;post 统一写成格式 3(无字形名)。
  if options.keeps_table("name") {
    copy_if_present(font, "name", tables_out)
  }
  if options.keeps_table("OS/2") {
    copy_os2(font, tables_out, pairs)
  }
  if options.keeps_table("cvt ") {
    copy_if_present(font, "cvt ", tables_out)
  }
  if options.keeps_table("fpgm") {
    copy_if_present(font, "fpgm", tables_out)
  }
  if options.keeps_table("prep") {
    copy_if_present(font, "prep", tables_out)
  }
  if options.keeps_table("gasp") {
    copy_if_present(font, "gasp", tables_out)
  }
  // 调用方显式追加保留的表(例如 GSUB);字形编号是否需要重写由调用方负责。
  for tag in options.keep_optional {
    if !default_optional_tables().contains(tag) && font.tables.contains(tag) {
      copy_if_present(font, tag, tables_out)
    }
  }
  if options.keeps_table("post") {
    let post = require_table_optional(font.tables, "post")
    match post {
      Some(record) =>
        if record.length >= 32 && ru32(font.bytes, record.offset) == 0x00030000 {
          let data : Array[Byte] = []
          push_range(data, font.bytes, record.offset, record.length)
          tables_out.push(("post", bytes_of(data)))
        } else {
          tables_out.push(("post", synth_post()))
        }
      None => tables_out.push(("post", synth_post()))
    }
  }
  assemble_sfnt(tables_out)
}

///|
/// 按字符集合做子集化,使用默认表策略。
pub fn subset_by_chars(
  font : Font,
  chars : Array[Int],
) -> Bytes raise FontError {
  subset_by_chars_with(font, chars, SubsetOptions::default())
}

///|
/// 按文本做子集化,使用默认表策略。
pub fn subset_by_text(font : Font, text : String) -> Bytes raise FontError {
  subset_by_text_with(font, text, SubsetOptions::default())
}

///|
/// 按文本做子集化,并应用自定义策略。
pub fn subset_by_text_with(
  font : Font,
  text : String,
  options : SubsetOptions,
) -> Bytes raise FontError {
  let chars : Array[Int] = []
  for ch in text {
    chars.push(ch.to_int())
  }
  subset_by_chars_with(font, chars, options)
}

///|
fn require_table_optional(
  tables : Map[String, TableRecord],
  tag : String,
) -> TableRecord? {
  tables.get(tag)
}

///|
fn copy_if_present(
  font : Font,
  tag : String,
  out : Array[(String, Bytes)],
) -> Unit raise FontError {
  match font.tables.get(tag) {
    Some(record) => {
      let data : Array[Byte] = []
      push_range(data, font.bytes, record.offset, record.length)
      out.push((tag, bytes_of(data)))
    }
    None => ()
  }
}

///|
/// OS/2 表:复制原表,并把 usFirstCharIndex / usLastCharIndex 收敛到新字符集。
fn copy_os2(
  font : Font,
  out : Array[(String, Bytes)],
  pairs : Array[(Int, Int)],
) -> Unit raise FontError {
  match font.tables.get("OS/2") {
    Some(record) => {
      let data : Array[Byte] = []
      push_range(data, font.bytes, record.offset, record.length)
      if data.length() >= 68 && pairs.length() > 0 {
        let mut first = 0xFFFF
        let mut last = 0
        for pair in pairs {
          if pair.0 <= 0xFFFF {
            if pair.0 < first {
              first = pair.0
            }
            if pair.0 > last {
              last = pair.0
            }
          }
        }
        if first <= last {
          data[64] = ((first >> 8) & 0xFF).to_byte()
          data[65] = (first & 0xFF).to_byte()
          data[66] = ((last >> 8) & 0xFF).to_byte()
          data[67] = (last & 0xFF).to_byte()
        }
      }
      out.push(("OS/2", bytes_of(data)))
    }
    None => ()
  }
}

///|
/// 32 字节的 post 格式 3 表:不携带字形名,避免删减字形后名字表错位。
fn synth_post() -> Bytes {
  let out : Array[Byte] = []
  push_u32(out, 0x00030000)
  push_u32(out, 0)
  push_u16(out, -100 & 0xFFFF)
  push_u16(out, 50)
  push_u32(out, 0)
  push_u32(out, 0)
  push_u32(out, 0)
  push_u32(out, 0)
  push_u32(out, 0)
  bytes_of(out)
}

///|
fn assemble_sfnt(tables : Array[(String, Bytes)]) -> Bytes raise FontError {
  tables.sort_by(fn(a, b) { a.0.compare(b.0) })
  let count = tables.length()
  let mut power = 1
  let mut selector = 0
  while power * 2 <= count {
    power = power * 2
    selector = selector + 1
  }
  let search_range = power * 16
  let range_shift = count * 16 - search_range

  let out : Array[Byte] = []
  push_u32(out, 0x00010000)
  push_u16(out, count)
  push_u16(out, search_range)
  push_u16(out, selector)
  push_u16(out, range_shift)

  let header_length = 12 + count * 16
  let offsets : Array[Int] = []
  let mut cursor = header_length
  for entry in tables {
    offsets.push(cursor)
    cursor = cursor + pad4(entry.1.length())
  }
  let mut i = 0
  while i < count {
    push_tag(out, tables[i].0)
    push_u32(out, 0) // checksum 占位
    push_u32(out, offsets[i])
    push_u32(out, tables[i].1.length())
    i = i + 1
  }
  i = 0
  while i < count {
    let data = tables[i].1
    let mut k = 0
    while k < data.length() {
      out.push(data.at(k))
      k = k + 1
    }
    push_zeros(out, pad4(data.length()) - data.length())
    i = i + 1
  }

  // 逐表校验和
  let snapshot = bytes_of(out)
  i = 0
  while i < count {
    let sum = checksum_range(snapshot, offsets[i], tables[i].1.length())
    write_u32u_at(out, 12 + i * 16 + 4, sum)
    i = i + 1
  }

  // 整字体校验和:adjustment = 0xB1B0AFBA - sum
  let snapshot2 = bytes_of(out)
  let total = checksum_range(snapshot2, 0, snapshot2.length())
  let magic : UInt64 = 0xB1B0AFBAUL
  let adjustment = ((magic - total.to_uint64()) & 0xFFFFFFFFUL).to_uint()
  let mut head_index = -1
  i = 0
  while i < count {
    if tables[i].0 == "head" {
      head_index = i
    }
    i = i + 1
  }
  if head_index < 0 {
    raise FontMissingTable("head")
  }
  write_u32u_at(out, offsets[head_index] + 8, adjustment)
  bytes_of(out)
}