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

///|
/// OpenType GSUB/GPOS shaping (AT).
///
/// Ported from `upstream swash/src/shape/at.rs`.
pub type FeatureBit = UInt16

///|
/// Feature mask: up to 256 feature bits packed into 4x u64 words.
pub struct FeatureMask {
  mut b0 : UInt64
  mut b1 : UInt64
  mut b2 : UInt64
  mut b3 : UInt64
}

///|
pub fn FeatureMask::default() -> FeatureMask {
  { b0: 0, b1: 0, b2: 0, b3: 0, }
}

///|
pub fn FeatureMask::is_empty(self : FeatureMask) -> Bool {
  self.b0 == 0 && self.b1 == 0 && self.b2 == 0 && self.b3 == 0
}

///|
pub fn FeatureMask::set(self : FeatureMask, bit : FeatureBit) -> Unit {
  let word = bit.to_int() / 64
  let shift = bit.to_int() & 63
  let mask : UInt64 = (1).to_uint64() << shift
  if word == 0 {
    self.b0 = self.b0 | mask
  } else if word == 1 {
    self.b1 = self.b1 | mask
  } else if word == 2 {
    self.b2 = self.b2 | mask
  } else {
    self.b3 = self.b3 | mask
  }
}

///|
pub fn FeatureMask::clear(self : FeatureMask, bit : FeatureBit) -> Unit {
  let word = bit.to_int() / 64
  let shift = bit.to_int() & 63
  let mask : UInt64 = (1).to_uint64() << shift
  let all = (-1).to_int64().reinterpret_as_uint64()
  let inv = all ^ mask
  if word == 0 {
    self.b0 = self.b0 & inv
  } else if word == 1 {
    self.b1 = self.b1 & inv
  } else if word == 2 {
    self.b2 = self.b2 & inv
  } else {
    self.b3 = self.b3 & inv
  }
}

///|
pub fn FeatureMask::has(self : FeatureMask, bit : FeatureBit) -> Bool {
  let word = bit.to_int() / 64
  let shift = bit.to_int() & 63
  let mask : UInt64 = (1).to_uint64() << shift
  if word == 0 {
    (self.b0 & mask) != 0
  } else if word == 1 {
    (self.b1 & mask) != 0
  } else if word == 2 {
    (self.b2 & mask) != 0
  } else {
    (self.b3 & mask) != 0
  }
}

///|
/// Returns the union of two masks.
pub fn FeatureMask::or(self : FeatureMask, other : FeatureMask) -> FeatureMask {
  {
    b0: self.b0 | other.b0,
    b1: self.b1 | other.b1,
    b2: self.b2 | other.b2,
    b3: self.b3 | other.b3,
  }
}

///|
/// Mutating OR assignment.
pub fn FeatureMask::or_assign(self : FeatureMask, other : FeatureMask) -> Unit {
  self.b0 = self.b0 | other.b0
  self.b1 = self.b1 | other.b1
  self.b2 = self.b2 | other.b2
  self.b3 = self.b3 | other.b3
}

///|
/// Masks or bits for specific feature groups.
pub struct FeatureGroups {
  default : FeatureMask
  mut reph : FeatureBit?
  mut pref : FeatureBit?
  stage1 : FeatureMask
  stage2 : FeatureMask
  basic : FeatureMask
  position : FeatureMask
  vert : FeatureMask
  rtl : FeatureMask
}

///|
pub fn FeatureGroups::default() -> FeatureGroups {
  {
    default: FeatureMask::default(),
    reph: None,
    pref: None,
    stage1: FeatureMask::default(),
    stage2: FeatureMask::default(),
    basic: FeatureMask::default(),
    position: FeatureMask::default(),
    vert: FeatureMask::default(),
    rtl: FeatureMask::default(),
  }
}

///|
pub fn feature_bit_to_mask(bit : FeatureBit?) -> FeatureMask {
  match bit {
    None => FeatureMask::default()
    Some(b) => {
      let m = FeatureMask::default()
      m.set(b)
      m
    }
  }
}

///|
/// Offsets for a particular layout stage.
pub struct StageOffsets {
  base : UInt32
  lang : UInt32
  vars : UInt32
}

///|
pub fn StageOffsets::default() -> StageOffsets {
  { base: 0U, lang: 0U, vars: 0U, }
}

///|
pub fn StageOffsets::from_layout(
  data : Bytes,
  base : UInt32,
  script : UInt32,
  language : UInt32?,
) -> (StageOffsets, (UInt32, UInt32))? {
  match language_or_default_by_tags(data, base, script, language) {
    None => None
    Some((lang, tags)) => {
      let vars = feature_var_offset(data, base)
      Some(({ base, lang, vars, }, tags))
    }
  }
}

///|
fn language_or_default_by_tags(
  data : Bytes,
  base : UInt32,
  script : UInt32,
  language : UInt32?,
) -> (UInt32, (UInt32, UInt32))? {
  let script_offset_opt = @internal.script_by_tag(data, base, script)
  let (script_offset, script_tag) = match script_offset_opt {
    Some(off) => (off, script)
    None =>
      match @internal.script_by_tag(data, base, @internal.DFLT) {
        None => return None
        Some(off) => (off, @internal.DFLT)
      }
  }
  let (lang_offset, used_default) = match
    @internal.script_language_by_tag(data, script_offset, language) {
    None => return None
    Some(r) => r
  }
  let lang_tag = if used_default {
    @internal.DFLT
  } else {
    match language {
      None => @internal.DFLT
      Some(t) => t
    }
  }
  Some((lang_offset, (script_tag, lang_tag)))
}

///|
fn feature_var_offset(data : Bytes, gsubgpos_offset : UInt32) -> UInt32 {
  if gsubgpos_offset == 0U {
    return 0U
  }
  let base = gsubgpos_offset.reinterpret_as_int()
  let b = @internal.BeBytes::BeBytes(data)
  let major = b.read_u16(base).unwrap_or(0U)
  let minor = b.read_u16(base + 2).unwrap_or(0U)
  if major > 1U || (major == 1U && minor >= 1U) {
    let offset = b.read_u32(base + 10).unwrap_or(0U)
    if offset != 0U {
      gsubgpos_offset + offset
    } else {
      0U
    }
  } else {
    0U
  }
}

///|
/// Maximum number of features that are allowed per stage.
const MAX_CACHED_FEATURES : Int32 = 256

// These limits are used by the lookup application engine (ported later).

///|
const MAX_NESTED_LOOKUPS : Int32 = 4

///|
const MAX_SEQUENCE : Int32 = 32

///|
/// Cache of features, lookups and subtables for a particular script/language pair.
///
/// Only the feature-group plumbing is ported in this step; lookup application
/// is implemented as part of the AT shaper task.
pub struct FeatureStore {
  sub_features : Array[(UInt32, FeatureBit)]
  pos_features : Array[(UInt32, FeatureBit)]
  lookups : Array[@internal.LookupData]
  subtables : Array[@internal.SubtableData]
  coverage : Array[UInt16]
  mut pos_start : Int32
  mut sub_count : Int32
  mut truncated : Bool
  mut groups : FeatureGroups
}

///|
/// A growable bitset used by the OT coverage cache builder.
priv struct BitSet {
  list : Array[UInt16]
  bits : Array[UInt64]
}

///|
fn BitSet::default() -> BitSet {
  { list: ([] : Array[UInt16]), bits: ([] : Array[UInt64]), }
}

