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

///|
/// Script-aware cluster segmentation.
///
/// Ported from `swash/src/text/cluster/*` (swash is dual-licensed Apache-2.0 OR MIT).

///|
/// Arbitrary user data that can be associated with a character throughout
/// the shaping pipeline.
pub type UserData = UInt

///|
/// Boundary type of a character or cluster.
pub(all) enum Boundary {
  None
  Word
  Line
  Mandatory
}

///|
fn boundary_from_raw(raw : UInt) -> Boundary {
  match raw & 0b11U {
    0U => None
    1U => Word
    2U => Line
    _ => Mandatory
  }
}

///|
fn boundary_to_raw(boundary : Boundary) -> UInt {
  match boundary {
    None => 0U
    Word => 1U
    Line => 2U
    Mandatory => 3U
  }
}

///|
/// Information about a character including unicode properties and boundary
/// analysis.
struct CharInfo {
  props : Properties
}

///|
pub fn CharInfo::CharInfo(
  properties : Properties,
  boundary : Boundary,
) -> CharInfo {
  { props: properties.with_boundary(boundary_to_raw(boundary)), }
}

///|
pub fn CharInfo::properties(self : CharInfo) -> Properties {
  self.props
}

///|
pub fn CharInfo::boundary(self : CharInfo) -> Boundary {
  boundary_from_raw(self.props.boundary())
}

///|
fn CharInfo::boundary_raw(self : CharInfo) -> UInt {
  self.props.boundary()
}

///|
fn CharInfo::with_properties(self : CharInfo, props : Properties) -> CharInfo {
  { props: props.with_boundary(self.props.boundary()), }
}

///|
pub fn CharInfo::category(self : CharInfo) -> Category {
  self.props.category()
}

///|
pub fn CharInfo::joining_type(self : CharInfo) -> JoiningType {
  self.props.joining_type()
}

///|
pub fn CharInfo::cluster_break(self : CharInfo) -> ClusterBreak {
  self.props.cluster_break()
}

///|
pub fn CharInfo::is_ignorable(self : CharInfo) -> Bool {
  self.props.is_ignorable()
}

///|
pub fn CharInfo::is_variation_selector(self : CharInfo) -> Bool {
  self.props.is_variation_selector()
}

///|
pub fn CharInfo::contributes_to_shaping(self : CharInfo) -> Bool {
  self.props.contributes_to_shaping()
}

///|
pub fn CharInfo::use_class(self : CharInfo) -> (UseClass, Bool, Bool) {
  self.props.use_class()
}

///|
pub fn CharInfo::myanmar_class(self : CharInfo) -> (MyanmarClass, Bool) {
  self.props.myanmar_class()
}

///|
pub fn CharInfo::cluster_class(self : CharInfo) -> (ClusterBreak, Bool) {
  self.props.cluster_class()
}

///|
pub fn CharInfo::from_char(ch : Char) -> CharInfo {
  { props: Codepoint::properties(ch), }
}

///|
pub fn CharInfo::default() -> CharInfo {
  CharInfo::from_char('\u{0000}')
}

///|
const BOUND_SHIFT : Int = 14

///|
const SPACE_SHIFT : Int = 1

///|
const EMOJI_SHIFT : Int = 8

///|
const INFO_MASK_ALL : UInt = 0xFFFFU

///|
const INFO_LO_MASK : UInt = 0x3FFFU

///|
const SPACE_MASK : UInt = 0b111U

///|
const EMOJI_MASK : UInt = 0b11U

///|
/// Information about a cluster including content properties and boundary analysis.
struct ClusterInfo {
  bits : UInt
}

///|

///|
fn ClusterInfo::ClusterInfo(bits : UInt) -> ClusterInfo {
  { bits, }
}

///|
pub fn ClusterInfo::is_broken(self : ClusterInfo) -> Bool {
  (self.bits & 1U) != 0U
}

///|
pub fn ClusterInfo::is_emoji(self : ClusterInfo) -> Bool {
  ((self.bits >> EMOJI_SHIFT) & EMOJI_MASK) != 0U
}

///|
pub fn ClusterInfo::emoji(self : ClusterInfo) -> Emoji {
  emoji_from_raw((self.bits >> EMOJI_SHIFT) & EMOJI_MASK)
}

///|
pub fn ClusterInfo::is_whitespace(self : ClusterInfo) -> Bool {
  ((self.bits >> SPACE_SHIFT) & SPACE_MASK) != 0U
}

///|
pub fn ClusterInfo::whitespace(self : ClusterInfo) -> Whitespace {
  whitespace_from_raw((self.bits >> SPACE_SHIFT) & SPACE_MASK)
}

///|
pub fn ClusterInfo::is_boundary(self : ClusterInfo) -> Bool {
  self.bits >> BOUND_SHIFT != 0U
}

///|
pub fn ClusterInfo::boundary(self : ClusterInfo) -> Boundary {
  boundary_from_raw(self.bits >> BOUND_SHIFT)
}

///|
/// Presentation mode for an emoji cluster.
pub(all) enum Emoji {
  None
  Default
  Text
  Color
}

///|
fn emoji_from_raw(bits : UInt) -> Emoji {
  match bits & 0b11U {
    0U => None
    1U => Default
    2U => Text
    _ => Color
  }
}

///|
fn emoji_to_raw(e : Emoji) -> UInt {
  match e {
    None => 0U
    Default => 1U
    Text => 2U
    Color => 3U
  }
}

///|
/// Whitespace content of a cluster.
pub(all) enum Whitespace {
  None
  Space
  NoBreakSpace
  Tab
  Newline
  Other
}

///|
pub fn Whitespace::is_space_or_nbsp(self : Whitespace) -> Bool {
  match self {
    Space => true
    NoBreakSpace => true
    _ => false
  }
}

///|
fn whitespace_from_raw(bits : UInt) -> Whitespace {
  match bits & 0b111U {
    0U => None
    1U => Space
    2U => NoBreakSpace
    3U => Tab
    4U => Newline
    5U => Other
    _ => None
  }
}

///|
fn whitespace_to_raw(space : Whitespace) -> UInt {
  match space {
    None => 0U
    Space => 1U
    NoBreakSpace => 2U
    Tab => 3U
    Newline => 4U
    Other => 5U
  }
}

///|
/// Character input to the cluster parser.
struct Token {
  ch : Char
  offset : UInt
  len : UInt
  info : CharInfo
  data : UserData
}

///|
pub fn Token::Token(
  ch : Char,
  offset : UInt,
  len : UInt,
  info : CharInfo,
  data : UserData,
) -> Token {
  { ch, offset, len, info, data, }
}

///|
pub fn Token::default() -> Token {
  { ch: '\u{0000}', offset: 0U, len: 1U, info: CharInfo::default(), data: 0U, }
}

///|
pub fn Token::ch(self : Token) -> Char {
  self.ch
}

///|
pub fn Token::offset(self : Token) -> UInt {
  self.offset
}

///|
pub fn Token::len(self : Token) -> UInt {
  self.len
}

///|
pub fn Token::info(self : Token) -> CharInfo {
  self.info
}

///|
pub fn Token::data(self : Token) -> UserData {
  self.data
}

///|
/// Shaping class of a character.
pub(all) enum ShapeClass {
  Reph
  Pref
  Kinzi
  Base
  Mark
  Halant
  MedialRa
  VMPre
  VPre
  VBlw
  Anusvara
  Zwj
  Zwnj
  Control
  Vs
  Other
}

///|
/// Character output from the cluster parser.
struct ClusterChar {
  mut ch : Char
  offset : UInt
  shape_class : ShapeClass
  joining_type : JoiningType
  ignorable : Bool
  contributes_to_shaping : Bool
  mut glyph_id : GlyphId
  data : UserData
}

///|
pub fn ClusterChar::ClusterChar(
  ch : Char,
  offset : UInt,
  shape_class : ShapeClass,
  joining_type : JoiningType,
  ignorable : Bool,
  contributes_to_shaping : Bool,
  glyph_id : GlyphId,
  data : UserData,
) -> ClusterChar {
  {
    ch,
    offset,
    shape_class,
    joining_type,
    ignorable,
    contributes_to_shaping,
    glyph_id,
    data,
  }
}

