///|
/// WOFF2 magic number: "wOF2" = 0x774F4632
let woff2_magic : Int = 0x774F4632

///|
/// Known table tags per WOFF2 spec, indexed 0-62
let known_tags : FixedArray[String] = [
  "cmap", "head", "hhea", "hmtx", "maxp", "name", "OS/2", "post", "cvt ", "fpgm",
  "glyf", "loca", "prep", "CFF ", "VORG", "EBDT", "EBLC", "gasp", "hdmx", "kern",
  "LTSH", "PCLT", "VDMX", "vhea", "vmtx", "BASE", "GDEF", "GPOS", "GSUB", "EBSC",
  "JSTF", "MATH", "CBDT", "CBLC", "COLR", "CPAL", "SVG ", "sbix", "acnt", "avar",
  "bdat", "bloc", "bsln", "cvar", "fdsc", "feat", "fmtx", "fvar", "gvar", "hsty",
  "just", "lcar", "mort", "morx", "opbd", "prop", "trak", "Zapf", "Silf", "Glat",
  "Gloc", "Feat", "Sill",
]

///|
/// WOFF2 table directory entry
priv struct Woff2TableEntry {
  tag : String
  transform_version : Int
  src_offset : Int
  src_length : Int
}

///|
/// Read a UIntBase128-encoded value (1-5 bytes, big-endian base-128)
fn read_uintbase128(reader : BinaryReader) -> Int? {
  let mut result = 0
  for i = 0; i < 5; i = i + 1 {
    let b = reader.read_uint8()
    if i == 0 && b == 0x80 {
      return None
    }
    if (result & 0xFE000000) != 0 {
      return None
    }
    result = (result << 7) | (b & 0x7F)
    if (b & 0x80) == 0 {
      return Some(result)
    }
    ignore(i)
  }
  None
}

///|
/// Read a 255UShort-encoded value (variable-length uint16)
fn read_255ushort(reader : BinaryReader) -> Int {
  let code = reader.read_uint8()
  if code == 253 {
    reader.read_uint16()
  } else if code == 255 {
    253 + reader.read_uint8()
  } else if code == 254 {
    506 + reader.read_uint8()
  } else {
    code
  }
}

///|
/// Append a big-endian int16 to an Array[Byte]
fn write_be16_arr(out : Array[Byte], value : Int) -> Unit {
  out.push(((value >> 8) & 0xFF).to_byte())
  out.push((value & 0xFF).to_byte())
}