///|
fn BitSet::clear(self : BitSet) -> Unit {
  self.list.clear()
  for i in 0.. Bool {
  let v = value.to_int()
  let index = v / 64
  let shift = v & 63
  let bit = (1).to_uint64() << shift
  if index >= self.bits.length() {
    self.bits.resize(index + 8, (0).to_uint64())
    self.bits.set(index, self.bits[index] | bit)
    self.list.push(value)
    true
  } else {
    let word = self.bits[index]
    if (word & bit) != 0 {
      false
    } else {
      self.bits.set(index, word | bit)
      self.list.push(value)
      true
    }
  }
}

///|
/// Builds a compact coverage cache used to cheaply skip lookups.
///
/// Format matches `upstream swash/src/shape/at.rs`:
/// `[min, max, bitset words (u16)]`, keyed by base index into `FeatureStore.coverage`.
struct CoverageBuilder {
  coverage : BitSet
  mut min : UInt16
  mut max : UInt16
}

///|
fn CoverageBuilder::default() -> CoverageBuilder {
  { coverage: BitSet::default(), min: 0xFFFFU.to_uint16(), max: 0, }
}

///|
fn CoverageBuilder::begin(self : CoverageBuilder) -> Unit {
  self.coverage.clear()
  self.min = 0xFFFFU.to_uint16()
  self.max = 0
}

///|
fn CoverageBuilder::add(self : CoverageBuilder, glyph_id : UInt16) -> Unit {
  if self.coverage.insert(glyph_id) {
    if glyph_id < self.min {
      self.min = glyph_id
    }
    if glyph_id > self.max {
      self.max = glyph_id
    }
  }
}

///|
fn CoverageBuilder::add_coverage(
  self : CoverageBuilder,
  data : Bytes,
  base : Int32,
) -> Unit? {
  let b = @internal.BeBytes::BeBytes(data)
  let fmt = match b.read_u16(base) {
    None => return None
    Some(v) => v.reinterpret_as_int()
  }
  let len = match b.read_u16(base + 2) {
    None => return None
    Some(v) => v.reinterpret_as_int()
  }
  let arr = base + 4
  if fmt == 1 {
    for i in 0.. return None
        Some(v) => v.to_uint16()
      }
      self.add(g)
    }
  } else if fmt == 2 {
    for i in 0.. return None
        Some(v) => v.to_uint16()
      }
      let last = match b.read_u16(rec + 2) {
        None => return None
        Some(v) => v.to_uint16()
      }
      let mut g = first.to_int()
      let end = last.to_int()
      while g <= end {
        self.add(g.to_uint16())
        g = g + 1
      }
    }
  } else {
    return None
  }
  Some(())
}

///|
fn CoverageBuilder::finish(
  self : CoverageBuilder,
  out : Array[UInt16],
) -> UInt32 {
  if self.coverage.list.length() == 0 {
    return 0xFFFFFFFFU
  }
  let key = out.length().reinterpret_as_uint()
  out.push(self.min)
  out.push(self.max)
  let bit_base = out.length()
  let range_len = self.max.to_int() - self.min.to_int() + 1
  out.resize(out.length() + (range_len + 15) / 16, (0).to_uint16())
  for g in self.coverage.list.iter() {
    let bit = g.to_int() - self.min.to_int()
    let idx = bit_base + bit / 16
    let word = out[idx]
    out.set(idx, word | ((1).to_uint16() << (bit & 15)))
  }
  key
}

///|
/// Builder for a feature cache.
pub struct FeatureStoreBuilder {
  indices : Array[(UInt16, FeatureBit, Byte)]
  coverage : CoverageBuilder
  mut next_bit : FeatureBit
}

///|
pub fn FeatureStoreBuilder::default() -> FeatureStoreBuilder {
  { indices: [], coverage: CoverageBuilder::default(), next_bit: 0, }
}

///|
pub fn FeatureStoreBuilder::build(
  self : FeatureStoreBuilder,
  store : Ref[FeatureStore],
  data : Bytes,
  coords : ArrayView[Int32],
  gdef : @internal.Gdef,
  gsub : StageOffsets,
  gpos : StageOffsets,
) -> Unit {
  store.val.clear()
  if gsub.base != 0U {
    FeatureStoreBuilder::build_stage(
      self,
      store,
      data,
      coords,
      gdef,
      gsub,
      (0).to_byte(),
    )
    store.val.sub_features.sort_by((a, b) => {
      if a.0 < b.0 {
        -1
      } else if a.0 > b.0 {
        1
      } else {
        0
      }
    })
  }
  store.val.sub_count = store.val.sub_features.length()
  store.val.pos_start = store.val.lookups.length()
  if gpos.base != 0U {
    FeatureStoreBuilder::build_stage(
      self,
      store,
      data,
      coords,
      gdef,
      gpos,
      (1).to_byte(),
    )
    store.val.pos_features.sort_by((a, b) => {
      if a.0 < b.0 {
        -1
      } else if a.0 > b.0 {
        1
      } else {
        0
      }
    })
  }
}

///|
fn FeatureStoreBuilder::build_stage(
  self : FeatureStoreBuilder,
  store : Ref[FeatureStore],
  data : Bytes,
  coords : ArrayView[Int32],
  gdef : @internal.Gdef,
  offsets : StageOffsets,
  stage : Byte,
) -> Unit {
  self.next_bit = 0
  self.indices.clear()
  let gdef_opt : @internal.Gdef? = if gdef.ok() { Some(gdef) } else { None }
  let base = offsets.base.reinterpret_as_int()
  let b = @internal.BeBytes::BeBytes(data)
  let lbase = offsets.lang.reinterpret_as_int()
  let list_base = match b.read_u16(base + 8) {
    None => return
    Some(v) => offsets.base + v
  }
  let vars : @internal.FeatureSubsts? = @internal.FeatureSubsts::from_data(
    data,
    offsets.vars,
    coords,
  )
  let fbase = match b.read_u16(base + 6) {
    None => return
    Some(v) => base + v.reinterpret_as_int()
  }
  let count = match b.read_u16(lbase + 4) {
    None => return
    Some(v) => v.reinterpret_as_int()
  }
  let actual_count = if count > MAX_CACHED_FEATURES {
    MAX_CACHED_FEATURES
  } else {
    count
  }
  if actual_count < count {
    store.val.truncated = true
  }
  let features = if stage == (0).to_byte() {
    store.val.sub_features
  } else {
    store.val.pos_features
  }
  for i in 0.. return
      Some(v) => v.to_uint16()
    }
    let rec = fbase + 2 + findex.to_int() * 6
    let ftag = match b.read_u32(rec) {
      None => return
      Some(v) => v
    }
    let fbit = self.next_bit
    self.next_bit = self.next_bit + 1
    let mask : Byte = if stage == (0).to_byte() {
      match ftag {
        ISOL => ISOL_MASK
        INIT => INIT_MASK
        MEDI => MEDI_MASK
        FINA => FINA_MASK
        MED2 => MED2_MASK
        FIN2 => FIN2_MASK
        FIN3 => FIN3_MASK
        LJMO => LJMO_MASK
        VJMO => VJMO_MASK
        TJMO => TJMO_MASK
        _ => NONE_MASK
      }
    } else {
      (0).to_byte()
    }
    features.push((ftag, fbit))
    let foffset = match vars {
      None => fbase + b.read_u16(rec + 4).unwrap_or(0).reinterpret_as_int()
      Some(v) =>
        match v.apply(data, findex) {
          None => fbase + b.read_u16(rec + 4).unwrap_or(0).reinterpret_as_int()
          Some(off) => off.reinterpret_as_int()
        }
    }
    let lcount = match b.read_u16(foffset + 2) {
      None => return
      Some(v) => v.reinterpret_as_int()
    }
    for j in 0.. return
        Some(v) => v.to_uint16()
      }
      self.indices.push((lookup_index, fbit, mask))
    }
  }
  self.indices.sort_by((a, b) => a.0.to_int() - b.0.to_int())
  let mut last_index : UInt16? = (None : UInt16?)
  for triple in self.indices.iter() {
    let (index, feature, lmask) = triple
    match last_index {
      Some(li) =>
        if li == index {
          if store.val.lookups.length() != 0 {
            let last = store.val.lookups[store.val.lookups.length() - 1]
            let lookup = last
            lookup.set_feature(feature)
            store.val.lookups.push(lookup)
          }
          continue
        }
      None => ()
    }
    match
      @internal.lookup_data(data, stage, list_base, index, lmask, gdef_opt) {
      None => ()
      Some(lookup0) => {
        let start = store.val.subtables.length()
        self.coverage.begin()
        let lookup = lookup0
        match
          FeatureStoreBuilder::collect_subtables(self, store, data, lookup) {
          Some(true) => {
            lookup.set_coverage(self.coverage.finish(store.val.coverage))
            lookup.set_feature(feature)
            store.val.lookups.push(lookup)
            last_index = Some(index)
          }
          _ =>
            // Failed to collect subtables or none were recognized.
            store.val.subtables.truncate(start)
        }
      }
    }
  }
}

///|
fn FeatureStoreBuilder::collect_subtables(
  self : FeatureStoreBuilder,
  store : Ref[FeatureStore],
  data : Bytes,
  lookup : @internal.LookupData,
) -> Bool? {
  let start = store.val.subtables.length()
  if start >= 0xFFFF {
    return None
  }
  let count = lookup.count.to_int()
  for i in 0.. ()
      Some(s) => {
        let base = s.offset.reinterpret_as_int() + s.coverage.to_int()
        match self.coverage.add_coverage(data, base) {
          None => return None
          Some(_) => ()
        }
        store.val.subtables.push(s)
      }
    }
  }
  let end = store.val.subtables.length()
  if end >= 0xFFFF {
    return None
  }
  lookup.set_subtables(start.to_uint16(), end.to_uint16())
  Some(end > start)
}