///|
pub fn ClusterChar::ch(self : ClusterChar) -> Char {
  self.ch
}

///|
pub fn ClusterChar::offset(self : ClusterChar) -> UInt {
  self.offset
}

///|
pub fn ClusterChar::shape_class(self : ClusterChar) -> ShapeClass {
  self.shape_class
}

///|
pub fn ClusterChar::joining_type(self : ClusterChar) -> JoiningType {
  self.joining_type
}

///|
pub fn ClusterChar::is_ignorable(self : ClusterChar) -> Bool {
  self.ignorable
}

///|
pub fn ClusterChar::contributes_to_shaping(self : ClusterChar) -> Bool {
  self.contributes_to_shaping
}

///|
pub fn ClusterChar::glyph_id(self : ClusterChar) -> GlyphId {
  self.glyph_id
}

///|
pub fn ClusterChar::data(self : ClusterChar) -> UserData {
  self.data
}

///|
/// Iterative status of mapping a character cluster to nominal glyph identifiers.
pub(all) enum Status {
  Discard
  Keep
  Complete
}

///|
/// Source range of a cluster in code units.
struct SourceRange {
  start : UInt
  end : UInt
}

///|
pub fn SourceRange::SourceRange(start : UInt, end : UInt) -> SourceRange {
  { start, end, }
}

///|
pub fn SourceRange::start(self : SourceRange) -> UInt {
  self.start
}

///|
pub fn SourceRange::end(self : SourceRange) -> UInt {
  self.end
}

///|
/// The maximum number of characters in a single cluster.
pub const MAX_CLUSTER_SIZE : Int = 32

///|
priv enum FormKind {
  Original
  Nfd
  Nfc
}

///|
priv enum FormState {
  None
  Valid
  Invalid
}

///|
priv struct Form {
  chars : Array[ClusterChar]
  mut map_len : Int
  mut state : FormState
}

///|

///|
fn Form::Form() -> Form {
  { chars: ([] : Array[ClusterChar]), map_len: 0, state: None, }
}

///|
fn Form::clear(self : Form) -> Unit {
  self.chars.clear()
  self.map_len = 0
  self.state = None
}

///|
fn Form::setup(self : Form) -> Unit {
  let mut count = 0
  for c in self.chars.iter() {
    match c.shape_class {
      Control => ()
      _ => count = count + 1
    }
  }
  self.map_len = if count > 0 { count } else { 1 }
}

///|
fn mapper_map(
  chars : Array[ClusterChar],
  map_len : Int,
  f : (Char) -> GlyphId,
  glyphs : Array[GlyphId],
  best_ratio : Double,
) -> Double {
  if map_len == 0 {
    return 1.0
  }
  let mut mapped = 0
  let n = chars.length()
  for i in 0.. best_ratio {
    for i in 0.. GlyphId,
  glyphs : Array[GlyphId],
  best_ratio : Double,
) -> Double {
  mapper_map(self.chars, self.map_len, f, glyphs, best_ratio)
}

///|
/// Character cluster; output from the parser and input to the shaper.
struct CharCluster {
  mut info_bits : UInt
  chars : Array[ClusterChar]
  mut map_len : Int
  mut start : UInt
  mut end : UInt
  mut force_normalize : Bool
  comp : Form
  decomp : Form
  mut form : FormKind
  mut best_ratio : Double
}

///|
pub fn CharCluster::CharCluster() -> CharCluster {
  {
    info_bits: 0U,
    chars: ([] : Array[ClusterChar]),
    map_len: 0,
    start: 0U,
    end: 0U,
    force_normalize: false,
    comp: Form(),
    decomp: Form(),
    form: Original,
    best_ratio: 0.0,
  }
}

///|
pub fn CharCluster::info(self : CharCluster) -> ClusterInfo {
  ClusterInfo(self.info_bits)
}

///|
pub fn CharCluster::user_data(self : CharCluster) -> UserData {
  if self.chars.length() == 0 {
    0U
  } else {
    self.chars[0].data
  }
}

///|
pub fn CharCluster::range(self : CharCluster) -> SourceRange {
  { start: self.start, end: self.end, }
}

///|
pub fn CharCluster::is_empty(self : CharCluster) -> Bool {
  self.chars.length() == 0
}

///|
pub fn CharCluster::chars(self : CharCluster) -> ArrayView[ClusterChar] {
  self.chars[:]
}

///|
pub fn CharCluster::mapped_chars(self : CharCluster) -> ArrayView[ClusterChar] {
  match self.form {
    Original => self.chars[:]
    Nfd => self.decomp.chars[:]
    Nfc => self.comp.chars[:]
  }
}

///|
pub fn CharCluster::map(self : CharCluster, f : (Char) -> GlyphId) -> Status {
  let len = self.chars.length()
  if len == 0 {
    return Complete
  }
  let glyph_ids : Array[GlyphId] = Array::makei(MAX_CLUSTER_SIZE, _ => {
    (0).to_uint16()
  })
  let prev_ratio = self.best_ratio
  if self.force_normalize {
    match CharCluster::composed(self) {
      None => ()
      Some(_) => {
        let ratio = self.comp.map(f, glyph_ids, self.best_ratio)
        if ratio > self.best_ratio {
          self.best_ratio = ratio
          self.form = Nfc
          if ratio >= 1.0 {
            return Complete
          }
        }
      }
    }
  }
  let map_len = if self.map_len > 0 { self.map_len } else { 1 }
  let ratio = mapper_map(self.chars, map_len, f, glyph_ids, self.best_ratio)
  if ratio > self.best_ratio {
    self.best_ratio = ratio
    self.form = Original
    if ratio >= 1.0 {
      return Complete
    }
  }
  if len > 1 {
    match CharCluster::decomposed(self) {
      None => ()
      Some(_) => {
        let ratio = self.decomp.map(f, glyph_ids, self.best_ratio)
        if ratio > self.best_ratio {
          self.best_ratio = ratio
          self.form = Nfd
          if ratio >= 1.0 {
            return Complete
          }
        }
        if !self.force_normalize {
          match CharCluster::composed(self) {
            None => ()
            Some(_) => {
              let ratio = self.comp.map(f, glyph_ids, self.best_ratio)
              if ratio > self.best_ratio {
                self.best_ratio = ratio
                self.form = Nfc
                if ratio >= 1.0 {
                  return Complete
                }
              }
            }
          }
        }
      }
    }
  }
  if self.best_ratio > prev_ratio {
    Keep
  } else {
    Discard
  }
}

///|
pub fn CharCluster::clear(self : CharCluster) -> Unit {
  self.info_bits = 0U
  self.chars.clear()
  self.map_len = 0
  self.start = 0U
  self.end = 0U
  self.force_normalize = false
  self.comp.clear()
  self.decomp.clear()
  self.form = Original
  self.best_ratio = 0.0
}

///|
fn CharCluster::set_broken(self : CharCluster) -> Unit {
  self.info_bits = self.info_bits | 1U
}

///|
fn CharCluster::set_emoji(self : CharCluster, emoji : Emoji) -> Unit {
  let mask = EMOJI_MASK << EMOJI_SHIFT
  let bits = self.info_bits & (INFO_MASK_ALL ^ mask)
  self.info_bits = bits | (emoji_to_raw(emoji) << EMOJI_SHIFT)
}

///|
fn CharCluster::set_space(self : CharCluster, space : Whitespace) -> Unit {
  let mask = SPACE_MASK << SPACE_SHIFT
  let bits = self.info_bits & (INFO_MASK_ALL ^ mask)
  self.info_bits = bits | (whitespace_to_raw(space) << SPACE_SHIFT)
}

///|
fn CharCluster::set_space_from_char(self : CharCluster, ch : Char) -> Unit {
  match ch {
    ' ' => self.set_space(Space)
    '\u{00A0}' => self.set_space(NoBreakSpace)
    '\t' => self.set_space(Tab)
    _ => ()
  }
}

///|
fn CharCluster::merge_boundary(self : CharCluster, boundary : UInt) -> Unit {
  let cur = self.info_bits >> BOUND_SHIFT
  let merged = if cur > boundary { cur } else { boundary }
  self.info_bits = (self.info_bits & INFO_LO_MASK) | (merged << BOUND_SHIFT)
}

///|
fn CharCluster::force_normalize(self : CharCluster) -> Unit {
  self.force_normalize = true
}

///|
fn CharCluster::push(
  self : CharCluster,
  input : Token,
  class : ShapeClass,
) -> Unit {
  let contributes_to_shaping = input.info.contributes_to_shaping()
  let c = ClusterChar::{
    ch: input.ch,
    shape_class: class,
    joining_type: input.info.joining_type(),
    ignorable: input.info.is_ignorable(),
    contributes_to_shaping,
    glyph_id: (0).to_uint16(),
    offset: input.offset,
    data: input.data,
  }
  if self.chars.length() == 0 {
    self.start = input.offset
  }
  self.merge_boundary(input.info.boundary_raw())
  self.end = input.offset + input.len
  self.chars.push(c)
  if contributes_to_shaping {
    self.map_len = self.map_len + 1
  }
}

///|
fn CharCluster::note_char(self : CharCluster, input : Token) -> Unit {
  if self.chars.length() == 0 {
    self.start = input.offset
  }
  self.merge_boundary(input.info.boundary_raw())
  self.end = input.offset + input.len
}

///|
fn CharCluster::decomposed(self : CharCluster) -> ArrayView[ClusterChar]? {
  match self.decomp.state {
    Invalid => None
    Valid => Some(self.decomp.chars[:])
    None => {
      self.decomp.state = Invalid
      self.decomp.chars.clear()
      for ch in self.chars.iter() {
        for c in Codepoint::decompose(ch.ch) {
          if self.decomp.chars.length() == MAX_CLUSTER_SIZE {
            return None
          }
          let copy = ClusterChar::{
            ch: c,
            offset: ch.offset,
            shape_class: ch.shape_class,
            joining_type: ch.joining_type,
            ignorable: ch.ignorable,
            contributes_to_shaping: ch.contributes_to_shaping,
            glyph_id: ch.glyph_id,
            data: ch.data,
          }
          self.decomp.chars.push(copy)
        }
      }
      if self.decomp.chars.length() == 0 {
        return None
      }
      self.decomp.state = Valid
      self.decomp.setup()
      Some(self.decomp.chars[:])
    }
  }
}

///|
fn CharCluster::composed(self : CharCluster) -> ArrayView[ClusterChar]? {
  match self.comp.state {
    Invalid => None
    Valid => Some(self.comp.chars[:])
    None =>
      match CharCluster::decomposed(self) {
        None => {
          self.comp.state = Invalid
          None
        }
        Some(d) => {
          if d.length() == 0 {
            self.comp.state = Invalid
            return None
          }
          self.comp.state = Invalid
          self.comp.chars.clear()
          let mut last = self.decomp.chars[0]
          let n = self.decomp.chars.length()
          for i in 1.. {
                self.comp.chars.push(last)
                last = ch
              }
              Some(comp) => last.ch = comp
            }
          }
          self.comp.chars.push(last)
          self.comp.state = Valid
          self.comp.setup()
          Some(self.comp.chars[:])
        }
      }
  }
}

