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

///|
/// AAT shaping helpers (morx/kerx/kern).
///
/// Ported from `upstream swash/src/shape/aat.rs`.

///|
fn selectors_contains(
  selectors : ArrayView[(UInt16, UInt16)],
  key : (UInt16, UInt16),
) -> Bool {
  let (ks, kv) = key
  let key_u = (ks.to_int().reinterpret_as_uint() << 16) |
    kv.to_int().reinterpret_as_uint()
  let mut l = 0
  let mut h = selectors.length()
  while l < h {
    let mid = (l + h) / 2
    let (s, v) = selectors[mid]
    let mid_u = (s.to_int().reinterpret_as_uint() << 16) |
      v.to_int().reinterpret_as_uint()
    if key_u > mid_u {
      l = mid + 1
    } else if key_u < mid_u {
      h = mid
    } else {
      return true
    }
  }
  false
}

///|
fn first_non_joining_index(buffer : Buffer) -> Int32? {
  for i in 0.. Unit {
  let max_ops = buffer.glyphs.length() * 16
  for chain in @internal.morx_chains(data, morx).iter() {
    let mut ops = 0
    let mut flags = chain.default_flags()
    if selectors.length() != 0 {
      for feature in chain.features() {
        let key = (feature.selector, feature.setting_selector)
        if selectors_contains(selectors, key) {
          flags = (flags & feature.disable_flags) | feature.enable_flags
        }
      }
    }
    for subtable in chain.subtables().iter() {
      if (subtable.flags() & flags) == 0U {
        continue
      }
      let reverse = subtable.should_reverse(buffer.is_rtl)
      buffer.ensure_order(reverse)
      let kind = match subtable.kind() {
        None => continue
        Some(v) => v
      }
      match kind {
        Rearrangement(t) => {
          let mut i = 0
          let state = @internal.MorxRearrangementState::MorxRearrangementState()
          while i < buffer.glyphs.length() && ops < max_ops {
            let gid = buffer.glyphs[i].id
            match
              t.next(state, i, gid, false, fn(r) {
                r.apply(buffer.glyphs)
                Some(())
              }) {
              None => break
              Some(advance) => i = i + advance
            }
            ops = ops + 1
          }
          // END_OF_TEXT state.
          t.next(state, i, (0).to_uint16(), true, fn(r) {
            r.apply(buffer.glyphs)
            Some(())
          })
          |> ignore
        }
        Contextual(t) => {
          let state = @internal.MorxContextualState::MorxContextualState()
          for i in 0.. ignore
          }
          // END_OF_TEXT state.
          let len = buffer.glyphs.length()
          if len != 0 {
            let last_ix = len - 1
            let last_id = buffer.glyphs[last_ix].id
            t.next(state, last_ix, last_id, true, fn(ix, g) {
              buffer.substitute(ix, g)
              Some(())
            })
            |> ignore
          }
        }
        NonContextual(t) =>
          for i in 0.. ()
              Some(s) => buffer.glyphs[i].id = s
            }
          }
        Ligature(t) => {
          let mut i = 0
          let state = @internal.MorxLigatureState::MorxLigatureState()
          while i < buffer.glyphs.length() && ops < max_ops {
            let gid = buffer.glyphs[i].id
            if t.next(state, i, gid, false, fn(ix, g, comps) {
                buffer.substitute_ligature(ix, g, comps)
                Some(())
              })
              is None {
              break
            }
            i = i + 1
            ops = ops + 1
          }
          // END_OF_TEXT state.
          let len = buffer.glyphs.length()
          let last_ix = if len == 0 { 0 } else { len - 1 }
          t.next(state, last_ix, (0).to_uint16(), true, fn(ix, g, comps) {
            buffer.substitute_ligature(ix, g, comps)
            Some(())
          })
          |> ignore
        }
        Insertion(t) => {
          let mut i = 0
          let state = @internal.MorxInsertionState::MorxInsertionState()
          while i < buffer.glyphs.length() && ops < max_ops {
            let gid = buffer.glyphs[i].id
            match
              t.next(state, i, gid, false, fn(ix, array) {
                let count = array.length()
                buffer.multiply(ix, count)
                let start = ix
                let end = start + count
                for j in start.. break
              Some(advance) => i = i + advance
            }
            ops = ops + 1
          }
          // END_OF_TEXT state.
          let len = buffer.glyphs.length()
          let last_ix = if len == 0 { 0 } else { len - 1 }
          t.next(state, last_ix, (0).to_uint16(), true, fn(ix, array) {
            let count = array.length()
            buffer.multiply(ix, count)
            let start = ix
            let end = start + count
            for j in start.. ignore
        }
      }
    }
  }
  buffer.ensure_order(false)
}

///|
pub fn apply_kerx(
  data : Bytes,
  kerx : UInt32,
  ankr : UInt32,
  buffer : Buffer,
  disable_kern : Bool,
) -> Unit {
  for subtable in @internal.kerx_subtables(data, kerx, ankr).iter() {
    let reverse = subtable.should_reverse(buffer.is_rtl)
    buffer.ensure_order(reverse)
    match subtable.kind() {
      None => continue
      Some(kind) => {
        if subtable.is_vertical() || subtable.is_cross_stream() {
          continue
        }
        match kind {
          Format0(t) => {
            if disable_kern {
              continue
            }
            let len = buffer.len()
            let left_index_opt = first_non_joining_index(buffer)
            let mut left_index = match left_index_opt {
              None => continue
              Some(v) => v
            }
            let mut left = buffer.glyphs[left_index].id
            for i in (left_index + 1).. ()
                Some(k) =>
                  if k != 0 {
                    buffer.positions[left_index].advance = buffer.positions[left_index].advance +
                      k.to_double()
                  }
              }
              left_index = i
              left = right
            }
          }
          Format1(t) => {
            if disable_kern {
              continue
            }
            let mut i = 0
            let len = buffer.glyphs.length()
            let state = @internal.KerxContextualState::KerxContextualState()
            while i < len {
              match
                t.next(state, i, buffer.glyphs[i].id, fn(ix, k) {
                  buffer.positions[ix].advance = buffer.positions[ix].advance +
                    k.to_double()
                  Some(())
                }) {
                None => break
                Some(advance) => i = i + advance
              }
            }
          }
          Format2(t) => {
            if disable_kern {
              continue
            }
            let len = buffer.len()
            let left_index_opt = first_non_joining_index(buffer)
            let mut left_index = match left_index_opt {
              None => continue
              Some(v) => v
            }
            let mut left = buffer.glyphs[left_index].id
            for i in (left_index + 1).. ()
                Some(k) =>
                  if k != 0 {
                    buffer.positions[left_index].advance = buffer.positions[left_index].advance +
                      k.to_double()
                  }
              }
              left_index = i
              left = right
            }
          }
          Format4(t) => {
            let mut i = 0
            let len = buffer.glyphs.length()
            let state = @internal.KerxFormat4State::KerxFormat4State()
            while i < len {
              match
                t.next(state, i, buffer.glyphs[i].id, fn(ix, base, x, y) {
                  buffer.position_mark(ix, base, x, y)
                  Some(())
                }) {
                None => break
                Some(advance) => i = i + advance
              }
            }
          }
        }
      }
    }
  }
  buffer.ensure_order(false)
}