///|
/// Parse a WOFF2 font, decompress tables, reconstruct transforms, and delegate to parse_ttf
pub fn parse_woff2(data : Bytes) -> TTFont? {
  let reader = BinaryReader::new(data)
  // WOFF2 Header (48 bytes)
  let signature = reader.read_uint32()
  if signature != woff2_magic {
    return None
  }
  let flavor = reader.read_uint32()
  let _length = reader.read_uint32()
  let num_tables = reader.read_uint16()
  let _reserved = reader.read_uint16()
  let _total_sfnt_size = reader.read_uint32()
  let total_compressed_size = reader.read_uint32()
  let _major_version = reader.read_uint16()
  let _minor_version = reader.read_uint16()
  let _meta_offset = reader.read_uint32()
  let _meta_length = reader.read_uint32()
  let _meta_orig_length = reader.read_uint32()
  let _priv_offset = reader.read_uint32()
  let _priv_length = reader.read_uint32()
  // Read table directory
  let entries : Array[Woff2TableEntry] = []
  let mut src_offset = 0
  for i = 0; i < num_tables; i = i + 1 {
    let flag_byte = reader.read_uint8()
    let tag_index = flag_byte & 0x3F
    let transform_version = (flag_byte >> 6) & 0x03
    let tag = if tag_index == 63 {
      reader.read_tag()
    } else {
      known_tags[tag_index]
    }
    // glyf/loca: xform version 0 = transform applied (default)
    // others: xform version != 0 = transform applied
    let has_transform = if tag == "glyf" || tag == "loca" {
      transform_version == 0
    } else {
      transform_version != 0
    }
    guard read_uintbase128(reader) is Some(orig_length) else { return None }
    let transform_length = if has_transform {
      guard read_uintbase128(reader) is Some(tl) else { return None }
      tl
    } else {
      orig_length
    }
    entries.push({
      tag,
      transform_version,
      src_offset,
      src_length: transform_length,
    })
    src_offset += transform_length
    ignore(i)
  }
  // Decompress all table data with Brotli
  let compressed_offset = reader.position()
  let compressed = bytes_slice(data, compressed_offset, total_compressed_size)
  let decompressed = @brotli.brotli_decompress(compressed) catch {
    _ => return None
  }
  // Find special table entries
  let mut glyf_idx : Int = -1
  let mut loca_idx : Int = -1
  let mut hhea_idx : Int = -1
  let mut hmtx_idx : Int = -1
  for i = 0; i < entries.length(); i = i + 1 {
    let e = entries[i]
    if e.tag == "glyf" {
      glyf_idx = i
    }
    if e.tag == "loca" {
      loca_idx = i
    }
    if e.tag == "hhea" {
      hhea_idx = i
    }
    if e.tag == "hmtx" {
      hmtx_idx = i
    }
    ignore(i)
  }
  // Get numHMetrics from hhea table (offset 34, uint16)
  let num_h_metrics = if hhea_idx >= 0 {
    let hhea = entries[hhea_idx]
    if hhea.src_length >= 36 {
      let hhea_reader = BinaryReader::at(decompressed, hhea.src_offset + 34)
      hhea_reader.read_uint16()
    } else {
      0
    }
  } else {
    0
  }
  // Reconstruct glyf/loca if transformed
  let mut glyf_data : Bytes? = None
  let mut loca_data : Bytes? = None
  let mut x_mins : Array[Int] = []
  if glyf_idx >= 0 && loca_idx >= 0 {
    let glyf_e = entries[glyf_idx]
    if glyf_e.transform_version == 0 {
      match
        reconstruct_glyf_loca(
          decompressed,
          glyf_e.src_offset,
          glyf_e.src_length,
        ) {
        Some((g, l, xm)) => {
          glyf_data = Some(g)
          loca_data = Some(l)
          x_mins = xm
        }
        None => return None
      }
    }
  }
  // Reconstruct hmtx if transformed
  let mut hmtx_data : Bytes? = None
  if hmtx_idx >= 0 {
    let hmtx_e = entries[hmtx_idx]
    // hmtx transform: transform_version != 0 for non-glyf/loca tables
    if hmtx_e.transform_version != 0 {
      hmtx_data = Some(
        reconstruct_hmtx(
          decompressed,
          hmtx_e.src_offset,
          hmtx_e.src_length,
          x_mins.length(),
          num_h_metrics,
          x_mins,
        ),
      )
    }
  }
  // Build table list for sfnt reconstruction
  let tables : Array[(String, Bytes)] = []
  for i = 0; i < entries.length(); i = i + 1 {
    let entry = entries[i]
    let table_data = if entry.tag == "glyf" {
      match glyf_data {
        Some(gd) => gd
        None => bytes_slice(decompressed, entry.src_offset, entry.src_length)
      }
    } else if entry.tag == "loca" {
      match loca_data {
        Some(ld) => ld
        None => bytes_slice(decompressed, entry.src_offset, entry.src_length)
      }
    } else if entry.tag == "hmtx" {
      match hmtx_data {
        Some(hd) => hd
        None => bytes_slice(decompressed, entry.src_offset, entry.src_length)
      }
    } else {
      bytes_slice(decompressed, entry.src_offset, entry.src_length)
    }
    tables.push((entry.tag, table_data))
    ignore(i)
  }
  let sfnt = rebuild_sfnt(flavor, tables)
  parse_ttf(sfnt)
}