// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Hint reliant font detection.
///
/// This is a direct port of `fontations/skrifa/src/outline/hint_reliant.rs`,
/// which itself follows FreeType's "tricky font" heuristics.
fn require_interpreter(_font : @moon_skrifa.FontRef) -> Bool {
  // "tricky font" detection by family name substring or cvt/fpgm/prep IDs.
  is_hint_reliant_by_name(_font) || matches_hint_reliant_id_list(_font)
}

///|
const TAG_CVT : UInt = 0x63767420 // "cvt "

///|
const MAX_HINT_RELIANT_NAME_LEN : Int = 18

///|
fn is_hint_reliant_by_name(font : @moon_skrifa.FontRef) -> Bool {
  match
    font
    .localized_strings(@moon_skrifa.STRING_ID_FAMILY_NAME)
    .english_or_first() {
    None => false
    Some(ls) => {
      let name = ls.to_string()
      // Ensure the name is short enough (FreeType compares fixed-size buffers).
      let mut n_chars = 0
      for c in name {
        // Keep this compatible with upstream's ASCII-only matching by
        // rejecting non-ASCII names.
        if c.to_int() > 0x7F {
          return false
        }
        n_chars = n_chars + 1
        if n_chars > MAX_HINT_RELIANT_NAME_LEN {
          return false
        }
      }
      matches_hint_reliant_name_list(name)
    }
  }
}

///|
fn matches_hint_reliant_name_list(name0 : String) -> Bool {
  let name = skip_pdf_random_tag(name0)
  // FreeType uses strstr(name, tricky_name): match via contains().
  for tricky in hint_reliant_names {
    if name.contains(tricky) {
      return true
    }
  }
  false
}

///|
fn skip_pdf_random_tag(name : String) -> String {
  // Random tag is 6 uppercase letters followed by a +.
  if name.length() < 8 {
    return name
  }
  if name.code_unit_at(6).to_int() != 43 {
    return name
  }
  for i in 0..<6 {
    let b = name.code_unit_at(i).to_int()
    if b < 65 || b > 90 {
      return name
    }
  }
  // Strip the `ABCDEF+` prefix.
  let out : Array[Char] = Array::new()
  let mut idx = 0
  for c in name {
    if idx >= 7 {
      out.push(c)
    }
    idx = idx + 1
  }
  String::from_array(out.op_as_view())
}

///|
let hint_reliant_names : Array[String] = Array::from_fixed_array([
  "cpop", "DFGirl-W6-WIN-BF", "DFGothic-EB", "DFGyoSho-Lt", "DFHei", "DFHSGothic-W5",
  "DFHSMincho-W3", "DFHSMincho-W7", "DFKaiSho-SB", "DFKaiShu", "DFKai-SB", "DFMing",
  "DLC", "HuaTianKaiTi?", "HuaTianSongTi?", "Ming(for ISO10646)", "MingLiU", "MingMedium",
  "PMingLiU", "MingLi43",
])

///|
priv struct TableId {
  checksum : UInt
  len : UInt
}

///|
fn TableId::default() -> TableId {
  { checksum: 0, len: 0 }
}

///|
fn compute_checksum(data : BytesView) -> UInt {
  // OpenType checksum: sum of big-endian u32 values, padding with zeros.
  let mut sum : UInt = 0
  let n = data.length()
  let mut i = 0
  while i < n {
    let b0 = data.at(i).to_uint()
    let b1 = if i + 1 < n { data.at(i + 1).to_uint() } else { 0 }
    let b2 = if i + 2 < n { data.at(i + 2).to_uint() } else { 0 }
    let b3 = if i + 3 < n { data.at(i + 3).to_uint() } else { 0 }
    let word = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
    sum = sum + word
    i = i + 4
  }
  sum
}

///|
fn TableId::from_font_and_tag(
  font : @moon_skrifa.FontRef,
  tag : UInt,
) -> TableId? {
  match font.table(tag) {
    None => None
    Some(view) =>
      Some({
        checksum: compute_checksum(view),
        len: view.length().reinterpret_as_uint(),
      })
  }
}

///|
priv struct FontId {
  cvt : TableId
  fpgm : TableId
  prep : TableId
}