///|
/// Scratch storage used by the OT lookup application engine.
pub struct Storage {
  stack : Array[LookupState]
  ids : Array[UInt16]
  indices : Array[Int32]
}

///|
pub fn Storage::default() -> Storage {
  {
    stack: Array::makei(MAX_NESTED_LOOKUPS, _ => LookupState::default()),
    ids: Array::makei(MAX_SEQUENCE, _ => (0).to_uint16()),
    indices: Array::makei(MAX_SEQUENCE, _ => 0),
  }
}

///|
/// Lookup cursor state for nested lookups.
struct LookupState {
  mut skip_state : SkipState
  mut cur : Int32
  mut end : Int32
}

///|
fn LookupState::default() -> LookupState {
  { skip_state: SkipState::default(), cur: 0, end: 0, }
}

///|
/// Temporary 4-tuple used for GPOS value records.
priv struct Pos4 {
  mut x : Double
  mut y : Double
  mut xadvance : Double
  mut yadvance : Double
}

///|
fn Pos4::default() -> Pos4 {
  { x: 0.0, y: 0.0, xadvance: 0.0, yadvance: 0.0, }
}

///|
/// Popcount for u16 formats (used by value record sizing).
fn popcount_u16(v : UInt16) -> Int32 {
  let mut x = v.to_int() & 0xFFFF
  let mut n = 0
  while x != 0 {
    n = n + (x & 1)
    x = x >> 1
  }
  n
}

///|
/// Context used by the OT lookup application engine.
priv struct ApplyContext {
  stage : Byte
  data : Bytes
  gsubgpos : UInt32
  defs : @internal.Gdef
  coords : ArrayView[Int32]
  enable_var : Bool
  store : FeatureStore
  storage : Storage
  mut top : Byte
  mut arg : UInt16
  start : Int32
  mut end : Int32
  mut s : LookupState
  buf : Buffer
}

///|

///|
fn ApplyContext::ApplyContext(
  stage : Byte,
  data : Bytes,
  gsubgpos : UInt32,
  defs : @internal.Gdef,
  coords : ArrayView[Int32],
  store : FeatureStore,
  storage : Storage,
  buffer : Buffer,
  range : IndexRange,
) -> ApplyContext {
  {
    stage,
    data,
    gsubgpos,
    defs,
    coords,
    enable_var: defs.has_var_store() && coords.length() != 0,
    store,
    storage,
    top: (0).to_byte(),
    arg: (0).to_uint16(),
    start: range.start,
    end: range.end,
    s: LookupState::default(),
    buf: buffer,
  }
}

///|
fn ApplyContext::apply_skip_state(self : ApplyContext) -> Unit {
  let ss = self.s.skip_state
  let bs = self.buf.skip_state
  if ss.flags == bs.flags &&
    ss.mask == bs.mask &&
    ss.mark_check == bs.mark_check &&
    ss.mark_class == bs.mark_class &&
    ss.mark_set == bs.mark_set {
    return
  }
  self.buf.skip_state = ss
  self.update_glyphs_skip(None)
}

///|
fn ApplyContext::update_glyphs_skip(
  self : ApplyContext,
  range : IndexRange?,
) -> Unit {
  let (start, end) = match range {
    None => (0, self.buf.glyphs.length())
    Some(r) => (r.start, r.end)
  }
  let ss = self.s.skip_state
  let mask = ss.mask
  if ss.mark_check != (0).to_byte() {
    if ss.mark_set != 0U {
      for i in start.. Unit {
  if self.defs.has_mark_classes() {
    for i in start.. Unit {
  let ss = self.s.skip_state
  let mask = ss.mask
  let g = self.buf.glyphs[index]
  let class = self.defs.class(g.id).to_byte()
  g.class = class
  g.skip = (ss.flags & ((1).to_byte() << class.to_int())) != 0 ||
    (g.mask & mask) != mask
  if class == (3).to_byte() {
    g.mark_type = if self.defs.has_mark_classes() {
      self.defs.mark_class(g.id).to_byte()
    } else {
      (0).to_byte()
    }
    if ss.mark_check != (0).to_byte() && !g.skip {
      if ss.mark_set != 0U {
        g.skip = self.defs.mark_set_coverage(ss.mark_set, g.id) is None
      } else {
        g.skip = g.mark_type != ss.mark_class
      }
    }
  } else {
    g.mark_type = (0).to_byte()
  }
  self.buf.glyphs.set(index, g)
}

///|
fn ApplyContext::ignored(self : ApplyContext, index : Int32) -> Bool {
  self.buf.glyphs[index].skip
}

///|
fn ApplyContext::next(self : ApplyContext, index : Int32) -> Int32? {
  for i in (index + 1).. Int32? {
  if index > self.start {
    let mut i = index - 1
    while i >= self.start {
      if !self.ignored(i) {
        return Some(i)
      }
      if i == self.start {
        break
      }
      i = i - 1
    }
  }
  None
}

///|
fn ApplyContext::previous_base(self : ApplyContext, index : Int32) -> Int32? {
  if index > self.start {
    let mut i = index - 1
    while i >= self.start {
      if !self.ignored(i) {
        let class = self.buf.glyphs[i].class
        if class != (3).to_byte() {
          return Some(i)
        }
      }
      if i == self.start {
        break
      }
      i = i - 1
    }
  }
  None
}

///|
fn ApplyContext::move_first(self : ApplyContext) -> Bool {
  while self.s.cur < self.s.end {
    if !self.buf.glyphs[self.s.cur].skip {
      break
    }
    self.s.cur = self.s.cur + 1
  }
  self.s.cur < self.s.end
}

///|
fn ApplyContext::move_last(self : ApplyContext) -> Bool {
  if self.s.end == 0 {
    return false
  }
  self.s.cur = self.s.end - 1
  while self.ignored(self.s.cur) {
    if self.s.cur == 0 {
      return false
    }
    self.s.cur = self.s.cur - 1
  }
  true
}

///|
fn ApplyContext::move_next(self : ApplyContext) -> Bool {
  self.s.cur = self.s.cur + 1
  while self.s.cur < self.s.end {
    if !self.buf.glyphs[self.s.cur].skip {
      break
    }
    self.s.cur = self.s.cur + 1
  }
  self.s.cur < self.s.end
}

///|
fn ApplyContext::move_to(self : ApplyContext, index : Int32) -> Bool {
  if !self.move_first() {
    return false
  }
  for _ in 0.. Bool {
  let avail = self.s.end - self.s.cur
  if avail < len + 1 {
    return false
  }
  let mut collected = 0
  let mut i = self.s.cur + 1
  while i < self.s.end {
    let g = self.buf.glyphs[i]
    if !g.skip {
      self.storage.indices.set(collected, i)
      self.storage.ids.set(collected, g.id)
      collected = collected + 1
      if collected == len {
        return true
      }
    }
    i = i + 1
  }
  false
}

///|
fn ApplyContext::extend_range(self : ApplyContext, count : Int32) -> Unit {
  self.end = self.end + count
  self.s.end = self.s.end + count
  self.s.cur = self.s.cur + count
  let top = self.top.to_int()
  for i in 0.. Bool,
) -> Bool? {
  let mut idx = start
  for i in 0.. return None
      Some(p) => {
        idx = p
        if !pred(i, self.buf.glyphs[idx].id) {
          return None
        }
      }
    }
  }
  Some(true)
}

///|
fn ApplyContext::match_sequence(
  self : ApplyContext,
  start : Int32,
  len : Int32,
  pred : (Int32, UInt16) -> Bool,
) -> Int32? {
  let mut idx = start
  for i in 0.. return None
      Some(n) => {
        idx = n
        if !pred(i, self.buf.glyphs[idx].id) {
          return None
        }
      }
    }
  }
  Some(idx)
}

///|
fn ApplyContext::apply(
  self : ApplyContext,
  lookup : @internal.LookupData,
  subtables : (Int32, Int32),
  cur : Int32,
  end : Int32?,
  first : Int32,
) -> Bool? {
  let feature_index = lookup.feature.to_int()
  self.arg = if lookup.stage == (0).to_byte() {
    self.buf.sub_args[feature_index]
  } else {
    self.buf.pos_args[feature_index]
  }
  self.s.skip_state = {
    flags: lookup.ignored,
    mask: lookup.mask,
    mark_check: lookup.mark_check,
    mark_class: lookup.mark_class,
    mark_set: lookup.mark_set,
  }
  self.s.cur = cur
  self.s.end = end.unwrap_or(self.end)
  self.apply_skip_state()
  let mut applied = false
  let (sub_start, sub_end) = subtables
  if lookup.kind is RevChainContext {
    if !self.move_last() {
      return Some(false)
    }
    while true {
      let i = self.s.cur
      match self.buf.glyphs.get(i) {
        None => return None
        Some(gd) =>
          if !gd.skip {
            let id = gd.id
            if self.store.covers(lookup.coverage, id) {
              for t in sub_start.. ()
                  Some(ix) =>
                    if self.apply_subtable(s, ix.to_int(), i, id) == Some(true) {
                      applied = true
                      break
                    }
                }
              }
            }
          }
      }
      if self.s.cur == 0 {
        break
      }
      self.s.cur = self.s.cur - 1
    }
  } else {
    if !self.move_to(first) {
      return Some(false)
    }
    while self.s.cur < self.s.end {
      let i = self.s.cur
      match self.buf.glyphs.get(i) {
        None => return None
        Some(gd) =>
          if !gd.skip {
            let id = gd.id
            if self.store.covers(lookup.coverage, id) {
              for t in sub_start.. ()
                  Some(ix) =>
                    if self.apply_subtable(s, ix.to_int(), i, id) == Some(true) {
                      applied = true
                      break
                    }
                }
              }
            }
          }
      }
      self.s.cur = self.s.cur + 1
    }
  }
  Some(applied)
}