///|
priv struct TokenStream {
  iter : Iter[Token]
  mut buf : Array[Token]
  mut buf_offset : Int
  mut done : Bool
}

///|

///|
fn TokenStream::TokenStream(iter : Iter[Token]) -> TokenStream {
  { iter, buf: ([] : Array[Token]), buf_offset: 0, done: false, }
}

///|
fn TokenStream::fill_to(self : TokenStream, n : Int) -> Unit {
  while !self.done && self.buf_offset + n >= self.buf.length() {
    match self.iter.next() {
      None => {
        self.done = true
        return
      }
      Some(t) => self.buf.push(t)
    }
  }
}

///|
fn TokenStream::peek(self : TokenStream, n : Int) -> Token? {
  self.fill_to(n)
  let ix = self.buf_offset + n
  if ix >= 0 && ix < self.buf.length() {
    Some(self.buf[ix])
  } else {
    None
  }
}

///|
fn TokenStream::next(self : TokenStream) -> Token? {
  self.fill_to(0)
  if self.buf_offset < self.buf.length() {
    let t = self.buf[self.buf_offset]
    self.buf_offset = self.buf_offset + 1
    if self.buf_offset > 16 {
      let remain = self.buf.length() - self.buf_offset
      let new_buf : Array[Token] = Array::makei(remain, i => {
        self.buf[self.buf_offset + i]
      })
      self.buf = new_buf
      self.buf_offset = 0
    }
    Some(t)
  } else {
    None
  }
}

///|
priv struct SimpleState {
  chars : TokenStream
  mut cur : Token
  mut cur_kind : ClusterBreak
  mut cur_emoji : Bool
  mut done : Bool
}

///|

///|
fn SimpleState::SimpleState(chars : Iter[Token]) -> SimpleState {
  let stream = TokenStream::TokenStream(chars)
  match stream.next() {
    None =>
      {
        chars: stream,
        cur: Token::default(),
        cur_kind: XX,
        cur_emoji: false,
        done: true,
      }
    Some(first) => {
      let (kind, emoji) = first.info.cluster_class()
      {
        chars: stream,
        cur: first,
        cur_kind: kind,
        cur_emoji: emoji,
        done: false,
      }
    }
  }
}

///|
fn SimpleState::next(self : SimpleState, cluster : CharCluster) -> Bool {
  if self.done {
    return false
  }
  simple_parse(self, cluster) |> ignore
  true
}

///|
fn cluster_break_index(kind : ClusterBreak) -> Int {
  match kind {
    CN => 0
    CR => 1
    EX => 2
    L => 3
    LF => 4
    LV => 5
    LVT => 6
    PP => 7
    RI => 8
    SM => 9
    T => 10
    V => 11
    XX => 12
    ZWJ => 13
  }
}

///|
fn cluster_break_eq(a : ClusterBreak, b : ClusterBreak) -> Bool {
  cluster_break_index(a) == cluster_break_index(b)
}

///|
fn simple_accept(
  s : SimpleState,
  cluster : CharCluster,
  kind : ClusterBreak,
) -> Bool? {
  if cluster_break_eq(s.cur_kind, kind) {
    match simple_accept_any(s, cluster) {
      None => None
      Some(_) => Some(true)
    }
  } else {
    Some(false)
  }
}

///|
fn simple_accept_as(
  s : SimpleState,
  cluster : CharCluster,
  kind : ClusterBreak,
  as_class : ShapeClass,
) -> Bool? {
  if cluster_break_eq(s.cur_kind, kind) {
    match simple_accept_any_as(s, cluster, as_class) {
      None => None
      Some(_) => Some(true)
    }
  } else {
    Some(false)
  }
}

///|
fn simple_accept_any(s : SimpleState, cluster : CharCluster) -> Unit? {
  cluster.push(s.cur, Base)
  match simple_advance(s, cluster) {
    None => None
    Some(_) => Some(())
  }
}

///|
fn simple_accept_any_as(
  s : SimpleState,
  cluster : CharCluster,
  as_class : ShapeClass,
) -> Unit? {
  cluster.push(s.cur, as_class)
  match simple_advance(s, cluster) {
    None => None
    Some(_) => Some(())
  }
}

///|
fn simple_advance(s : SimpleState, cluster : CharCluster) -> Unit? {
  if cluster.chars.length() == MAX_CLUSTER_SIZE {
    return None
  }
  match s.chars.next() {
    None => {
      s.done = true
      None
    }
    Some(input) => {
      let (kind, emoji) = input.info.cluster_class()
      s.cur = input
      s.cur_emoji = emoji
      s.cur_kind = kind
      Some(())
    }
  }
}

