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

///|
/// Minimal CFF/CFF2 DICT parsing helpers (only operators needed for outlines).

///|
priv struct CffDictInfo {
  mut charstrings_offset : Int?
  mut var_store_offset : Int?
  mut private_size : Int?
  mut private_offset : Int?
  mut subrs_offset : Int?
}

///|
fn cff_dict_read_i32_be(view : BytesView, offset : Int) -> Int? {
  if offset < 0 || offset + 4 > view.length() {
    None
  } else {
    let b0 = view.at(offset).to_int()
    let b1 = view.at(offset + 1).to_int()
    let b2 = view.at(offset + 2).to_int()
    let b3 = view.at(offset + 3).to_int()
    let u = (b0.reinterpret_as_uint() << 24) |
      (b1.reinterpret_as_uint() << 16) |
      (b2.reinterpret_as_uint() << 8) |
      b3.reinterpret_as_uint()
    Some(u.reinterpret_as_int())
  }
}

///|
fn cff_dict_parse_int(data : BytesView, pos : Int, b0 : Int) -> (Int, Int)? {
  match b0 {
    28 =>
      match type2_read_i16_be(data, pos) {
        None => None
        Some(v) => Some((v, pos + 2))
      }
    29 =>
      match cff_dict_read_i32_be(data, pos) {
        None => None
        Some(v) => Some((v, pos + 4))
      }
    32..=246 => Some((b0 - 139, pos))
    247..=250 =>
      if pos >= data.length() {
        None
      } else {
        let b1 = data.at(pos).to_int()
        Some(((b0 - 247) * 256 + b1 + 108, pos + 1))
      }
    251..=254 =>
      if pos >= data.length() {
        None
      } else {
        let b1 = data.at(pos).to_int()
        Some((-(b0 - 251) * 256 - b1 - 108, pos + 1))
      }
    // Real numbers (BCD) are not needed for our offsets; skip parsing.
    30 => None
    _ => None
  }
}

///|
fn cff_dict_parse(data : BytesView) -> CffDictInfo {
  let info = CffDictInfo::{
    charstrings_offset: None,
    var_store_offset: None,
    private_size: None,
    private_offset: None,
    subrs_offset: None,
  }
  let stack : Array[Int] = Array::new()
  let mut pos = 0
  while pos < data.length() {
    let b0 = data.at(pos).to_int()
    pos = pos + 1
    if b0 == 28 || b0 == 29 || b0 == 30 || (b0 >= 32 && b0 <= 254) {
      match cff_dict_parse_int(data, pos, b0) {
        None => break
        Some((v, p)) => {
          stack.push(v)
          pos = p
        }
      }
      continue
    }
    let op = if b0 == 12 {
      if pos >= data.length() {
        break
      }
      let b1 = data.at(pos).to_int()
      pos = pos + 1
      1200 + b1
    } else {
      b0
    }
    match op {
      // CharStrings offset
      17 =>
        if stack.length() >= 1 {
          info.charstrings_offset = Some(stack.at(stack.length() - 1))
        }
      // CFF2 VarStore offset (ItemVariationStore)
      24 =>
        if stack.length() >= 1 {
          info.var_store_offset = Some(stack.at(stack.length() - 1))
        }
      // Private DICT range: size, offset
      18 =>
        if stack.length() >= 2 {
          info.private_size = Some(stack.at(0))
          info.private_offset = Some(stack.at(1))
        }
      // Subrs offset (within Private DICT)
      19 =>
        if stack.length() >= 1 {
          info.subrs_offset = Some(stack.at(stack.length() - 1))
        }
      _ => ()
    }
    stack.clear()
  }
  info
}