///|
fn ApplyContext::apply_subtable(
  self : ApplyContext,
  subtable : @internal.SubtableData,
  index : Int32,
  cur : Int32,
  g : UInt16,
) -> Bool? {
  let b = @internal.BeBytes::BeBytes(self.data)
  let kind = subtable.kind
  let base = subtable.offset.reinterpret_as_int()
  match kind {
    SingleSub1 => {
      let delta = match b.read_i16(base + 4) {
        None => return None
        Some(v) => v
      }
      let subst = (g.to_int() + delta).to_uint16()
      self.buf.substitute(cur, subst)
      self.update_glyph(cur)
      Some(true)
    }
    SingleSub2 => {
      let arr = base + 6
      let subst = match b.read_u16(arr + index * 2) {
        None => return None
        Some(v) => v.to_uint16()
      }
      self.buf.substitute(cur, subst)
      self.update_glyph(cur)
      Some(true)
    }
    MultiSub1 => {
      let seqbase = match b.read_u16(base + 6 + index * 2) {
        None => return None
        Some(v) => base + v.reinterpret_as_int()
      }
      let seqlen = match b.read_u16(seqbase) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      if seqlen > MAX_SEQUENCE {
        return Some(false)
      }
      let seqarr = seqbase + 2
      for i in 0.. return None
          Some(v) => v.to_uint16()
        }
        self.storage.ids.set(i, subst)
      }
      self.buf.substitute_multiple(cur, self.storage.ids[0:seqlen])
      self.update_glyphs(cur, cur + seqlen)
      self.extend_range(seqlen - 1)
      Some(true)
    }
    AltSub1 => {
      let offset = match b.read_u16(base + 6 + index * 2) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      if offset == 0 {
        return Some(false)
      }
      let arg = self.arg.to_int()
      let setbase = base + offset
      let count = match b.read_u16(setbase) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      if arg >= count {
        return Some(false)
      }
      let subst = match b.read_u16(setbase + 2 + arg * 2) {
        None => return None
        Some(v) => v.to_uint16()
      }
      self.buf.substitute(cur, subst)
      self.update_glyph(cur)
      Some(true)
    }
    LigSub1 => {
      let setbase = match b.read_u16(base + 6 + index * 2) {
        None => return None
        Some(v) => base + v.reinterpret_as_int()
      }
      let ligcount = match b.read_u16(setbase) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      let mut seqlen = 0
      for i in 0.. return None
          Some(v) => setbase + v.reinterpret_as_int()
        }
        let mut compcount = match b.read_u16(ligbase + 2) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        if compcount == 0 {
          continue
        }
        compcount = compcount - 1
        if compcount >= MAX_SEQUENCE {
          continue
        }
        let arr = ligbase + 4
        if seqlen < compcount {
          if !self.collect_sequence(compcount) {
            continue
          }
          seqlen = compcount
        }
        let mut matched = true
        for j in 0.. return None
            Some(v) => v.to_uint16()
          }
          if expected != self.storage.ids[j] {
            matched = false
            break
          }
        }
        if !matched {
          continue
        }
        let glyph = match b.read_u16(ligbase) {
          None => return None
          Some(v) => v.to_uint16()
        }
        self.buf.substitute_ligature(
          cur,
          glyph,
          self.storage.indices[0:compcount],
        )
        self.update_glyph(cur)
        return Some(true)
      }
      None
    }
    SingleAdj1 => {
      let pos = Pos4::default()
      let format = match b.read_u16(base + 4) {
        None => return None
        Some(v) => v.to_uint16()
      }
      match self.value_record(base, base + 6, format, pos) {
        None => return None
        Some(_) => ()
      }
      self.buf.position(cur, pos.x, pos.y, pos.xadvance, pos.yadvance)
      Some(true)
    }
    SingleAdj2 => {
      let vf = match b.read_u16(base + 4) {
        None => return None
        Some(v) => v.to_uint16()
      }
      let len = popcount_u16(vf) * 2
      let pos = Pos4::default()
      match self.value_record(base, base + 8 + index * len, vf, pos) {
        None => return None
        Some(_) => ()
      }
      self.buf.position(cur, pos.x, pos.y, pos.xadvance, pos.yadvance)
      Some(true)
    }
    PairAdj1 => {
      let next = match self.next(cur) {
        None => return None
        Some(v) => v
      }
      let g2 = self.buf.glyphs[next].id
      let vf1 = match b.read_u16(base + 4) {
        None => return None
        Some(v) => v.to_uint16()
      }
      let vf2 = match b.read_u16(base + 6) {
        None => return None
        Some(v) => v.to_uint16()
      }
      let len1 = popcount_u16(vf1) * 2
      let step = len1 + popcount_u16(vf2) * 2 + 2
      let setbase = match b.read_u16(base + 10 + index * 2) {
        None => return None
        Some(v) => base + v.reinterpret_as_int()
      }
      let count = match b.read_u16(setbase) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      let vbase = setbase + 2
      let mut l = 0
      let mut h = count
      while l < h {
        let i = (l + h) / 2
        let v = vbase + i * step
        let gv = match b.read_u16(v) {
          None => return None
          Some(x) => x.to_uint16()
        }
        if g2 > gv {
          l = i + 1
        } else if g2 < gv {
          h = i
        } else {
          if vf1 != 0 {
            let pos = Pos4::default()
            match self.value_record(setbase, v + 2, vf1, pos) {
              None => return None
              Some(_) => ()
            }
            self.buf.position(cur, pos.x, pos.y, pos.xadvance, pos.yadvance)
          }
          if vf2 != 0 {
            let pos = Pos4::default()
            match self.value_record(setbase, v + 2 + len1, vf2, pos) {
              None => return None
              Some(_) => ()
            }
            self.buf.position(next, pos.x, pos.y, pos.xadvance, pos.yadvance)
          }
          return Some(true)
        }
      }
      None
    }
    PairAdj2 => {
      let next = match self.next(cur) {
        None => return None
        Some(v) => v
      }
      let g2 = self.buf.glyphs[next].id
      let vf1 = match b.read_u16(base + 4) {
        None => return None
        Some(v) => v.to_uint16()
      }
      let vf2 = match b.read_u16(base + 6) {
        None => return None
        Some(v) => v.to_uint16()
      }
      let len1 = popcount_u16(vf1) * 2
      let step = len1 + popcount_u16(vf2) * 2
      let class1 = match b.read_u16(base + 8) {
        None => return None
        Some(v) => self.class(base + v.reinterpret_as_int(), g).to_int()
      }
      let class2 = match b.read_u16(base + 10) {
        None => return None
        Some(v) => self.class(base + v.reinterpret_as_int(), g2).to_int()
      }
      let class2_count = match b.read_u16(base + 14) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      let v = base + 16 + class1 * step * class2_count + class2 * step
      if vf1 != 0 {
        let pos = Pos4::default()
        match self.value_record(base, v, vf1, pos) {
          None => return None
          Some(_) => ()
        }
        self.buf.position(cur, pos.x, pos.y, pos.xadvance, pos.yadvance)
      }
      if vf2 != 0 {
        let pos = Pos4::default()
        match self.value_record(base, v + len1, vf2, pos) {
          None => return None
          Some(_) => ()
        }
        self.buf.position(next, pos.x, pos.y, pos.xadvance, pos.yadvance)
      }
      Some(true)
    }
    Cursive1 => {
      let next = match self.next(cur) {
        None => return None
        Some(v) => v
      }
      if next - cur > 255 {
        return Some(false)
      }
      let g2 = self.buf.glyphs[next].id
      let index2 = match subtable.coverage(self.data, g2) {
        None => return None
        Some(v) => v.to_int()
      }
      let recbase = base + 6
      let mut exit_offset = match b.read_u16(recbase + index * 4 + 2) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      let mut entry_offset = match b.read_u16(recbase + index2 * 4) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      if exit_offset == 0 || entry_offset == 0 {
        return Some(false)
      }
      exit_offset = exit_offset + base
      entry_offset = entry_offset + base
      let exit = match self.anchor(exit_offset) {
        None => return None
        Some(v) => v
      }
      let entry = match self.anchor(entry_offset) {
        None => return None
        Some(v) => v
      }
      self.buf.position_cursive(cur, next, entry.0 - exit.0, entry.1 - exit.1)
      Some(true)
    }
    MarkToBase1 | MarkToMark1 => {
      let prev = if kind is MarkToBase1 {
        match self.previous_base(cur) {
          None => return None
          Some(v) => v
        }
      } else {
        match self.previous(cur) {
          None => return None
          Some(v) => v
        }
      }
      let diff = cur - prev
      if diff > 255 {
        return Some(false)
      }
      let g2 = self.buf.glyphs[prev].id
      let base_cov = match b.read_u16(base + 4) {
        None => return None
        Some(v) => base + v.reinterpret_as_int()
      }
      let index2 = match self.coverage(base_cov, g2) {
        None => return None
        Some(v) => v.to_int()
      }
      let (mark_class, mark_anchor) = {
        let markbase = match b.read_u16(base + 8) {
          None => return None
          Some(v) => base + v.reinterpret_as_int()
        }
        let a = match self.mark_anchor(markbase, index.to_uint16()) {
          None => return None
          Some(v) => v
        }
        (a.0.to_int(), a.1)
      }
      let base_anchor = {
        let class_count = match b.read_u16(base + 6) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        let basebase = match b.read_u16(base + 10) {
          None => return None
          Some(v) => base + v.reinterpret_as_int()
        }
        let base_count = match b.read_u16(basebase) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        let count = base_count * class_count
        let ix = class_count * index2 + mark_class
        if ix >= count {
          return Some(false)
        }
        let abase = match b.read_u16(basebase + 2 + ix * 2) {
          None => return None
          Some(v) => basebase + v.reinterpret_as_int()
        }
        match self.anchor(abase) {
          None => return None
          Some(v) => v
        }
      }
      self.buf.position_mark(
        cur,
        prev,
        base_anchor.0 - mark_anchor.0,
        base_anchor.1 - mark_anchor.1,
      )
      Some(true)
    }
    MarkToLig1 => {
      let comp_index = self.buf.glyphs[cur].component.to_int()
      if comp_index == 0xFF {
        return None
      }
      let prev = match self.previous_base(cur) {
        None => return None
        Some(v) => v
      }
      let diff = cur - prev
      if diff > 255 {
        return None
      }
      let g2 = self.buf.glyphs[prev].id
      let mark_index = index.to_uint16()
      let base_index = {
        let base_cov = match b.read_u16(base + 4) {
          None => return None
          Some(v) => base + v.reinterpret_as_int()
        }
        match self.coverage(base_cov, g2) {
          None => return None
          Some(v) => v
        }
      }
      let class_count = match b.read_u16(base + 6) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      let mark_base = match b.read_u16(base + 8) {
        None => return None
        Some(v) => base + v.reinterpret_as_int()
      }
      let mark_anchor = match self.mark_anchor(mark_base, mark_index) {
        None => return None
        Some(v) => v
      }
      let mark_class = mark_anchor.0.to_int()
      let mark_anchor = mark_anchor.1
      let mut lig_array = match b.read_u16(base + 10) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      if lig_array == 0 {
        return None
      }
      lig_array = lig_array + base
      let lig_array_len = match b.read_u16(lig_array) {
        None => return None
        Some(v) => v.to_uint16()
      }
      if base_index >= lig_array_len {
        return None
      }
      let mut lig_attach = match
        b.read_u16(lig_array + 2 + base_index.to_int() * 2) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      if lig_attach == 0 {
        return None
      }
      lig_attach = lig_attach + lig_array
      let comp_count = match b.read_u16(lig_attach) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      if comp_count == 0 || comp_index >= comp_count {
        return None
      }
      let comp_rec = lig_attach +
        2 +
        comp_index * class_count * 2 +
        mark_class * 2
      let anchor_offset = match b.read_u16(comp_rec) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      if anchor_offset == 0 {
        return None
      }
      let base_anchor = match self.anchor(lig_attach + anchor_offset) {
        None => return None
        Some(v) => v
      }
      self.buf.position_mark(
        cur,
        prev,
        base_anchor.0 - mark_anchor.0,
        base_anchor.1 - mark_anchor.1,
      )
      Some(true)
    }
    Context1 => {
      let set_index = index
      let mut off = base + 4
      let set_count = match b.read_u16(off) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      off = off + 2
      let set_offsets = match read_u16_array(b, off, set_count) {
        None => return None
        Some(v) => v
      }
      off = off + set_count * 2
      if set_index < 0 || set_index >= set_count {
        return None
      }
      let mut offset = set_offsets[set_index].to_int()
      if offset == 0 {
        return Some(false)
      }
      offset = offset + base
      off = offset
      let rule_count = match b.read_u16(off) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      off = off + 2
      let rule_offsets = match read_u16_array(b, off, rule_count) {
        None => return None
        Some(v) => v
      }
      for i in 0.. return None
          Some(v) => v.reinterpret_as_int()
        }
        c = c + 2
        let subst_count = match b.read_u16(c) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        c = c + 2
        let mut input_end = cur
        if input_count > 1 {
          input_count = input_count - 1
          let seq = match read_u16_array(b, c, input_count) {
            None => return None
            Some(v) => v
          }
          c = c + input_count * 2
          match self.match_sequence(cur, input_count, (j, id) => id == seq[j]) {
            None => continue
            Some(end_ix) => input_end = end_ix
          }
        }
        self.apply_contextual(b, c, subst_count, input_end) |> ignore
        return Some(true)
      }
      None
    }
    Context2 => {
      let mut off = base + 4
      let mut input_classdef = match b.read_u16(off) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      off = off + 2
      if input_classdef == 0 {
        return Some(false)
      }
      input_classdef = input_classdef + base
      let set_index = self.class(input_classdef, g).to_int()
      let set_count = match b.read_u16(off) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      off = off + 2
      let set_offsets = match read_u16_array(b, off, set_count) {
        None => return None
        Some(v) => v
      }
      off = off + set_count * 2
      if set_index < 0 || set_index >= set_count {
        return None
      }
      let mut offset = set_offsets[set_index].to_int()
      if offset == 0 {
        return Some(false)
      }
      offset = offset + base
      off = offset
      let rule_count = match b.read_u16(off) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      off = off + 2
      let rule_offsets = match read_u16_array(b, off, rule_count) {
        None => return None
        Some(v) => v
      }
      for i in 0.. return None
          Some(v) => v.reinterpret_as_int()
        }
        c = c + 2
        let subst_count = match b.read_u16(c) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        c = c + 2
        let mut input_end = cur
        if input_count > 1 {
          input_count = input_count - 1
          let seq = match read_u16_array(b, c, input_count) {
            None => return None
            Some(v) => v
          }
          c = c + input_count * 2
          match
            self.match_sequence(cur, input_count, (j, id) => {
              self.class(input_classdef, id) == seq[j]
            }) {
            None => continue
            Some(end_ix) => input_end = end_ix
          }
        }
        self.apply_contextual(b, c, subst_count, input_end) |> ignore
        return Some(true)
      }
      None
    }
    Context3 => {
      let mut c = base + 2
      let mut input_count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      if input_count == 0 {
        return None
      }
      input_count = input_count - 1
      let subst_count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      c = c + 2 // skip coverage for the first glyph
      let input = match read_u16_array(b, c, input_count) {
        None => return None
        Some(v) => v
      }
      c = c + input_count * 2
      let input_end = match
        self.match_sequence(cur, input_count, (j, id) => {
          self.coverage(base + input[j].to_int(), id) is Some(_)
        }) {
        None => return None
        Some(v) => v
      }
      self.apply_contextual(b, c, subst_count, input_end) |> ignore
      Some(true)
    }
    ChainContext1 => {
      let set_index = index
      let mut off = base + 4
      let set_count = match b.read_u16(off) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      off = off + 2
      let set_offsets = match read_u16_array(b, off, set_count) {
        None => return None
        Some(v) => v
      }
      if set_index < 0 || set_index >= set_count {
        return None
      }
      let mut offset = set_offsets[set_index].to_int()
      if offset == 0 {
        return Some(false)
      }
      offset = offset + base
      off = offset
      let rule_count = match b.read_u16(off) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      off = off + 2
      let rule_offsets = match read_u16_array(b, off, rule_count) {
        None => return None
        Some(v) => v
      }
      for i in 0.. return None
          Some(v) => v.reinterpret_as_int()
        }
        c = c + 2
        if backtrack_count != 0 {
          let seq = match read_u16_array(b, c, backtrack_count) {
            None => return None
            Some(v) => v
          }
          c = c + backtrack_count * 2
          let pred = (j, id) => id == seq[j]
          if self.match_backtrack(cur, backtrack_count, pred) is None {
            continue
          }
        }
        let mut input_count = match b.read_u16(c) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        c = c + 2
        let mut input_end = cur
        if input_count > 1 {
          input_count = input_count - 1
          let seq = match read_u16_array(b, c, input_count) {
            None => return None
            Some(v) => v
          }
          c = c + input_count * 2
          match self.match_sequence(cur, input_count, (j, id) => id == seq[j]) {
            None => continue
            Some(end_ix) => input_end = end_ix
          }
        }
        let lookahead_count = match b.read_u16(c) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        c = c + 2
        if lookahead_count != 0 {
          let seq = match read_u16_array(b, c, lookahead_count) {
            None => return None
            Some(v) => v
          }
          c = c + lookahead_count * 2
          let pred = (j, id) => id == seq[j]
          if self.match_sequence(input_end, lookahead_count, pred) is None {
            continue
          }
        }
        let count = match b.read_u16(c) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        c = c + 2
        self.apply_contextual(b, c, count, input_end) |> ignore
        return Some(true)
      }
      None
    }
    ChainContext2 => {
      let mut c = base + 4
      let backtrack_classdef_rel = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      let backtrack_classdef = base + backtrack_classdef_rel
      c = c + 2
      let mut input_classdef = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      if input_classdef == 0 {
        return Some(false)
      }
      input_classdef = input_classdef + base
      let set_index = self.class(input_classdef, g).to_int()
      let lookahead_classdef_rel = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      let lookahead_classdef = base + lookahead_classdef_rel
      c = c + 2
      let set_count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      let set_offsets = match read_u16_array(b, c, set_count) {
        None => return None
        Some(v) => v
      }
      c = c + set_count * 2
      if set_index < 0 || set_index >= set_count {
        return None
      }
      let mut offset = set_offsets[set_index].to_int()
      if offset == 0 {
        return Some(false)
      }
      offset = offset + base
      let mut off = offset
      let rule_count = match b.read_u16(off) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      off = off + 2
      let rule_offsets = match read_u16_array(b, off, rule_count) {
        None => return None
        Some(v) => v
      }
      for i in 0.. return None
          Some(v) => v.reinterpret_as_int()
        }
        d = d + 2
        if backtrack_count != 0 {
          let seq = match read_u16_array(b, d, backtrack_count) {
            None => return None
            Some(v) => v
          }
          d = d + backtrack_count * 2
          let pred = (j, id) => self.class(backtrack_classdef, id) == seq[j]
          if self.match_backtrack(cur, backtrack_count, pred) is None {
            continue
          }
        }
        let mut input_count = match b.read_u16(d) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        d = d + 2
        let mut input_end = cur
        if input_count > 1 {
          input_count = input_count - 1
          let seq = match read_u16_array(b, d, input_count) {
            None => return None
            Some(v) => v
          }
          d = d + input_count * 2
          match
            self.match_sequence(cur, input_count, (j, id) => {
              self.class(input_classdef, id) == seq[j]
            }) {
            None => continue
            Some(end_ix) => input_end = end_ix
          }
        }
        let lookahead_count = match b.read_u16(d) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        d = d + 2
        if lookahead_count != 0 {
          let seq = match read_u16_array(b, d, lookahead_count) {
            None => return None
            Some(v) => v
          }
          d = d + lookahead_count * 2
          let pred = (j, id) => self.class(lookahead_classdef, id) == seq[j]
          if self.match_sequence(input_end, lookahead_count, pred) is None {
            continue
          }
        }
        let count = match b.read_u16(d) {
          None => return None
          Some(v) => v.reinterpret_as_int()
        }
        d = d + 2
        self.apply_contextual(b, d, count, input_end) |> ignore
        return Some(true)
      }
      None
    }
    ChainContext3 => {
      let mut c = base + 2
      let backtrack_count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      if backtrack_count != 0 {
        if backtrack_count > cur - self.start {
          return None
        }
        let backtrack = match read_u16_array(b, c, backtrack_count) {
          None => return None
          Some(v) => v
        }
        c = c + backtrack_count * 2
        match
          self.match_backtrack(cur, backtrack_count, (j, id) => {
            self.coverage(
              base + backtrack.get(j).unwrap_or((0).to_uint16()).to_int(),
              id,
            )
            is Some(_)
          }) {
          None => return None
          Some(_) => ()
        }
      }
      let input_count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int() - 1
      }
      c = c + 2
      c = c + 2 // skip coverage for the first glyph
      let mut input_end = cur
      if input_count != 0 {
        let input = match read_u16_array(b, c, input_count) {
          None => return None
          Some(v) => v
        }
        c = c + input_count * 2
        input_end = match
          self.match_sequence(cur, input_count, (j, id) => {
            self.coverage(
              base + input.get(j).unwrap_or((0).to_uint16()).to_int(),
              id,
            )
            is Some(_)
          }) {
          None => return None
          Some(v) => v
        }
      }
      let lookahead_count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      if lookahead_count != 0 {
        if lookahead_count > self.s.end - input_end {
          return None
        }
        let lookahead = match read_u16_array(b, c, lookahead_count) {
          None => return None
          Some(v) => v
        }
        c = c + lookahead_count * 2
        match
          self.match_sequence(input_end, lookahead_count, (j, id) => {
            self.coverage(
              base + lookahead.get(j).unwrap_or((0).to_uint16()).to_int(),
              id,
            )
            is Some(_)
          }) {
          None => return None
          Some(_) => ()
        }
      }
      let count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      self.apply_contextual(b, c, count, input_end) |> ignore
      Some(true)
    }
    RevChainContext1 => {
      let mut c = base + 4
      let backtrack_count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      if backtrack_count != 0 {
        if backtrack_count > cur - self.start {
          return None
        }
        let backtrack = match read_u16_array(b, c, backtrack_count) {
          None => return None
          Some(v) => v
        }
        c = c + backtrack_count * 2
        match
          self.match_backtrack(cur, backtrack_count, (j, id) => {
            self.coverage(
              base + backtrack.get(j).unwrap_or((0).to_uint16()).to_int(),
              id,
            )
            is Some(_)
          }) {
          None => return None
          Some(_) => ()
        }
      }
      let lookahead_count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      if lookahead_count != 0 {
        if lookahead_count + cur + 1 > self.s.end {
          return None
        }
        let lookahead = match read_u16_array(b, c, lookahead_count) {
          None => return None
          Some(v) => v
        }
        c = c + lookahead_count * 2
        match
          self.match_sequence(cur, lookahead_count, (j, id) => {
            self.coverage(
              base + lookahead.get(j).unwrap_or((0).to_uint16()).to_int(),
              id,
            )
            is Some(_)
          }) {
          None => return None
          Some(_) => ()
        }
      }
      let count = match b.read_u16(c) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      c = c + 2
      let substs = match read_u16_array(b, c, count) {
        None => return None
        Some(v) => v
      }
      if index < 0 || index >= count {
        return None
      }
      self.buf.substitute(cur, substs[index])
      Some(true)
    }
  }
}

