// 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.

///|
/// CFF2 (OpenType `CFF2` table) outline extraction (MVP).
///
/// This parses the header/top-dict and evaluates Type2 charstrings.
///
/// Notes:
/// - Variation operators (vsindex/blend) are currently unsupported.
/// - Font DICT / FDSelect are ignored for now; local subrs are therefore unsupported.

///|
fn cff2_read_u16_be(view : BytesView, offset : Int) -> Int? {
  type2_read_u16_be(view, offset)
}

///|

///|
fn cff2_outline_path(
  font : @moon_skrifa.FontRef,
  gid : @moon_skrifa.GlyphId,
  coords : ArrayView[@moon_skrifa.NormalizedCoord],
) -> Array[PathElement]? {
  let cff2 = match font.table(TAG_CFF2) {
    None => return None
    Some(v) => v
  }
  if cff2.length() < 5 {
    return None
  }
  let major = cff2.at(0).to_int()
  let minor = cff2.at(1).to_int()
  if major != 2 || minor != 0 {
    return None
  }
  let header_size = cff2.at(2).to_int()
  let top_len = match cff2_read_u16_be(cff2, 3) {
    None => return None
    Some(v) => v
  }
  if header_size < 5 || header_size > cff2.length() {
    return None
  }
  let top_start = header_size
  let top_end = top_start + top_len
  if top_len < 0 || top_end < top_start || top_end > cff2.length() {
    return None
  }
  let top_dict = cff2.sub(start=top_start, end=top_end)
  let dict = cff_dict_parse(top_dict)
  let charstrings_off = match dict.charstrings_offset {
    None => return None
    Some(v) => v
  }
  let blend_state = match dict.var_store_offset {
    None => None
    Some(off) =>
      if off < 0 || off > cff2.length() {
        None
      } else {
        type2_blend_state_new(cff2, off, coords)
      }
  }
  if charstrings_off < 0 || charstrings_off > cff2.length() {
    return None
  }
  let global_off = top_end
  if global_off > cff2.length() {
    return None
  }
  let global_ix = match
    type2_index2_read(cff2.sub(start=global_off, end=cff2.length())) {
    None => return None
    Some(ix) => ix
  }
  // CharStrings INDEX2 is at the offset from Top DICT.
  let char_ix = match
    type2_index2_read(cff2.sub(start=charstrings_off, end=cff2.length())) {
    None => return None
    Some(ix) => ix
  }
  let glyph_index = gid.to_uint64().to_int()
  if glyph_index < 0 || glyph_index >= char_ix.count {
    return None
  }
  let charstring = match type2_index_get(char_ix, glyph_index) {
    None => return None
    Some(v) => v
  }
  if charstring.length() == 0 {
    return Some(Array::new())
  }
  type2_path(charstring, Some(global_ix), None, blend_state)
}

///|
fn cff2_glyph_count(font : @moon_skrifa.FontRef) -> Int? {
  let cff2 = match font.table(TAG_CFF2) {
    None => return None
    Some(v) => v
  }
  if cff2.length() < 5 {
    return None
  }
  let major = cff2.at(0).to_int()
  let minor = cff2.at(1).to_int()
  if major != 2 || minor != 0 {
    return None
  }
  let header_size = cff2.at(2).to_int()
  let top_len = match cff2_read_u16_be(cff2, 3) {
    None => return None
    Some(v) => v
  }
  if header_size < 5 || header_size > cff2.length() {
    return None
  }
  let top_start = header_size
  let top_end = top_start + top_len
  if top_len < 0 || top_end < top_start || top_end > cff2.length() {
    return None
  }
  let top_dict = cff2.sub(start=top_start, end=top_end)
  let dict = cff_dict_parse(top_dict)
  let charstrings_off = match dict.charstrings_offset {
    None => return None
    Some(v) => v
  }
  if charstrings_off < 0 || charstrings_off > cff2.length() {
    return None
  }
  let char_ix = match
    type2_index2_read(cff2.sub(start=charstrings_off, end=cff2.length())) {
    None => return None
    Some(ix) => ix
  }
  Some(char_ix.count)
}