///|
fn simple_parse_emoji_extension(
  s : SimpleState,
  cluster : CharCluster,
) -> Bool? {
  while true {
    if cluster_break_eq(s.cur_kind, EX) {
      match s.cur.ch.to_int() {
        0x200C =>
          match simple_accept_any_as(s, cluster, Zwnj) {
            None => return None
            Some(_) => ()
          }
        0xFE0F => {
          cluster.set_emoji(Color)
          cluster.note_char(s.cur)
          match simple_advance(s, cluster) {
            None => return None
            Some(_) => ()
          }
        }
        0xFE0E => {
          cluster.set_emoji(Text)
          cluster.note_char(s.cur)
          match simple_advance(s, cluster) {
            None => return None
            Some(_) => ()
          }
        }
        _ =>
          match simple_accept_any_as(s, cluster, Mark) {
            None => return None
            Some(_) => ()
          }
      }
    } else if cluster_break_eq(s.cur_kind, ZWJ) {
      match simple_accept_any_as(s, cluster, Zwj) {
        None => return None
        Some(_) => ()
      }
      return Some(true)
    } else {
      break
    }
  }
  Some(false)
}

///|
fn simple_parse_extension(s : SimpleState, cluster : CharCluster) -> Bool? {
  if cluster_break_eq(s.cur_kind, EX) {
    if s.cur.ch.to_int() == 0x200C {
      match simple_accept_any_as(s, cluster, Zwnj) {
        None => return None
        Some(_) => ()
      }
    } else if s.cur.info.is_variation_selector() {
      match simple_accept_any_as(s, cluster, Vs) {
        None => return None
        Some(_) => ()
      }
    } else {
      cluster.force_normalize()
      match simple_accept_any_as(s, cluster, Mark) {
        None => return None
        Some(_) => ()
      }
    }
    Some(true)
  } else if cluster_break_eq(s.cur_kind, SM) {
    cluster.force_normalize()
    match simple_accept_any_as(s, cluster, Mark) {
      None => return None
      Some(_) => ()
    }
    Some(true)
  } else if cluster_break_eq(s.cur_kind, ZWJ) {
    match simple_accept_any_as(s, cluster, Zwj) {
      None => return None
      Some(_) => ()
    }
    Some(true)
  } else {
    Some(false)
  }
}

///|
fn simple_parse(s : SimpleState, cluster : CharCluster) -> Unit? {
  while true {
    match simple_accept(s, cluster, PP) {
      None => return None
      Some(true) => ()
      Some(false) => break
    }
  }
  if s.cur_emoji {
    cluster.set_emoji(Default)
    while s.cur_emoji {
      match simple_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match simple_parse_emoji_extension(s, cluster) {
        None => return None
        Some(true) => ()
        Some(false) => break
      }
    }
  } else {
    match s.cur_kind {
      CN =>
        match simple_accept_any_as(s, cluster, Control) {
          None => return None
          Some(_) => ()
        }
      LF => {
        cluster.set_space(Newline)
        match simple_accept_any_as(s, cluster, Control) {
          None => return None
          Some(_) => ()
        }
      }
      CR => {
        cluster.set_space(Newline)
        match simple_accept_any_as(s, cluster, Control) {
          None => return None
          Some(_) => ()
        }
        match simple_accept_as(s, cluster, LF, Control) {
          None => return None
          Some(v) => v |> ignore
        }
      }
      L => {
        match simple_accept_any(s, cluster) {
          None => return None
          Some(_) => ()
        }
        match s.cur_kind {
          L =>
            match simple_accept_any(s, cluster) {
              None => return None
              Some(_) => ()
            }
          V =>
            match simple_accept_any(s, cluster) {
              None => return None
              Some(_) => ()
            }
          LV =>
            match simple_accept_any(s, cluster) {
              None => return None
              Some(_) => ()
            }
          LVT =>
            match simple_accept_any(s, cluster) {
              None => return None
              Some(_) => ()
            }
          _ => ()
        }
      }
      LV => {
        match simple_accept_any(s, cluster) {
          None => return None
          Some(_) => ()
        }
        match s.cur_kind {
          V =>
            match simple_accept_any(s, cluster) {
              None => return None
              Some(_) => ()
            }
          T =>
            match simple_accept_any(s, cluster) {
              None => return None
              Some(_) => ()
            }
          _ => ()
        }
      }
      V => {
        match simple_accept_any(s, cluster) {
          None => return None
          Some(_) => ()
        }
        match s.cur_kind {
          V =>
            match simple_accept_any(s, cluster) {
              None => return None
              Some(_) => ()
            }
          T =>
            match simple_accept_any(s, cluster) {
              None => return None
              Some(_) => ()
            }
          _ => ()
        }
      }
      LVT => {
        match simple_accept_any(s, cluster) {
          None => return None
          Some(_) => ()
        }
        match simple_accept(s, cluster, T) {
          None => return None
          Some(v) => v |> ignore
        }
      }
      T => {
        match simple_accept_any(s, cluster) {
          None => return None
          Some(_) => ()
        }
        match simple_accept(s, cluster, T) {
          None => return None
          Some(v) => v |> ignore
        }
      }
      RI =>
        match simple_accept(s, cluster, RI) {
          None => return None
          Some(v) => v |> ignore
        }
      EX => cluster.set_broken()
      SM => cluster.set_broken()
      ZWJ => cluster.set_broken()
      _ => {
        cluster.set_space_from_char(s.cur.ch)
        match simple_accept_any(s, cluster) {
          None => return None
          Some(_) => ()
        }
      }
    }
  }
  while true {
    match simple_parse_extension(s, cluster) {
      None => return None
      Some(true) => ()
      Some(false) => break
    }
  }
  Some(())
}

///|
fn UseClass::to_shape_class(self : UseClass) -> ShapeClass {
  match self {
    B => Base
    H => Halant
    VPre => VPre
    VMPre => VMPre
    VBlw => VBlw
    R => Reph
    ZWNJ => Zwnj
    ZWJ => Zwj
    _ => Other
  }
}

///|
priv struct ComplexTokens {
  stream : TokenStream
  decomp : Array[(Token, UseClass)]
  mut decomp_offset : Int
  script : Script
}

///|

///|
fn ComplexTokens::ComplexTokens(
  script : Script,
  iter : Iter[Token],
) -> ComplexTokens {
  {
    stream: TokenStream(iter),
    decomp: ([] : Array[(Token, UseClass)]),
    decomp_offset: 0,
    script,
  }
}

///|
fn ComplexTokens::next(self : ComplexTokens) -> (Token, UseClass, Bool)? {
  if self.decomp_offset < self.decomp.length() {
    let (input, class) = self.decomp[self.decomp_offset]
    self.decomp_offset = self.decomp_offset + 1
    Some((input, class, false))
  } else {
    match self.stream.next() {
      None => None
      Some(input) => {
        let (class, needs_decomp, emoji) = input.info.use_class()
        if needs_decomp {
          self.decomp.clear()
          self.decomp_offset = 0
          for c in Codepoint::decompose(input.ch) {
            if self.decomp.length() == 3 {
              break
            }
            let props = Codepoint::properties(c)
            let (cclass, _, _) = props.use_class()
            let c2 = Token::{
              ch: c,
              offset: input.offset,
              len: input.len,
              info: input.info.with_properties(props),
              data: input.data,
            }
            self.decomp.push((c2, cclass))
          }
          self.next()
        } else {
          match self.script {
            Khmer =>
              match input.ch.to_int() {
                0x17BE => {
                  let a = '\u{17C1}'
                  let props = Codepoint::properties(a)
                  let (aclass, _, _) = props.use_class()
                  let a = Token::{
                    ch: a,
                    offset: input.offset,
                    len: input.len,
                    info: input.info.with_properties(props),
                    data: input.data,
                  }
                  self.decomp.clear()
                  self.decomp.push((a, aclass))
                  self.decomp.push((input, class))
                  self.decomp_offset = 0
                  self.next()
                }
                0x17BF => {
                  let a = '\u{17C1}'
                  let props = Codepoint::properties(a)
                  let (aclass, _, _) = props.use_class()
                  let a = Token::{
                    ch: a,
                    offset: input.offset,
                    len: input.len,
                    info: input.info.with_properties(props),
                    data: input.data,
                  }
                  self.decomp.clear()
                  self.decomp.push((a, aclass))
                  self.decomp.push((input, class))
                  self.decomp_offset = 0
                  self.next()
                }
                0x17C0 => {
                  let a = '\u{17C1}'
                  let props = Codepoint::properties(a)
                  let (aclass, _, _) = props.use_class()
                  let a = Token::{
                    ch: a,
                    offset: input.offset,
                    len: input.len,
                    info: input.info.with_properties(props),
                    data: input.data,
                  }
                  self.decomp.clear()
                  self.decomp.push((a, aclass))
                  self.decomp.push((input, class))
                  self.decomp_offset = 0
                  self.next()
                }
                0x17C4 => {
                  let a = '\u{17C1}'
                  let props = Codepoint::properties(a)
                  let (aclass, _, _) = props.use_class()
                  let a = Token::{
                    ch: a,
                    offset: input.offset,
                    len: input.len,
                    info: input.info.with_properties(props),
                    data: input.data,
                  }
                  self.decomp.clear()
                  self.decomp.push((a, aclass))
                  self.decomp.push((input, class))
                  self.decomp_offset = 0
                  self.next()
                }
                0x17C5 => {
                  let a = '\u{17C1}'
                  let props = Codepoint::properties(a)
                  let (aclass, _, _) = props.use_class()
                  let a = Token::{
                    ch: a,
                    offset: input.offset,
                    len: input.len,
                    info: input.info.with_properties(props),
                    data: input.data,
                  }
                  self.decomp.clear()
                  self.decomp.push((a, aclass))
                  self.decomp.push((input, class))
                  self.decomp_offset = 0
                  self.next()
                }
                _ => Some((input, class, emoji))
              }
            _ => Some((input, class, emoji))
          }
        }
      }
    }
  }
}