///|
fn ApplyContext::apply_nested(
  self : ApplyContext,
  index : UInt16,
  cur : Int32,
  end : Int32,
  first : Int32,
) -> Bool? {
  if self.top.to_int() == MAX_NESTED_LOOKUPS {
    return None
  }
  let b = @internal.BeBytes::BeBytes(self.data)
  let list_rel = match b.read_u16(self.gsubgpos.reinterpret_as_int() + 8) {
    None => return None
    Some(v) => v
  }
  let list_base = self.gsubgpos + list_rel
  let lookup = match
    @internal.lookup_data(
      self.data,
      self.stage,
      list_base,
      index,
      (0).to_byte(),
      Some(self.defs),
    ) {
    None => return None
    Some(v) => v
  }
  self.storage.stack.set(self.top.to_int(), self.s)
  self.top = (self.top.to_int() + 1).to_byte()
  let v = self.apply_uncached(lookup, cur, end + 1, first)
  self.top = (self.top.to_int() - 1).to_byte()
  self.s = self.storage.stack[self.top.to_int()]
  v
}

///|
fn ApplyContext::apply_uncached(
  self : ApplyContext,
  lookup : @internal.LookupData,
  cur : Int32,
  end : Int32,
  first : Int32,
) -> Bool? {
  let b = @internal.BeBytes::BeBytes(self.data)
  let base = lookup.offset.reinterpret_as_int()
  self.s.cur = cur
  let buf_len = self.buf.len()
  self.s.end = if end < buf_len { end } else { buf_len }
  let mut applied = false
  let subtables = base + 6
  let count = lookup.count.to_int()
  let ext = lookup.is_ext
  let kind = lookup.kind
  let reverse = lookup.kind is RevChainContext
  if reverse {
    if !self.move_last() {
      return Some(false)
    }
  } else if !self.move_to(first) {
    return Some(false)
  }
  let cur = self.s.cur
  let g = self.buf.glyphs[cur].id
  for i in 0.. return None
      Some(v) => v.reinterpret_as_int()
    }
    let mut subtable = base + subtable_rel
    if ext {
      let ext_rel = match b.read_u32(subtable + 4) {
        None => return None
        Some(v) => v.reinterpret_as_int()
      }
      subtable = subtable + ext_rel
    }
    let fmt = match b.read_u16(subtable) {
      None => return None
      Some(v) => v.to_uint16()
    }
    match
      @internal.subtable_data(
        self.data,
        subtable.reinterpret_as_uint(),
        kind,
        fmt,
      ) {
      None => ()
      Some(s) =>
        match s.coverage(self.data, g) {
          None => ()
          Some(ix) =>
            if self.apply_subtable(s, ix.to_int(), cur, g) == Some(true) {
              applied = true
              break
            }
        }
    }
  }
  Some(applied)
}

