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

///|
struct PointDelta {
  mut x : Double
  mut y : Double
  mut referenced : Bool
} derive(Show, ToJson)

fn clone_points(points : Array[@sfnt.GlyphPoint]) -> Array[@sfnt.GlyphPoint] {
  let out : Array[@sfnt.GlyphPoint] = []
  for point in points {
    out.push(@sfnt.GlyphPoint::new(
      point.x,
      point.y,
      point.flag,
      point.is_end_point,
    ))
  }
  out
}

fn make_deltas(count : Int) -> Array[PointDelta] {
  let deltas : Array[PointDelta] = []
  for _ in 0.. Int {
  if i >= end { start } else { i + 1 }
}

fn infer_delta(
  orig_points : Array[@sfnt.GlyphPoint],
  deltas : Array[PointDelta],
  target : Int,
  prev : Int,
  next : Int,
  use_x : Bool,
) -> Double {
  let prev_val = if use_x { orig_points[prev].x } else { orig_points[prev].y }
  let next_val = if use_x { orig_points[next].x } else { orig_points[next].y }
  let target_val = if use_x { orig_points[target].x } else { orig_points[target].y }
  let prev_delta = if use_x { deltas[prev].x } else { deltas[prev].y }
  let next_delta = if use_x { deltas[next].x } else { deltas[next].y }
  if prev_val == next_val {
    return if prev_delta == next_delta { prev_delta } else { 0.0 }
  }
  let min_val = if prev_val < next_val { prev_val } else { next_val }
  let max_val = if prev_val > next_val { prev_val } else { next_val }
  if target_val <= min_val {
    return if prev_val < next_val { prev_delta } else { next_delta }
  }
  if target_val >= max_val {
    return if prev_val > next_val { prev_delta } else { next_delta }
  }
  let ratio = (target_val - prev_val) / (next_val - prev_val)
  prev_delta + ratio * (next_delta - prev_delta)
}

fn iup_interpolate(
  orig_points : Array[@sfnt.GlyphPoint],
  deltas : Array[PointDelta],
) -> Unit {
  let count = orig_points.length()
  let mut start = 0
  let mut end = 0
  while true {
    while end < count && !orig_points[end].is_end_point {
      end = end + 1
    }
    if end >= count {
      break
    }
    let refs : Array[Int] = []
    for i in start..=end {
      if deltas[i].referenced {
        refs.push(i)
      }
    }
    let contour_len = end - start + 1
    if refs.length() > 0 && refs.length() < contour_len {
      let ref_count = refs.length()
      for r in 0.. Result[Unit, @ot_var.VarError] {
  let tuple_data = match gvar.glyph_tuple_variations(glyph) {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match tuple_data {
    None => Ok(())
    Some(data) => {
      let tuples = match data.decode(points.length(), true) {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      let mut orig_points : Array[@sfnt.GlyphPoint]? = None
      for tuple in tuples {
        let scalar = tuple.scalar(coords)
        if scalar == 0.0 {
          continue
        }
        match tuple.indices {
          None => {
            let deltas_y = match tuple.deltas_y {
              None => []
              Some(values) => values
            }
            for i in 0.. {
            if orig_points is None {
              orig_points = Some(clone_points(points))
            }
            let deltas = make_deltas(points.length())
            let deltas_y = match tuple.deltas_y {
              None => []
              Some(values) => values
            }
            for i in 0..= points.length() {
                continue
              }
              if i < tuple.deltas_x.length() {
                deltas[idx].x =
                  deltas[idx].x + tuple.deltas_x[i].to_double() * scalar
              }
              if i < deltas_y.length() {
                deltas[idx].y =
                  deltas[idx].y + deltas_y[i].to_double() * scalar
              }
              deltas[idx].referenced = true
            }
            match orig_points {
              None => ()
              Some(original) => iup_interpolate(original, deltas)
            }
            for i in 0.. Unit {
  if a == 1.0 && b == 0.0 && c == 0.0 && d == 1.0 {
    return
  }
  for point in points {
    point.transform(a, b, c, d)
  }
}

fn translate_points(
  points : Array[@sfnt.GlyphPoint],
  dx : Double,
  dy : Double,
) -> Unit {
  if dx == 0.0 && dy == 0.0 {
    return
  }
  for point in points {
    point.translate(dx, dy)
  }
}

fn apply_component_transform(
  points : Array[@sfnt.GlyphPoint],
  component : @sfnt.CompositeComponent,
  trans_point : @sfnt.GlyphPoint,
) -> Unit {
  if component.scaled_offset {
    translate_points(points, trans_point.x, trans_point.y)
    transform_points(points, component.a, component.b, component.c, component.d)
  } else {
    transform_points(points, component.a, component.b, component.c, component.d)
    translate_points(points, trans_point.x, trans_point.y)
  }
}

fn build_phantom_points(
  bounds : @sfnt.GlyphBounds?,
  hmtx : @sfnt.HmtxTable,
  vmtx : @sfnt.VmtxTable?,
  glyph : Int,
) -> Result[Array[@sfnt.GlyphPoint], FontError] {
  if glyph < 0 || glyph >= hmtx.advances.length() {
    return Err(GlyphOutOfRange)
  }
  let x_min = match bounds {
    None => 0
    Some(value) => value.x_min
  }
  let y_max = match bounds {
    None => 0
    Some(value) => value.y_max
  }
  let lsb = hmtx.lsbs[glyph]
  let h_adv = hmtx.advances[glyph]
  let h_delta = x_min - lsb
  let mut tsb = 0
  let mut v_adv = 0
  match vmtx {
    None => ()
    Some(table) => {
      if glyph < 0 || glyph >= table.advances.length() {
        return Err(GlyphOutOfRange)
      }
      tsb = table.tsbs[glyph]
      v_adv = table.advances[glyph]
    }
  }
  let v_orig = y_max + tsb
  let phantoms : Array[@sfnt.GlyphPoint] = []
  phantoms.push(@sfnt.GlyphPoint::new(
    h_delta.to_double(),
    0.0,
    0,
    false,
  ))
  phantoms.push(@sfnt.GlyphPoint::new(
    (h_adv + h_delta).to_double(),
    0.0,
    0,
    false,
  ))
  phantoms.push(@sfnt.GlyphPoint::new(
    0.0,
    v_orig.to_double(),
    0,
    false,
  ))
  phantoms.push(@sfnt.GlyphPoint::new(
    0.0,
    (v_orig - v_adv).to_double(),
    0,
    false,
  ))
  Ok(phantoms)
}

fn split_phantoms(
  points : Array[@sfnt.GlyphPoint],
) -> (Array[@sfnt.GlyphPoint], Array[@sfnt.GlyphPoint]) {
  let mut outline_len = points.length()
  if outline_len >= 4 {
    outline_len = outline_len - 4
  } else {
    outline_len = 0
  }
  let outlines : Array[@sfnt.GlyphPoint] = []
  let phantoms : Array[@sfnt.GlyphPoint] = []
  for i in 0.. Array[@sfnt.GlyphPoint] {
  if points.length() >= 4 {
    return points
  }
  let out : Array[@sfnt.GlyphPoint] = []
  for point in points {
    out.push(point)
  }
  while out.length() < 4 {
    out.push(@sfnt.GlyphPoint::new(0.0, 0.0, 0, false))
  }
  out
}

fn glyph_bounds_from_points(
  points : Array[@sfnt.GlyphPoint],
  contour_count : Int,
  contour_points : Int,
) -> @sfnt.GlyphBounds? {
  if contour_points <= 0 || contour_points > points.length() {
    return None
  }
  let mut min_x = points[0].x
  let mut max_x = points[0].x
  let mut min_y = points[0].y
  let mut max_y = points[0].y
  for i in 1.. max_x { max_x = p.x }
    if p.y < min_y { min_y = p.y }
    if p.y > max_y { max_y = p.y }
  }
  let x_min = @math.floor(min_x).to_int()
  let y_min = @math.floor(min_y).to_int()
  let x_max = @math.ceil(max_x).to_int()
  let y_max = @math.ceil(max_y).to_int()
  Some(@sfnt.GlyphBounds::new(contour_count, x_min, y_min, x_max, y_max))
}

fn Font::glyph_points_with_gvar_depth(
  self : Font,
  glyph : UInt,
  coords : Array[Int],
  depth : Int,
) -> Result[Array[@sfnt.GlyphPoint]?, FontError] {
  if depth <= 0 {
    return Ok(None)
  }
  let maxp = match self.ensure_maxp() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let index = glyph.reinterpret_as_int()
  if index < 0 || index >= maxp.num_glyphs {
    return Err(GlyphOutOfRange)
  }
  let loca = match self.ensure_loca() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let glyf = match self.ensure_glyf() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let offsets = loca.offsets
  if index + 1 >= offsets.length() {
    return Err(GlyphOutOfRange)
  }
  let start = offsets[index]
  let end = offsets[index + 1]
  let length = end - start
  let bounds = match glyf.glyph_bounds(start, length) {
    Err(err) => return Err(Parse(err))
    Ok(value) => value
  }
  let simple_points = match glyf.glyph_simple_points(start, length) {
    Err(err) => return Err(Parse(err))
    Ok(value) => value
  }
  match simple_points {
    None => {
      let components = match glyf.glyph_composite_components(start, length) {
        Err(err) => return Err(Parse(err))
        Ok(value) => value
      }
      match components {
        None => Ok(None)
        Some(components) => {
          let hmtx = match self.ensure_hmtx() {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          let vmtx = match self.vmtx() {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          let trans_points : Array[@sfnt.GlyphPoint] = []
          for component in components {
            trans_points.push(@sfnt.GlyphPoint::new(
              component.tx,
              component.ty,
              0,
              true,
            ))
          }
          let phantoms = match build_phantom_points(bounds, hmtx, vmtx, index) {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          let var_points : Array[@sfnt.GlyphPoint] = []
          for point in trans_points {
            var_points.push(point)
          }
          for phantom in phantoms {
            var_points.push(phantom)
          }
          if !coords.is_empty() {
            let gvar = match self.gvar() {
              Err(err) => return Err(err)
              Ok(value) => value
            }
            match gvar {
              None => ()
              Some(gvar) =>
                match apply_gvar_deltas_to_points(var_points, coords, gvar, index) {
                  Err(err) => return Err(Var(err))
                  Ok(_) => ()
                }
            }
          }
          let mut parent_phantoms = ensure_four_phantoms(
            var_points[var_points.length() - 4:var_points.length()].to_array(),
          )
          let all_points : Array[@sfnt.GlyphPoint] = []
          let mut comp_index = 0
          for component in components {
            if comp_index >= var_points.length() {
              break
            }
            let comp_gid = if component.glyph < 0 {
              None
            } else {
              Some(component.glyph.reinterpret_as_uint())
            }
            match comp_gid {
              None => ()
              Some(comp_gid) => {
                let comp_points = match self.glyph_points_with_gvar_depth(
                  comp_gid,
                  coords,
                  depth - 1,
                ) {
                  Err(err) => return Err(err)
                  Ok(value) => value
                }
                match comp_points {
                  None => ()
                  Some(comp_points) => {
                    let (comp_outline, comp_phantoms) = split_phantoms(comp_points)
                    if component.use_my_metrics && comp_phantoms.length() >= 4 {
                      parent_phantoms = comp_phantoms
                    }
                    if !comp_outline.is_empty() {
                      apply_component_transform(
                        comp_outline,
                        component,
                        var_points[comp_index],
                      )
                      if component.is_anchored {
                        let p1 = component.arg1
                        let p2 = component.arg2
                        if p1 >= 0 && p1 < all_points.length() &&
                          p2 >= 0 && p2 < comp_outline.length() {
                          let dx = all_points[p1].x - comp_outline[p2].x
                          let dy = all_points[p1].y - comp_outline[p2].y
                          translate_points(comp_outline, dx, dy)
                        }
                      }
                      for point in comp_outline {
                        all_points.push(point)
                      }
                    }
                  }
                }
              }
            }
            comp_index = comp_index + 1
          }
          for phantom in parent_phantoms {
            all_points.push(phantom)
          }
          Ok(Some(all_points))
        }
      }
    }
    Some(points) => {
      let points = points
      let hmtx = match self.ensure_hmtx() {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      let vmtx = match self.vmtx() {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      let phantoms = match build_phantom_points(bounds, hmtx, vmtx, index) {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      for phantom in phantoms {
        points.push(phantom)
      }
      if !coords.is_empty() {
        let gvar = match self.gvar() {
          Err(err) => return Err(err)
          Ok(value) => value
        }
        match gvar {
          None => ()
          Some(gvar) =>
            match apply_gvar_deltas_to_points(points, coords, gvar, index) {
              Err(err) => return Err(Var(err))
              Ok(_) => ()
            }
        }
      }
      Ok(Some(points))
    }
  }
}

fn Font::glyph_points_with_gvar(
  self : Font,
  glyph : UInt,
  coords : Array[Int],
) -> Result[Array[@sfnt.GlyphPoint]?, FontError] {
  self.glyph_points_with_gvar_depth(glyph, coords, 8)
}

fn glyph_bounds_from_points_for_font(
  points : Array[@sfnt.GlyphPoint],
) -> @sfnt.GlyphBounds? {
  let mut contour_count = 0
  let mut contour_points = points.length()
  if contour_points >= 4 {
    contour_points = contour_points - 4
  }
  for i in 0.. Result[Int?, FontError] {
  let points = match self.glyph_points_with_gvar(glyph, coords) {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match points {
    None => Ok(None)
    Some(points) => {
      if points.length() < 4 {
        return Ok(None)
      }
      let left = points[points.length() - 4].x
      let right = points[points.length() - 3].x
      let top = points[points.length() - 2].y
      let bottom = points[points.length() - 1].y
      if vertical {
        Ok(Some((top - bottom).round().to_int()))
      } else {
        Ok(Some((right - left).round().to_int()))
      }
    }
  }
}

///|
/// Return glyph contour point in font units if available.
pub fn Font::glyph_contour_point_for_origin(
  self : Font,
  glyph : UInt,
  point_index : Int,
) -> Result[(Int, Int)?, FontError] {
  if point_index < 0 {
    return Ok(None)
  }
  let loca_blob = match self.face.reference_table_optional(tag_loca) {
    Err(err) => return Err(FaceTable(err))
    Ok(value) => value
  }
  let glyf_blob = match self.face.reference_table_optional(tag_glyf) {
    Err(err) => return Err(FaceTable(err))
    Ok(value) => value
  }
  if loca_blob is None || glyf_blob is None {
    return Ok(None)
  }
  let coords : Array[Int] = match self.var_coords_norm {
    None => []
    Some(value) => value
  }
  let points = match self.glyph_points_with_gvar(glyph, coords) {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match points {
    None => Ok(None)
    Some(points) => {
      let mut outline_len = points.length()
      if outline_len >= 4 {
        outline_len = outline_len - 4
      } else {
        outline_len = 0
      }
      if point_index < 0 || point_index >= outline_len {
        return Ok(None)
      }
      let point = points[point_index]
      Ok(Some((point.x.round().to_int(), point.y.round().to_int())))
    }
  }
}