// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
let khmer_feature_tags : Array[@common.Tag] = [
  @common.Tag::from_chars('l', 'o', 'c', 'l'),
  @common.Tag::from_chars('c', 'c', 'm', 'p'),
  @common.Tag::from_chars('p', 'r', 'e', 'f'),
  @common.Tag::from_chars('b', 'l', 'w', 'f'),
  @common.Tag::from_chars('a', 'b', 'v', 'f'),
  @common.Tag::from_chars('p', 's', 't', 'f'),
  @common.Tag::from_chars('c', 'f', 'a', 'r'),
  @common.Tag::from_chars('p', 'r', 'e', 's'),
  @common.Tag::from_chars('a', 'b', 'v', 's'),
  @common.Tag::from_chars('b', 'l', 'w', 's'),
  @common.Tag::from_chars('p', 's', 't', 's'),
  @common.Tag::from_chars('c', 'l', 'i', 'g'),
]

let khmer_feature_pref : Int = 2
let khmer_feature_blwf : Int = 3
let khmer_feature_abvf : Int = 4
let khmer_feature_pstf : Int = 5
let khmer_feature_cfar : Int = 6

///|
let khmer_cat_h : Int = 4

///|
let khmer_cat_ra : Int = 15

///|
let khmer_cat_vpre : Int = 22

fn khmer_category(codepoint : UInt) -> Int {
  indic_category(indic_get_categories(codepoint)).reinterpret_as_int()
}

fn reorder_range_by_order(
  buffer : Buffer,
  categories : Array[Int],
  start : Int,
  order : Array[Int],
) -> Array[Int] {
  let end = start + order.length()
  if start < 0 || end > buffer.infos.length() || start >= end {
    return categories
  }
  let next_infos : Array[GlyphInfo] = []
  let next_positions : Array[GlyphPosition] = []
  let next_categories : Array[Int] = []
  for i in 0.. Array[Int] {
  if start < 0 || end > buffer.infos.length() || start >= end {
    return categories
  }
  buffer.merge_clusters(start, end)
  let mut coeng_index = -1
  let mut num_coengs = 0
  if start + 1 < end {
    for i in (start + 1)..= end {
        break
      }
      if categories[i] == khmer_cat_h && num_coengs <= 2 {
        num_coengs = num_coengs + 1
        if categories[i + 1] == khmer_cat_ra {
          coeng_index = i
          break
        }
      }
    }
  }
  let order : Array[Int] = []
  if coeng_index >= 0 {
    order.push(coeng_index - start)
    order.push(coeng_index + 1 - start)
  }
  for i in start.. Unit {
  if categories.length() != buffer.infos.length() || syllables.length() != buffer.infos.length() {
    return ()
  }
  let mut current_categories = categories
  let mut i = 0
  while i < syllables.length() {
    let syllable = syllables[i]
    let mut j = i + 1
    while j < syllables.length() && syllables[j] == syllable {
      j = j + 1
    }
    let syllable_type = syllable & 0x0f
    if syllable_type == khmer_syllable_type_consonant ||
      syllable_type == khmer_syllable_type_broken {
      current_categories = reorder_consonant_syllable_khmer(buffer, current_categories, i, j)
    }
    i = j
  }
}

fn build_khmer_feature_masks(
  categories : Array[Int],
  syllables : Array[Int],
) -> Array[UInt] {
  let len = categories.length()
  let masks = Array::make(len, 0U)
  let pref_bit = 1U << khmer_feature_pref
  let blwf_bit = 1U << khmer_feature_blwf
  let abvf_bit = 1U << khmer_feature_abvf
  let pstf_bit = 1U << khmer_feature_pstf
  let cfar_bit = 1U << khmer_feature_cfar
  let mut i = 0
  while i < syllables.length() {
    let syllable = syllables[i]
    let mut j = i + 1
    while j < syllables.length() && syllables[j] == syllable {
      j = j + 1
    }
    let syllable_type = syllable & 0x0f
    if syllable_type == khmer_syllable_type_consonant ||
      syllable_type == khmer_syllable_type_broken {
      for k in (i + 1).. Result[Unit, ShapeError] {
  let categories : Array[UInt] = []
  for info in self.infos {
    categories.push(indic_category(indic_get_categories(info.codepoint)))
  }
  let syllables = find_syllables_khmer(categories)
  match insert_dotted_circle_for_syllables(
    font,
    self,
    syllables,
    khmer_syllable_type_broken,
  ) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  let categories_after_u : Array[UInt] = []
  let categories_after_i : Array[Int] = []
  for info in self.infos {
    let category = khmer_category(info.codepoint)
    categories_after_i.push(category)
    categories_after_u.push(category.reinterpret_as_uint())
  }
  let syllables_after = find_syllables_khmer(categories_after_u)
  reorder_khmer_syllables(self, categories_after_i, syllables_after)
  let reordered_categories : Array[Int] = []
  for info in self.infos {
    reordered_categories.push(khmer_category(info.codepoint))
  }
  let glyph_masks = build_khmer_feature_masks(reordered_categories, syllables_after)
  match self.shape_basic(font) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  let gsub = match font.gsub() {
    Err(err) => return Err(Font(err))
    Ok(value) => value
  }
  if gsub is Some(gsub) {
    match apply_gsub_features_for_syllables(
      font,
      self,
      gsub,
      khmer_feature_tags,
      syllables_after,
      glyph_masks=glyph_masks,
    ) {
      Err(err) => return Err(err)
      Ok(_) => ()
    }
  }
  apply_gpos(font, self)
}