///|
fn ApplyContext::apply_contextual(
  self : ApplyContext,
  b : @internal.BeBytes,
  offset : Int32,
  count : Int32,
  end : Int32,
) -> Bool? {
  let mut applied = false
  let mut offset = offset
  let start = self.s.cur
  for _ in 0.. return None
      Some(v) => v.reinterpret_as_int()
    }
    offset = offset + 2
    let lookup = match b.read_u16(offset) {
      None => return None
      Some(v) => v.to_uint16()
    }
    offset = offset + 2
    if self.apply_nested(lookup, start, end, first) == Some(true) {
      applied = true
    }
  }
  if applied {
    self.s.cur = end
  }
  Some(applied)
}

///|
fn ApplyContext::coverage(
  self : ApplyContext,
  coverage_offset : Int32,
  glyph_id : UInt16,
) -> UInt16? {
  @internal.coverage(self.data, coverage_offset.reinterpret_as_uint(), glyph_id)
}

///|
fn ApplyContext::class(
  self : ApplyContext,
  classdef_offset : Int32,
  glyph_id : UInt16,
) -> UInt16 {
  @internal.classdef(self.data, classdef_offset.reinterpret_as_uint(), glyph_id)
}

///|
fn ApplyContext::value_record(
  self : ApplyContext,
  parent_offset : Int32,
  offset : Int32,
  format : UInt16,
  pos : Pos4,
) -> Unit? {
  let b = @internal.BeBytes::BeBytes(self.data)
  let mut offset = offset
  let fmt = format.to_int() & 0xFFFF
  if fmt == 4 {
    pos.xadvance = match b.read_i16(offset) {
      None => return None
      Some(v) => v.to_double()
    }
    return Some(())
  }
  if (fmt & 1) != 0 {
    pos.x = match b.read_i16(offset) {
      None => return None
      Some(v) => v.to_double()
    }
    offset = offset + 2
  }
  if (fmt & 2) != 0 {
    pos.y = match b.read_i16(offset) {
      None => return None
      Some(v) => v.to_double()
    }
    offset = offset + 2
  }
  if (fmt & 4) != 0 {
    pos.xadvance = match b.read_i16(offset) {
      None => return None
      Some(v) => v.to_double()
    }
    offset = offset + 2
  }
  if (fmt & 8) != 0 {
    pos.yadvance = match b.read_i16(offset) {
      None => return None
      Some(v) => v.to_double()
    }
    offset = offset + 2
  }
  if (fmt & (0x10 | 0x20 | 0x40 | 0x80)) == 0 {
    return Some(())
  }
  if self.enable_var {
    if (fmt & 0x10) != 0 {
      let delta = match
        self.value_delta(
          parent_offset,
          match b.read_u16(offset) {
            None => return None
            Some(v) => v.to_uint16()
          },
        ) {
        None => return None
        Some(v) => v
      }
      pos.x = pos.x + delta
      offset = offset + 2
    }
    if (fmt & 0x20) != 0 {
      let delta = match
        self.value_delta(
          parent_offset,
          match b.read_u16(offset) {
            None => return None
            Some(v) => v.to_uint16()
          },
        ) {
        None => return None
        Some(v) => v
      }
      pos.y = pos.y + delta
      offset = offset + 2
    }
    if (fmt & 0x40) != 0 {
      let delta = match
        self.value_delta(
          parent_offset,
          match b.read_u16(offset) {
            None => return None
            Some(v) => v.to_uint16()
          },
        ) {
        None => return None
        Some(v) => v
      }
      pos.xadvance = pos.xadvance + delta
      offset = offset + 2
    }
    if (fmt & 0x80) != 0 {
      let delta = match
        self.value_delta(
          parent_offset,
          match b.read_u16(offset) {
            None => return None
            Some(v) => v.to_uint16()
          },
        ) {
        None => return None
        Some(v) => v
      }
      pos.yadvance = pos.yadvance + delta
    }
  }
  Some(())
}