///|
priv struct ComplexState {
  chars : ComplexTokens
  mut cur : Token
  mut cur_kind : UseClass
  mut cur_emoji : Bool
  mut done : Bool
}

///|

///|
fn ComplexState::ComplexState(
  script : Script,
  chars : Iter[Token],
) -> ComplexState {
  let tokens = ComplexTokens::ComplexTokens(script, chars)
  match tokens.next() {
    None =>
      {
        chars: tokens,
        cur: Token::default(),
        cur_kind: O,
        cur_emoji: false,
        done: true,
      }
    Some((first, kind, emoji)) =>
      {
        chars: tokens,
        cur: first,
        cur_kind: kind,
        cur_emoji: emoji,
        done: false,
      }
  }
}

///|
fn ComplexState::next(self : ComplexState, cluster : CharCluster) -> Bool {
  if self.done {
    return false
  }
  complex_parse(self, cluster) |> ignore
  true
}

///|
fn use_class_index(kind : UseClass) -> Int {
  match kind {
    B => 0
    CGJ => 1
    CMAbv => 2
    CMBlw => 3
    CS => 4
    FAbv => 5
    FBlw => 6
    FPst => 7
    FM => 8
    GB => 9
    H => 10
    HN => 11
    IND => 12
    MAbv => 13
    MBlw => 14
    MPre => 15
    MPst => 16
    N => 17
    O => 18
    R => 19
    Rsv => 20
    S => 21
    SMAbv => 22
    SMBlw => 23
    SUB => 24
    VAbv => 25
    VBlw => 26
    VPre => 27
    VPst => 28
    VMAbv => 29
    VMBlw => 30
    VMPre => 31
    VMPst => 32
    VS => 33
    WJ => 34
    ZWJ => 35
    ZWNJ => 36
  }
}

///|
fn use_class_eq(a : UseClass, b : UseClass) -> Bool {
  use_class_index(a) == use_class_index(b)
}

///|
fn complex_accept_as(
  s : ComplexState,
  cluster : CharCluster,
  kind : UseClass,
  as_class : ShapeClass,
) -> Bool? {
  if use_class_eq(s.cur_kind, kind) {
    match complex_accept_any_as(s, cluster, as_class) {
      None => None
      Some(_) => Some(true)
    }
  } else {
    Some(false)
  }
}

///|
fn complex_accept(
  s : ComplexState,
  cluster : CharCluster,
  kind : UseClass,
) -> Bool? {
  complex_accept_as(s, cluster, kind, Other)
}

///|
fn complex_accept_zero_or_many(
  s : ComplexState,
  cluster : CharCluster,
  kind : UseClass,
) -> Bool? {
  let mut some = false
  while true {
    match complex_accept(s, cluster, kind) {
      None => return None
      Some(true) => some = true
      Some(false) => break
    }
  }
  Some(some)
}

///|
fn complex_accept_zero_or_many_as(
  s : ComplexState,
  cluster : CharCluster,
  kind : UseClass,
  as_class : ShapeClass,
) -> Bool? {
  let mut some = false
  while true {
    match complex_accept_as(s, cluster, kind, as_class) {
      None => return None
      Some(true) => some = true
      Some(false) => break
    }
  }
  Some(some)
}

///|
fn complex_accept_any(s : ComplexState, cluster : CharCluster) -> Unit? {
  cluster.push(s.cur, Other)
  match complex_advance(s, cluster) {
    None => None
    Some(_) => Some(())
  }
}

///|
fn complex_accept_any_as(
  s : ComplexState,
  cluster : CharCluster,
  as_class : ShapeClass,
) -> Unit? {
  cluster.push(s.cur, as_class)
  match complex_advance(s, cluster) {
    None => None
    Some(_) => Some(())
  }
}

///|
fn complex_advance(s : ComplexState, cluster : CharCluster) -> Unit? {
  if cluster.chars.length() == MAX_CLUSTER_SIZE {
    return None
  }
  match s.chars.next() {
    None => {
      s.done = true
      None
    }
    Some((input, kind, emoji)) => {
      s.cur = input
      s.cur_kind = kind
      s.cur_emoji = emoji
      if input.ch == '\u{034F}' {
        match complex_accept_any_as(s, cluster, Other) {
          None => return None
          Some(_) => ()
        }
      }
      Some(())
    }
  }
}

///|
fn complex_parse_emoji_extension(
  s : ComplexState,
  cluster : CharCluster,
) -> Bool? {
  while true {
    match s.cur.info.cluster_break() {
      EX =>
        match s.cur.ch.to_int() {
          0x200C =>
            match complex_accept_any_as(s, cluster, Zwnj) {
              None => return None
              Some(_) => ()
            }
          0xFE0F => {
            cluster.set_emoji(Color)
            cluster.note_char(s.cur)
            match complex_advance(s, cluster) {
              None => return None
              Some(_) => ()
            }
          }
          0xFE0E => {
            cluster.set_emoji(Text)
            cluster.note_char(s.cur)
            match complex_advance(s, cluster) {
              None => return None
              Some(_) => ()
            }
          }
          _ =>
            match complex_accept_any_as(s, cluster, Mark) {
              None => return None
              Some(_) => ()
            }
        }
      ZWJ => {
        match complex_accept_any_as(s, cluster, Zwj) {
          None => return None
          Some(_) => ()
        }
        return Some(true)
      }
      _ => break
    }
  }
  Some(false)
}

///|
fn complex_parse_halant_number(
  s : ComplexState,
  cluster : CharCluster,
) -> Bool? {
  match s.cur_kind {
    HN => {
      match complex_accept_any_as(s, cluster, Halant) {
        None => return None
        Some(_) => ()
      }
      match s.cur_kind {
        N => {
          match complex_accept_any_as(s, cluster, Base) {
            None => return None
            Some(_) => ()
          }
          match complex_accept_as(s, cluster, VS, Vs) {
            None => return None
            Some(v) => v |> ignore
          }
          Some(true)
        }
        _ => Some(false)
      }
    }
    _ => None
  }
}