///|
pub fn apply_kern(data : Bytes, kern : UInt32, buffer : Buffer) -> Unit {
  for subtable in @internal.kern_subtables(data, kern).iter() {
    let kind = match subtable.kind() {
      None => continue
      Some(v) => v
    }
    if !subtable.is_horizontal() {
      continue
    }
    buffer.ensure_order(buffer.is_rtl)
    let cross_stream = subtable.cross_stream()
    match kind {
      Format0(t) => {
        buffer.ensure_order(false)
        let len = buffer.len()
        let left_index_opt = first_non_joining_index(buffer)
        let mut left_index = match left_index_opt {
          None => continue
          Some(v) => v
        }
        let mut left = buffer.glyphs[left_index].id
        for i in (left_index + 1).. ()
            Some(k) =>
              if k != 0 {
                buffer.positions[left_index].advance = buffer.positions[left_index].advance +
                  k.to_double()
              }
          }
          left_index = i
          left = right
        }
      }
      Format1(t) => {
        let mut i = 0
        let len = buffer.glyphs.length()
        let state = @internal.KernFormat1State::KernFormat1State()
        while i < len {
          match
            t.next(state, i, buffer.glyphs[i].id, fn(ix, k) {
              let g = buffer.glyphs[ix]
              if g.joining_type == (6).to_byte() {
                if cross_stream {
                  if buffer.positions[ix].y == 0.0 {
                    buffer.positions[ix].y = k.to_double()
                  }
                } else {
                  match find_base(buffer, buffer.is_rtl, ix) {
                    None => ()
                    Some(base) => {
                      let diff = if base >= ix { base - ix } else { ix - base }
                      if diff < 255 {
                        if buffer.positions[ix].base == (0).to_byte() {
                          buffer.position_mark(ix, base, k.to_double(), 0.0)
                        }
                      }
                    }
                  }
                }
              }
              Some(())
            }) {
            None => break
            Some(advance) => i = i + advance
          }
        }
      }
    }
  }
  buffer.ensure_order(false)
}

///|
fn find_base(buffer : Buffer, reverse : Bool, index : Int32) -> Int32? {
  let cluster = buffer.glyphs[index].cluster
  if reverse {
    for i in (index + 1).. 0 {
    let mut i = index - 1
    while i >= 0 {
      let g = buffer.glyphs[i]
      if g.cluster != cluster {
        return None
      }
      if g.char_class is Base {
        return Some(i)
      }
      if i == 0 {
        break
      }
      i = i - 1
    }
  }
  None
}