///|
fn ApplyContext::value_delta(
  self : ApplyContext,
  parent_offset : Int32,
  offset : UInt16,
) -> Double? {
  if offset == 0 {
    return Some(0.0)
  }
  let b = @internal.BeBytes::BeBytes(self.data)
  let offset = parent_offset + offset.to_int()
  let format = match b.read_u16(offset + 4) {
    None => return None
    Some(v) => v.reinterpret_as_int() & 0xFFFF
  }
  if format != 0x8000 {
    return Some(0.0)
  }
  let outer = match b.read_u16(offset) {
    None => return None
    Some(v) => v.to_uint16()
  }
  let inner = match b.read_u16(offset + 2) {
    None => return None
    Some(v) => v.to_uint16()
  }
  Some(self.defs.delta(outer, inner, self.coords))
}

///|
fn ApplyContext::anchor(
  self : ApplyContext,
  offset : Int32,
) -> (Double, Double)? {
  let b = @internal.BeBytes::BeBytes(self.data)
  let format = match b.read_u16(offset) {
    None => return None
    Some(v) => v.reinterpret_as_int() & 0xFFFF
  }
  let mut x = match b.read_i16(offset + 2) {
    None => return None
    Some(v) => v.to_double()
  }
  let mut y = match b.read_i16(offset + 4) {
    None => return None
    Some(v) => v.to_double()
  }
  if format == 3 && self.defs.has_var_store() && self.coords.length() != 0 {
    let dx = match
      self.value_delta(
        offset,
        match b.read_u16(offset + 6) {
          None => return None
          Some(v) => v.to_uint16()
        },
      ) {
      None => return None
      Some(v) => v
    }
    let dy = match
      self.value_delta(
        offset,
        match b.read_u16(offset + 8) {
          None => return None
          Some(v) => v.to_uint16()
        },
      ) {
      None => return None
      Some(v) => v
    }
    x = x + dx
    y = y + dy
  }
  Some((x, y))
}

///|
fn ApplyContext::mark_anchor(
  self : ApplyContext,
  marks : Int32,
  index : UInt16,
) -> (UInt16, (Double, Double))? {
  let b = @internal.BeBytes::BeBytes(self.data)
  let count = match b.read_u16(marks) {
    None => return None
    Some(v) => v.to_uint16()
  }
  if index >= count {
    return None
  }
  let rec = marks + 2 + index.to_int() * 4
  let class = match b.read_u16(rec) {
    None => return None
    Some(v) => v.to_uint16()
  }
  let offset = match b.read_u16(rec + 2) {
    None => return None
    Some(v) => v.reinterpret_as_int()
  }
  if offset == 0 {
    return None
  }
  let anchor = match self.anchor(marks + offset) {
    None => return None
    Some(v) => v
  }
  Some((class, anchor))
}