///|
fn complex_parse_halant_base(
  s : ComplexState,
  cluster : CharCluster,
  vt : Ref[Bool],
) -> Bool? {
  vt.val = false
  match s.cur_kind {
    SUB => {
      match complex_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      match complex_accept_zero_or_many(s, cluster, CMAbv) {
        None => return None
        Some(v) => v |> ignore
      }
      match complex_accept_zero_or_many(s, cluster, CMBlw) {
        None => return None
        Some(v) => v |> ignore
      }
      Some(true)
    }
    H => {
      vt.val = true
      match s.chars.script {
        Khmer =>
          if s.cur.ch == '\u{17D2}' {
            match complex_accept_any_as(s, cluster, Other) {
              None => return None
              Some(_) => ()
            }
          } else {
            match complex_accept_any_as(s, cluster, Halant) {
              None => return None
              Some(_) => ()
            }
          }
        _ =>
          match complex_accept_any_as(s, cluster, Halant) {
            None => return None
            Some(_) => ()
          }
      }
      match s.cur_kind {
        B => {
          vt.val = false
          match complex_accept_any_as(s, cluster, Base) {
            None => return None
            Some(_) => ()
          }
          match complex_accept_as(s, cluster, VS, Vs) {
            None => return None
            Some(v) => v |> ignore
          }
          match complex_accept_zero_or_many(s, cluster, CMAbv) {
            None => return None
            Some(v) => v |> ignore
          }
          match complex_accept_zero_or_many(s, cluster, CMBlw) {
            None => return None
            Some(v) => v |> ignore
          }
          Some(true)
        }
        _ => Some(false)
      }
    }
    _ => Some(false)
  }
}

///|
fn complex_parse_vowel_modifier(
  s : ComplexState,
  cluster : CharCluster,
) -> Bool? {
  match s.cur_kind {
    VMPre => {
      match complex_accept_any_as(s, cluster, VMPre) {
        None => return None
        Some(_) => ()
      }
      Some(true)
    }
    VMAbv => {
      match complex_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      Some(true)
    }
    VMBlw => {
      match complex_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      Some(true)
    }
    VMPst => {
      match complex_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      Some(true)
    }
    H => {
      match complex_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      Some(true)
    }
    _ => Some(false)
  }
}

///|
fn complex_parse_standard_tail(
  s : ComplexState,
  cluster : CharCluster,
  is_potential_symbol : Bool,
  vt : Ref[Bool],
) -> Unit? {
  match complex_accept_as(s, cluster, VS, Vs) {
    None => return None
    Some(v) => v |> ignore
  }
  let k = s.cur_kind
  if is_potential_symbol && (use_class_eq(k, SMAbv) || use_class_eq(k, SMBlw)) {
    match complex_accept_zero_or_many(s, cluster, SMAbv) {
      None => return None
      Some(v) => v |> ignore
    }
    match complex_accept_zero_or_many(s, cluster, SMBlw) {
      None => return None
      Some(v) => v |> ignore
    }
    return Some(())
  }
  match complex_accept_zero_or_many(s, cluster, CMAbv) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept_zero_or_many(s, cluster, CMBlw) {
    None => return None
    Some(v) => v |> ignore
  }
  while true {
    match complex_parse_halant_base(s, cluster, vt) {
      None => return None
      Some(true) => ()
      Some(false) => break
    }
  }
  if vt.val {
    return Some(())
  }
  match complex_accept(s, cluster, MPre) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept(s, cluster, MAbv) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept(s, cluster, MBlw) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept(s, cluster, MBlw) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept(s, cluster, MPst) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept_zero_or_many_as(s, cluster, VPre, VPre) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept_zero_or_many(s, cluster, VAbv) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept_zero_or_many(s, cluster, VBlw) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept_zero_or_many(s, cluster, VPst) {
    None => return None
    Some(v) => v |> ignore
  }
  while true {
    match complex_parse_vowel_modifier(s, cluster) {
      None => return None
      Some(true) => ()
      Some(false) => break
    }
  }
  match complex_accept_zero_or_many(s, cluster, FAbv) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept_zero_or_many(s, cluster, FBlw) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept_zero_or_many(s, cluster, FPst) {
    None => return None
    Some(v) => v |> ignore
  }
  match complex_accept(s, cluster, FM) {
    None => return None
    Some(v) => v |> ignore
  }
  Some(())
}

///|
fn complex_parse_standard(
  s : ComplexState,
  cluster : CharCluster,
  is_potential_symbol : Bool,
  vt : Ref[Bool],
) -> Unit? {
  match s.cur_kind {
    B => {
      match complex_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match complex_parse_standard_tail(s, cluster, is_potential_symbol, vt) {
        None => return None
        Some(_) => ()
      }
    }
    GB => {
      match complex_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match complex_parse_standard_tail(s, cluster, is_potential_symbol, vt) {
        None => return None
        Some(_) => ()
      }
    }
    _ => {
      cluster.set_broken()
      match complex_accept_any_as(s, cluster, s.cur_kind.to_shape_class()) {
        None => return None
        Some(_) => ()
      }
    }
  }
  Some(())
}

///|
fn complex_parse(s : ComplexState, cluster : CharCluster) -> Unit? {
  let vt = Ref(false)
  if s.done {
    return Some(())
  }
  if s.cur_emoji {
    cluster.set_emoji(Default)
    while s.cur_emoji {
      match complex_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match complex_parse_emoji_extension(s, cluster) {
        None => return None
        Some(true) => ()
        Some(false) => break
      }
    }
    return Some(())
  }
  match s.cur_kind {
    O =>
      match s.cur.ch {
        '\r' => {
          cluster.set_space(Newline)
          match complex_accept_any_as(s, cluster, Control) {
            None => return None
            Some(_) => ()
          }
          if s.cur.ch == '\n' {
            match complex_accept_any_as(s, cluster, Control) {
              None => return None
              Some(_) => ()
            }
          }
        }
        '\n' => {
          cluster.set_space(Newline)
          match complex_accept_any_as(s, cluster, Control) {
            None => return None
            Some(_) => ()
          }
        }
        _ => {
          cluster.set_space_from_char(s.cur.ch)
          let class = match s.cur.info.category() {
            Format =>
              match s.cur.ch.to_int() {
                0x200C => ShapeClass::Zwnj
                0x200D => Zwj
                _ => Control
              }
            Control => Control
            _ => Base
          }
          match complex_accept_any_as(s, cluster, class) {
            None => return None
            Some(_) => ()
          }
        }
      }
    IND => {
      match complex_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match complex_accept_as(s, cluster, VS, Vs) {
        None => return None
        Some(v) => v |> ignore
      }
    }
    Rsv => {
      match complex_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match complex_accept_as(s, cluster, VS, Vs) {
        None => return None
        Some(v) => v |> ignore
      }
    }
    WJ => {
      match complex_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match complex_accept_as(s, cluster, VS, Vs) {
        None => return None
        Some(v) => v |> ignore
      }
    }
    R => {
      match complex_accept_any_as(s, cluster, Reph) {
        None => return None
        Some(_) => ()
      }
      match complex_parse_standard(s, cluster, false, vt) {
        None => return None
        Some(_) => ()
      }
    }
    CS => {
      match complex_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      match complex_parse_standard(s, cluster, false, vt) {
        None => return None
        Some(_) => ()
      }
    }
    B =>
      match complex_parse_standard(s, cluster, false, vt) {
        None => return None
        Some(_) => ()
      }
    GB =>
      match complex_parse_standard(s, cluster, true, vt) {
        None => return None
        Some(_) => ()
      }
    N => {
      match complex_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match complex_accept_as(s, cluster, VS, Vs) {
        None => return None
        Some(v) => v |> ignore
      }
      while true {
        match complex_parse_halant_number(s, cluster) {
          None => break
          Some(true) => ()
          Some(false) => break
        }
      }
    }
    S => {
      match complex_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match complex_accept_as(s, cluster, VS, Vs) {
        None => return None
        Some(v) => v |> ignore
      }
      match complex_accept_zero_or_many(s, cluster, SMAbv) {
        None => return None
        Some(v) => v |> ignore
      }
      match complex_accept_zero_or_many(s, cluster, SMBlw) {
        None => return None
        Some(v) => v |> ignore
      }
    }
    _ =>
      match complex_parse_standard(s, cluster, false, vt) {
        None => return None
        Some(_) => ()
      }
  }
  None
}