///|
fn FontId::from_font(font : @moon_skrifa.FontRef) -> FontId {
  {
    cvt: TableId::from_font_and_tag(font, TAG_CVT).unwrap_or(TableId::default()),
    fpgm: TableId::from_font_and_tag(font, TAG_FPGM).unwrap_or(
      TableId::default(),
    ),
    prep: TableId::from_font_and_tag(font, TAG_PREP).unwrap_or(
      TableId::default(),
    ),
  }
}

///|
fn matches_hint_reliant_id_list(font : @moon_skrifa.FontRef) -> Bool {
  let id = FontId::from_font(font)
  for entry in hint_reliant_ids {
    if entry.cvt.checksum == id.cvt.checksum &&
      entry.cvt.len == id.cvt.len &&
      entry.fpgm.checksum == id.fpgm.checksum &&
      entry.fpgm.len == id.fpgm.len &&
      entry.prep.checksum == id.prep.checksum &&
      entry.prep.len == id.prep.len {
      return true
    }
  }
  false
}

///|
let hint_reliant_ids : Array[FontId] = Array::from_fixed_array([
  // MingLiU 1995
  {
    cvt: { checksum: 0x05BCF058, len: 0x000002E4 },
    fpgm: { checksum: 0x28233BF1, len: 0x000087C4 },
    prep: { checksum: 0xA344A1EA, len: 0x000001E1 },
  },
  // MingLiU 1996-
  {
    cvt: { checksum: 0x05BCF058, len: 0x000002E4 },
    fpgm: { checksum: 0x28233BF1, len: 0x000087C4 },
    prep: { checksum: 0xA344A1EB, len: 0x000001E1 },
  },
  // DFGothic-EB
  {
    cvt: { checksum: 0x12C3EBB2, len: 0x00000350 },
    fpgm: { checksum: 0xB680EE64, len: 0x000087A7 },
    prep: { checksum: 0xCE939563, len: 0x00000758 },
  },
  // DFGyoSho-Lt
  {
    cvt: { checksum: 0x11E5EAD4, len: 0x00000350 },
    fpgm: { checksum: 0xCE5956E9, len: 0x0000BC85 },
    prep: { checksum: 0x8272F416, len: 0x00000045 },
  },
  // DFHei-Md-HK-BF
  {
    cvt: { checksum: 0x1257EB46, len: 0x00000350 },
    fpgm: { checksum: 0xF699D160, len: 0x0000715F },
    prep: { checksum: 0xD222F568, len: 0x000003BC },
  },
  // DFHSGothic-W5
  {
    cvt: { checksum: 0x1262EB4E, len: 0x00000350 },
    fpgm: { checksum: 0xE86A5D64, len: 0x00007940 },
    prep: { checksum: 0x7850F729, len: 0x000005FF },
  },
  // DFHSMincho-W3
  {
    cvt: { checksum: 0x122DEB0A, len: 0x00000350 },
    fpgm: { checksum: 0x3D16328A, len: 0x0000859B },
    prep: { checksum: 0xA93FC33B, len: 0x000002CB },
  },
  // DFHSMincho-W7
  {
    cvt: { checksum: 0x125FEB26, len: 0x00000350 },
    fpgm: { checksum: 0xA5ACC982, len: 0x00007EE1 },
    prep: { checksum: 0x90999196, len: 0x0000041F },
  },
  // DFKaiShu
  {
    cvt: { checksum: 0x11E5EAD4, len: 0x00000350 },
    fpgm: { checksum: 0x5A30CA3B, len: 0x00009063 },
    prep: { checksum: 0x13A42602, len: 0x0000007E },
  },
  // DFKaiShu, variant
  {
    cvt: { checksum: 0x11E5EAD4, len: 0x00000350 },
    fpgm: { checksum: 0xA6E78C01, len: 0x00008998 },
    prep: { checksum: 0x13A42602, len: 0x0000007E },
  },
  // DFKaiShu-Md-HK-BF
  {
    cvt: { checksum: 0x11E5EAD4, len: 0x00000360 },
    fpgm: { checksum: 0x9DB282B2, len: 0x0000C06E },
    prep: { checksum: 0x53E6D7CA, len: 0x00000082 },
  },
  // DFMing-Bd-HK-BF
  {
    cvt: { checksum: 0x1243EB18, len: 0x00000350 },
    fpgm: { checksum: 0xBA0A8C30, len: 0x000074AD },
    prep: { checksum: 0xF3D83409, len: 0x0000037B },
  },
  // DLCLiShu
  {
    cvt: { checksum: 0x07DCF546, len: 0x00000308 },
    fpgm: { checksum: 0x40FE7C90, len: 0x00008E2A },
    prep: { checksum: 0x608174B5, len: 0x0000007A },
  },
  // DLCHayBold
  {
    cvt: { checksum: 0xEB891238, len: 0x00000308 },
    fpgm: { checksum: 0xD2E4DCD4, len: 0x0000676F },
    prep: { checksum: 0x8EA5F293, len: 0x000003B8 },
  },
  // HuaTianKaiTi
  {
    cvt: { checksum: 0xFFFBFFFC, len: 0x00000008 },
    fpgm: { checksum: 0x9C9E48B8, len: 0x0000BEA2 },
    prep: { checksum: 0x70020112, len: 0x00000008 },
  },
  // HuaTianSongTi
  {
    cvt: { checksum: 0xFFFBFFFC, len: 0x00000008 },
    fpgm: { checksum: 0x0A5A0483, len: 0x00017C39 },
    prep: { checksum: 0x70020112, len: 0x00000008 },
  },
  // NEC fadpop7.ttf
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x40C92555, len: 0x000000E5 },
    prep: { checksum: 0xA39B58E3, len: 0x0000117C },
  },
  // NEC fadrei5.ttf
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x33C41652, len: 0x000000E5 },
    prep: { checksum: 0x26D6C52A, len: 0x00000F6A },
  },
  // NEC fangot7.ttf
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x6DB1651D, len: 0x0000019D },
    prep: { checksum: 0x6C6E4B03, len: 0x00002492 },
  },
  // NEC fangyo5.ttf
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x40C92555, len: 0x000000E5 },
    prep: { checksum: 0xDE51FAD0, len: 0x0000117C },
  },
  // NEC fankyo5.ttf
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x85E47664, len: 0x000000E5 },
    prep: { checksum: 0xA6C62831, len: 0x00001CAA },
  },
  // NEC fanrgo5.ttf
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x2D891CFD, len: 0x0000019D },
    prep: { checksum: 0xA0604633, len: 0x00001DE8 },
  },
  // NEC fangot5.ttc
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x40AA774C, len: 0x000001CB },
    prep: { checksum: 0x9B5CAA96, len: 0x00001F9A },
  },
  // NEC fanmin3.ttc
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x0D3DE9CB, len: 0x00000141 },
    prep: { checksum: 0xD4127766, len: 0x00002280 },
  },
  // NEC FA-Gothic, 1996
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x4A692698, len: 0x000001F0 },
    prep: { checksum: 0x340D4346, len: 0x00001FCA },
  },
  // NEC FA-Minchou, 1996
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0xCD34C604, len: 0x00000166 },
    prep: { checksum: 0x6CF31046, len: 0x000022B0 },
  },
  // NEC FA-RoundGothicB, 1996
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0x5DA75315, len: 0x0000019D },
    prep: { checksum: 0x40745A5F, len: 0x000022E0 },
  },
  // NEC FA-RoundGothicM, 1996
  {
    cvt: { checksum: 0x00000000, len: 0x00000000 },
    fpgm: { checksum: 0xF055FC48, len: 0x000001C2 },
    prep: { checksum: 0x3900DED3, len: 0x00001E18 },
  },
  // MINGLI.TTF, 1992
  {
    cvt: { checksum: 0x00170003, len: 0x00000060 },
    fpgm: { checksum: 0xDBB4306E, len: 0x000058AA },
    prep: { checksum: 0xD643482A, len: 0x00000035 },
  },
  // DFHei-Bd-WIN-HK-BF, issue #1087
  {
    cvt: { checksum: 0x1269EB58, len: 0x00000350 },
    fpgm: { checksum: 0x5CD5957A, len: 0x00006A4E },
    prep: { checksum: 0xF758323A, len: 0x00000380 },
  },
  // DFMing-Md-WIN-HK-BF, issue #1087
  {
    cvt: { checksum: 0x122FEB0B, len: 0x00000350 },
    fpgm: { checksum: 0x7F10919A, len: 0x000070A9 },
    prep: { checksum: 0x7CD7E7B7, len: 0x0000025C },
  },
])