///|
fn read_u16_array(
  b : @internal.BeBytes,
  offset : Int32,
  count : Int32,
) -> Array[UInt16]? {
  if count < 0 {
    return None
  }
  if !b.check_range(offset, count * 2) {
    return None
  }
  let out : Array[UInt16] = []
  for i in 0.. return None
      Some(v) => v.to_uint16()
    }
    out.push(v)
  }
  Some(out)
}

///|
/// Applies lookups for either substitutions (`stage=0`) or positioning (`stage=1`).
///
/// This is ported from `upstream swash/src/shape/at.rs` (`apply`) and is
/// completed as part of ro7.2.3.
pub fn apply(
  stage : Byte,
  data : Bytes,
  base : UInt32,
  coords : ArrayView[Int32],
  gdef : @internal.Gdef,
  storage : Storage,
  store : FeatureStore,
  feature_mask : FeatureMask,
  buffer : Buffer,
  buffer_range : IndexRange?,
) -> Bool? {
  if base == 0U || feature_mask.is_empty() {
    return Some(false)
  }
  let range = match buffer_range {
    None => IndexRange::IndexRange(0, buffer.len())
    Some(r) => r
  }
  let acx = ApplyContext::ApplyContext(
    stage, data, base, gdef, coords, store, storage, buffer, range,
  )
  let (lookup_start, lookup_end) = if stage == (0).to_byte() {
    (0, store.pos_start)
  } else {
    (store.pos_start, store.lookups.length())
  }
  let mut applied = false
  for i in lookup_start.. store.subtables.length() {
      return None
    }
    if acx.apply(lookup, (start, end), range.start, None, 0) == Some(true) {
      applied = true
    }
  }
  Some(applied)
}

///|
pub fn FeatureStore::default() -> FeatureStore {
  {
    sub_features: [],
    pos_features: [],
    lookups: [],
    subtables: [],
    coverage: [],
    pos_start: 0,
    sub_count: 0,
    truncated: false,
    groups: FeatureGroups::default(),
  }
}

///|
pub fn FeatureStore::clear(self : FeatureStore) -> Unit {
  self.sub_features.clear()
  self.pos_features.clear()
  self.lookups.clear()
  self.subtables.clear()
  self.coverage.clear()
  self.pos_start = 0
  self.sub_count = 0
  self.truncated = false
  self.groups = FeatureGroups::default()
}

///|
pub fn FeatureStore::sub_bit(
  self : FeatureStore,
  feature : UInt32,
) -> FeatureBit? {
  // Binary search by tag.
  let mut l = 0
  let mut h = self.sub_features.length()
  while l < h {
    let mid = (l + h) / 2
    let (t, b) = self.sub_features[mid]
    if feature < t {
      h = mid
    } else if feature > t {
      l = mid + 1
    } else {
      return Some(b)
    }
  }
  None
}

///|
pub fn FeatureStore::pos_bit(
  self : FeatureStore,
  feature : UInt32,
) -> FeatureBit? {
  let mut l = 0
  let mut h = self.pos_features.length()
  while l < h {
    let mid = (l + h) / 2
    let (t, b) = self.pos_features[mid]
    if feature < t {
      h = mid
    } else if feature > t {
      l = mid + 1
    } else {
      return Some(b)
    }
  }
  None
}

///|
/// Returns new `basic` and `position` masks based on the specified custom features.
pub fn FeatureStore::custom_masks(
  self : FeatureStore,
  features : ArrayView[@swash.FeatureSetting],
  sub_args : Array[UInt16],
  pos_args : Array[UInt16],
  dir : Direction,
) -> (FeatureMask, FeatureMask) {
  // NOTE: the `args` arrays are owned by `Buffer` in the upstream pipeline.
  sub_args.clear()
  for _ in 0.. FeatureMask {
  let mask = mask0
  for req in requested_features.iter() {
    let tag = req.tag
    // Find bit index.
    let mut l = 0
    let mut h = stage_features.length()
    while l < h {
      let mid = (l + h) / 2
      let (t, b) = stage_features[mid]
      if tag < t {
        h = mid
      } else if tag > t {
        l = mid + 1
      } else {
        let ix = b.to_int()
        let arg = req.value
        args[ix] = arg
        if arg != 0 {
          mask.set(b)
        } else {
          mask.clear(b)
        }
        break
      }
    }
  }
  mask
}

///|
/// Computes feature groups for a script based on available features.
pub fn FeatureStore::groups(
  self : FeatureStore,
  script : @swash.Script,
) -> FeatureGroups {
  let g = FeatureGroups::default()
  feature_masks(self, Some(g.vert), Some(g.position), [VRT2])
  feature_masks(self, Some(g.rtl), Some(g.position), [RTLM])
  if g.vert.is_empty() {
    feature_masks(self, Some(g.vert), Some(g.position), [VERT])
  }
  if script.is_complex() {
    if script is Myanmar {
      feature_masks(self, Some(g.default), Some(g.position), [
        CALT,
        CCMP,
        LOCL,
        RVRN,
      ])
      g.reph = self.sub_bit(RPHF)
      g.pref = self.sub_bit(PREF)
      feature_masks(self, Some(g.stage1), Some(g.position), [BLWF, PSTF])
      feature_masks(self, Some(g.stage2), Some(g.position), [
        PRES,
        ABVS,
        BLWS,
        PSTS,
      ])
      feature_masks(self, Some(g.basic), Some(g.position), [
        DIST,
        KERN,
        MARK,
        MKMK,
      ])
    } else {
      feature_masks(self, Some(g.default), Some(g.position), [
        AKHN,
        CALT,
        CCMP,
        LOCL,
        NUKT,
        RVRN,
      ])
      g.reph = self.sub_bit(RPHF)
      g.pref = self.sub_bit(PREF)
      feature_masks(self, Some(g.stage1), Some(g.position), [
        ABVF,
        BLWF,
        CJCT,
        HALF,
        PSTF,
        RKRF,
        VATU,
      ])
      if script.is_joined() {
        feature_masks(self, Some(g.stage2), Some(g.position), [
          FIN2,
          FIN3,
          FINA,
          INIT,
          ISOL,
          MED2,
          MEDI,
        ])
      }
      feature_masks(self, Some(g.basic), Some(g.position), [
        ABVS,
        BLWS,
        CALT,
        CLIG,
        HALN,
        LIGA,
        PRES,
        PSTS,
        RCLT,
        RLIG,
      ])
      feature_masks(self, Some(g.basic), Some(g.position), [
        ABVM,
        BLWM,
        CURS,
        DIST,
        KERN,
        MARK,
        MKMK,
      ])
    }
  } else if script is Hangul {
    feature_masks(self, Some(g.basic), Some(g.position), [
      CCMP,
      LJMO,
      RVRN,
      TJMO,
      VJMO,
    ])
  } else {
    if script.is_joined() {
      feature_masks(self, Some(g.basic), Some(g.position), [
        CALT,
        CCMP,
        CLIG,
        FIN2,
        FIN3,
        FINA,
        INIT,
        ISOL,
        LIGA,
        LOCL,
        MED2,
        MEDI,
        MSET,
        RLIG,
        RVRN,
      ])
    } else {
      feature_masks(self, Some(g.basic), Some(g.position), [
        CALT,
        CCMP,
        CLIG,
        LIGA,
        LOCL,
        RVRN,
      ])
    }
    feature_masks(self, Some(g.basic), Some(g.position), [
      CURS,
      DIST,
      KERN,
      MARK,
      MKMK,
    ])
  }
  g
}

///|
fn FeatureStore::covers(
  self : FeatureStore,
  key : UInt32,
  glyph_id : UInt16,
) -> Bool {
  if key == 0xFFFFFFFFU {
    return true
  }
  let cache = self.coverage
  let base = key.reinterpret_as_int()
  if base < 0 || base + 1 >= cache.length() {
    return false
  }
  let first = cache[base]
  let last = cache[base + 1]
  if glyph_id >= first && glyph_id <= last {
    let bit = (glyph_id - first).to_int()
    let idx = base + 2 + bit / 16
    if idx < 0 || idx >= cache.length() {
      return false
    }
    (cache[idx] & ((1).to_uint16() << (bit & 15))) != 0
  } else {
    false
  }
}

///|
fn feature_masks(
  store : FeatureStore,
  sub_mask : FeatureMask?,
  pos_mask : FeatureMask?,
  features : ArrayView[UInt32],
) -> Unit {
  match sub_mask {
    None => ()
    Some(m) =>
      for feature in features.iter() {
        match store.sub_bit(feature) {
          None => ()
          Some(bit) => m.set(bit)
        }
      }
  }
  match pos_mask {
    None => ()
    Some(m) =>
      for feature in features.iter() {
        match store.pos_bit(feature) {
          None => ()
          Some(bit) => m.set(bit)
        }
      }
  }
}