///|
priv struct MyanmarState {
  chars : TokenStream
  mut cur : Token
  mut cur_kind : MyanmarClass
  mut cur_emoji : Bool
  mut done : Bool
}

///|

///|
fn MyanmarState::MyanmarState(chars : Iter[Token]) -> MyanmarState {
  let stream = TokenStream::TokenStream(chars)
  match stream.next() {
    None =>
      {
        chars: stream,
        cur: Token::default(),
        cur_kind: O,
        cur_emoji: false,
        done: true,
      }
    Some(first) => {
      let (kind, emoji) = first.info.myanmar_class()
      {
        chars: stream,
        cur: first,
        cur_kind: kind,
        cur_emoji: emoji,
        done: false,
      }
    }
  }
}

///|
fn MyanmarState::next(self : MyanmarState, cluster : CharCluster) -> Bool {
  if self.done {
    return false
  }
  myanmar_parse(self, cluster) |> ignore
  true
}

///|
fn myanmar_class_index(kind : MyanmarClass) -> Int {
  match kind {
    A => 0
    As => 1
    C => 2
    D => 3
    D0 => 4
    DB => 5
    GB => 6
    H => 7
    IV => 8
    J => 9
    K => 10
    MH => 11
    MR => 12
    MW => 13
    MY => 14
    O => 15
    P => 16
    PT => 17
    R => 18
    S => 19
    V => 20
    VAbv => 21
    VBlw => 22
    VPre => 23
    VPst => 24
    VS => 25
    WJ => 26
  }
}

///|
fn myanmar_class_eq(a : MyanmarClass, b : MyanmarClass) -> Bool {
  myanmar_class_index(a) == myanmar_class_index(b)
}

///|
fn myanmar_accept_as(
  s : MyanmarState,
  cluster : CharCluster,
  kind : MyanmarClass,
  as_class : ShapeClass,
) -> Bool? {
  if myanmar_class_eq(s.cur_kind, kind) {
    match myanmar_accept_any_as(s, cluster, as_class) {
      None => None
      Some(_) => Some(true)
    }
  } else {
    Some(false)
  }
}

///|
fn myanmar_accept(
  s : MyanmarState,
  cluster : CharCluster,
  kind : MyanmarClass,
) -> Bool? {
  myanmar_accept_as(s, cluster, kind, Other)
}

///|
fn myanmar_accept_zero_or_many(
  s : MyanmarState,
  cluster : CharCluster,
  kind : MyanmarClass,
) -> Bool? {
  let mut some = false
  while true {
    match myanmar_accept(s, cluster, kind) {
      None => return None
      Some(true) => some = true
      Some(false) => break
    }
  }
  Some(some)
}

///|
fn myanmar_accept_zero_or_many_as(
  s : MyanmarState,
  cluster : CharCluster,
  kind : MyanmarClass,
  as_class : ShapeClass,
) -> Bool? {
  let mut some = false
  while true {
    match myanmar_accept_as(s, cluster, kind, as_class) {
      None => return None
      Some(true) => some = true
      Some(false) => break
    }
  }
  Some(some)
}

///|
fn myanmar_accept_any(s : MyanmarState, cluster : CharCluster) -> Unit? {
  cluster.push(s.cur, Other)
  match myanmar_advance(s, cluster) {
    None => None
    Some(_) => Some(())
  }
}

///|
fn myanmar_accept_any_as(
  s : MyanmarState,
  cluster : CharCluster,
  as_class : ShapeClass,
) -> Unit? {
  cluster.push(s.cur, as_class)
  match myanmar_advance(s, cluster) {
    None => None
    Some(_) => Some(())
  }
}

///|
fn myanmar_advance(s : MyanmarState, cluster : CharCluster) -> Unit? {
  if cluster.chars.length() == MAX_CLUSTER_SIZE {
    return None
  }
  match s.chars.next() {
    None => {
      s.done = true
      None
    }
    Some(input) => {
      let (kind, emoji) = input.info.myanmar_class()
      s.cur = input
      s.cur_emoji = emoji
      s.cur_kind = kind
      if input.ch == '\u{034F}' {
        match myanmar_accept_any(s, cluster) {
          None => return None
          Some(_) => ()
        }
      }
      Some(())
    }
  }
}

///|
fn myanmar_parse_emoji_extension(
  s : MyanmarState,
  cluster : CharCluster,
) -> Bool? {
  while true {
    match s.cur.info.cluster_break() {
      EX =>
        match s.cur.ch.to_int() {
          0x200C =>
            match myanmar_accept_any_as(s, cluster, Zwnj) {
              None => return None
              Some(_) => ()
            }
          0xFE0F => {
            cluster.set_emoji(Color)
            cluster.note_char(s.cur)
            match myanmar_advance(s, cluster) {
              None => return None
              Some(_) => ()
            }
          }
          0xFE0E => {
            cluster.set_emoji(Text)
            cluster.note_char(s.cur)
            match myanmar_advance(s, cluster) {
              None => return None
              Some(_) => ()
            }
          }
          _ =>
            match myanmar_accept_any_as(s, cluster, Mark) {
              None => return None
              Some(_) => ()
            }
        }
      ZWJ => {
        match myanmar_accept_any_as(s, cluster, Zwj) {
          None => return None
          Some(_) => ()
        }
        return Some(true)
      }
      _ => break
    }
  }
  Some(false)
}

///|
fn myanmar_parse_stacked_consonant_or_vowel(
  s : MyanmarState,
  cluster : CharCluster,
  vt : Ref[Bool],
) -> Bool? {
  vt.val = false
  match s.cur_kind {
    H => {
      vt.val = true
      match myanmar_accept_any_as(s, cluster, Halant) {
        None => return None
        Some(_) => ()
      }
      match s.cur_kind {
        C => {
          vt.val = false
          match myanmar_accept_any_as(s, cluster, Base) {
            None => return None
            Some(_) => ()
          }
          match myanmar_accept_as(s, cluster, VS, Vs) {
            None => return None
            Some(v) => v |> ignore
          }
          Some(true)
        }
        IV => {
          vt.val = false
          match myanmar_accept_any_as(s, cluster, Base) {
            None => return None
            Some(_) => ()
          }
          match myanmar_accept_as(s, cluster, VS, Vs) {
            None => return None
            Some(v) => v |> ignore
          }
          Some(true)
        }
        _ => Some(false)
      }
    }
    _ => Some(false)
  }
}

///|
fn myanmar_parse_post_base_vowel(
  s : MyanmarState,
  cluster : CharCluster,
) -> Bool? {
  match s.cur_kind {
    VPst => {
      match myanmar_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      match myanmar_accept(s, cluster, MH) {
        None => return None
        Some(v) => v |> ignore
      }
      match myanmar_accept_zero_or_many(s, cluster, As) {
        None => return None
        Some(v) => v |> ignore
      }
      match myanmar_accept_zero_or_many(s, cluster, VAbv) {
        None => return None
        Some(v) => v |> ignore
      }
      match myanmar_accept_zero_or_many_as(s, cluster, A, Anusvara) {
        None => return None
        Some(v) => v |> ignore
      }
      match myanmar_accept(s, cluster, DB) {
        None => return None
        Some(true) =>
          match myanmar_accept(s, cluster, As) {
            None => return None
            Some(v) => v |> ignore
          }
        Some(false) => ()
      }
      Some(true)
    }
    _ => Some(false)
  }
}

///|
fn myanmar_parse_pwo_tone_mark(
  s : MyanmarState,
  cluster : CharCluster,
) -> Bool? {
  match s.cur_kind {
    PT => {
      match myanmar_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      match myanmar_accept(s, cluster, As) {
        None => return None
        Some(true) =>
          match myanmar_accept_as(s, cluster, A, Anusvara) {
            None => return None
            Some(v) => v |> ignore
          }
        Some(false) => {
          match myanmar_accept_zero_or_many_as(s, cluster, A, Anusvara) {
            None => return None
            Some(v) => v |> ignore
          }
          match myanmar_accept(s, cluster, DB) {
            None => return None
            Some(v) => v |> ignore
          }
          match myanmar_accept(s, cluster, As) {
            None => return None
            Some(v) => v |> ignore
          }
        }
      }
      Some(true)
    }
    _ => Some(false)
  }
}

///|
fn myanmar_parse(s : MyanmarState, cluster : CharCluster) -> Unit? {
  let vt = Ref(false)
  if s.done {
    return Some(())
  }
  if s.cur_emoji {
    cluster.set_emoji(Default)
    while s.cur_emoji {
      match myanmar_accept_any_as(s, cluster, Base) {
        None => return None
        Some(_) => ()
      }
      match myanmar_parse_emoji_extension(s, cluster) {
        None => return None
        Some(true) => ()
        Some(false) => break
      }
    }
    return Some(())
  }
  match s.cur_kind {
    O => {
      match s.cur.ch {
        '\r' => {
          cluster.set_space(Newline)
          match myanmar_accept_any_as(s, cluster, Control) {
            None => return None
            Some(_) => ()
          }
          if s.cur.ch == '\n' {
            match myanmar_accept_any_as(s, cluster, Control) {
              None => return None
              Some(_) => ()
            }
          }
        }
        '\n' => {
          cluster.set_space(Newline)
          match myanmar_accept_any_as(s, cluster, Control) {
            None => return None
            Some(_) => ()
          }
        }
        _ => {
          cluster.set_space_from_char(s.cur.ch)
          let class = match s.cur.info.category() {
            Format =>
              match s.cur.ch.to_int() {
                0x200C => ShapeClass::Zwnj
                0x200D => Zwj
                _ => Control
              }
            Control => Control
            _ => Base
          }
          match myanmar_accept_any_as(s, cluster, class) {
            None => return None
            Some(_) => ()
          }
        }
      }
      ()
    }
    P => {
      match myanmar_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      ()
    }
    S => {
      match myanmar_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      ()
    }
    R => {
      match myanmar_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      ()
    }
    WJ => {
      match myanmar_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      ()
    }
    D0 => {
      match myanmar_accept_any(s, cluster) {
        None => return None
        Some(_) => ()
      }
      ()
    }
    _ => {
      match s.cur.ch.to_int() {
        0x1004 =>
          match myanmar_try_kinzi(s, cluster) {
            None => return None
            Some(_) => ()
          }
        0x101B =>
          match myanmar_try_kinzi(s, cluster) {
            None => return None
            Some(_) => ()
          }
        0x105A =>
          match myanmar_try_kinzi(s, cluster) {
            None => return None
            Some(_) => ()
          }
        _ => ()
      }
      match s.cur_kind {
        C =>
          match myanmar_parse_main(s, cluster, vt) {
            None => return None
            Some(_) => ()
          }
        IV =>
          match myanmar_parse_main(s, cluster, vt) {
            None => return None
            Some(_) => ()
          }
        D =>
          match myanmar_parse_main(s, cluster, vt) {
            None => return None
            Some(_) => ()
          }
        DB =>
          match myanmar_parse_main(s, cluster, vt) {
            None => return None
            Some(_) => ()
          }
        _ => {
          cluster.set_broken()
          match myanmar_accept_any(s, cluster) {
            None => return None
            Some(_) => ()
          }
          return Some(())
        }
      }
      return Some(())
    }
  }
  None
}

///|
fn myanmar_try_kinzi(s : MyanmarState, cluster : CharCluster) -> Unit? {
  match s.chars.peek(0) {
    None => Some(())
    Some(b) =>
      if b.ch == '\u{103A}' {
        match s.chars.peek(1) {
          None => Some(())
          Some(c) =>
            if c.ch == '\u{1039}' {
              cluster.push(s.cur, Kinzi)
              cluster.push(b, Kinzi)
              cluster.push(c, Kinzi)
              match myanmar_advance(s, cluster) {
                None => return None
                Some(_) => ()
              }
              match myanmar_advance(s, cluster) {
                None => return None
                Some(_) => ()
              }
              match myanmar_advance(s, cluster) {
                None => return None
                Some(_) => ()
              }
              Some(())
            } else {
              Some(())
            }
        }
      } else {
        Some(())
      }
  }
}

///|
fn myanmar_parse_main(
  s : MyanmarState,
  cluster : CharCluster,
  vt : Ref[Bool],
) -> Unit? {
  match myanmar_accept_any_as(s, cluster, Base) {
    None => return None
    Some(_) => ()
  }
  match myanmar_accept_as(s, cluster, VS, Vs) {
    None => return None
    Some(v) => v |> ignore
  }
  while true {
    match myanmar_parse_stacked_consonant_or_vowel(s, cluster, vt) {
      None => return None
      Some(true) => ()
      Some(false) => break
    }
  }
  if vt.val {
    return Some(())
  }
  match myanmar_accept_zero_or_many(s, cluster, As) {
    None => return None
    Some(v) => v |> ignore
  }
  match myanmar_accept(s, cluster, MY) {
    None => return None
    Some(true) =>
      match myanmar_accept(s, cluster, As) {
        None => return None
        Some(v) => v |> ignore
      }
    Some(false) => ()
  }
  match myanmar_accept_as(s, cluster, MR, MedialRa) {
    None => return None
    Some(v) => v |> ignore
  }
  match myanmar_accept(s, cluster, MW) {
    None => return None
    Some(true) => {
      match myanmar_accept(s, cluster, MH) {
        None => return None
        Some(v) => v |> ignore
      }
      match myanmar_accept(s, cluster, As) {
        None => return None
        Some(v) => v |> ignore
      }
    }
    Some(false) =>
      match myanmar_accept(s, cluster, MH) {
        None => return None
        Some(true) =>
          match myanmar_accept(s, cluster, As) {
            None => return None
            Some(v) => v |> ignore
          }
        Some(false) => ()
      }
  }
  match myanmar_accept_zero_or_many_as(s, cluster, VPre, VPre) {
    None => return None
    Some(v) => v |> ignore
  }
  match myanmar_accept_zero_or_many(s, cluster, VAbv) {
    None => return None
    Some(v) => v |> ignore
  }
  match myanmar_accept_zero_or_many_as(s, cluster, VBlw, VBlw) {
    None => return None
    Some(v) => v |> ignore
  }
  match myanmar_accept_zero_or_many_as(s, cluster, A, Anusvara) {
    None => return None
    Some(v) => v |> ignore
  }
  match myanmar_accept(s, cluster, DB) {
    None => return None
    Some(true) =>
      match myanmar_accept(s, cluster, As) {
        None => return None
        Some(v) => v |> ignore
      }
    Some(false) => ()
  }
  while true {
    match myanmar_parse_post_base_vowel(s, cluster) {
      None => return None
      Some(true) => ()
      Some(false) => break
    }
  }
  while true {
    match myanmar_parse_pwo_tone_mark(s, cluster) {
      None => return None
      Some(true) => ()
      Some(false) => break
    }
  }
  match myanmar_accept_zero_or_many(s, cluster, V) {
    None => return None
    Some(v) => v |> ignore
  }
  match myanmar_accept(s, cluster, J) {
    None => return None
    Some(v) => v |> ignore
  }
  Some(())
}

///|
/// Parser that accepts a sequence of characters and outputs character clusters.
struct Parser {
  mut inner : Inner
}

///|
priv enum Inner {
  Simple(SimpleState)
  Myanmar(MyanmarState)
  Complex(ComplexState)
}

///|
pub fn Parser::Parser(script : Script, tokens : Iter[Token]) -> Parser {
  let inner = if script.is_complex() {
    match script {
      Myanmar => Inner::Myanmar(MyanmarState(tokens))
      _ => Complex(ComplexState(script, tokens))
    }
  } else {
    Simple(SimpleState(tokens))
  }
  { inner, }
}

///|
pub fn Parser::next(self : Parser, cluster : CharCluster) -> Bool {
  cluster.clear()
  match self.inner {
    Simple(s) => {
      let ok = s.next(cluster)
      self.inner = Simple(s)
      ok
    }
    Myanmar(s) => {
      let ok = s.next(cluster)
      self.inner = Myanmar(s)
      ok
    }
    Complex(s) => {
      let ok = s.next(cluster)
      self.inner = Complex(s)
      ok
    }
  }
}