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

fn has_vowel_constraints(script : @common.Script) -> Bool {
  script == @common.script_devanagari ||
  script == @common.script_bengali ||
  script == @common.script_gurmukhi ||
  script == @common.script_gujarati ||
  script == @common.script_oriya ||
  script == @common.script_tamil ||
  script == @common.script_telugu ||
  script == @common.script_kannada ||
  script == @common.script_malayalam ||
  script == @common.script_sinhala ||
  script == @common.script_brahmi ||
  script == @common.script_khojki ||
  script == @common.script_khudawadi ||
  script == @common.script_tirhuta ||
  script == @common.script_modi ||
  script == @common.script_takri
}

fn vowel_constraint_match(
  script : @common.Script,
  infos : Array[GlyphInfo],
  i : Int,
) -> Int {
  let len = infos.length()
  if i + 1 >= len {
    return 0
  }
  let cp = infos[i].codepoint
  let cp1 = infos[i + 1].codepoint
  if script == @common.script_devanagari {
      if cp == 0x0930U &&
        i + 2 < len &&
        cp1 == 0x094DU &&
        infos[i + 2].codepoint == 0x0907U {
        3
      } else if cp == 0x0905U {
        let matched = match cp1 {
          0x093AU | 0x093BU | 0x093EU | 0x0945U | 0x0946U | 0x0949U | 0x094AU
          | 0x094BU | 0x094CU | 0x094FU | 0x0956U | 0x0957U => true
          _ => false
        }
        if matched { 2 } else { 0 }
      } else if cp == 0x0906U {
        let matched = match cp1 {
          0x093AU | 0x0945U | 0x0946U | 0x0947U | 0x0948U => true
          _ => false
        }
        if matched { 2 } else { 0 }
      } else if cp == 0x0909U {
        if cp1 == 0x0941U { 2 } else { 0 }
      } else if cp == 0x090FU {
        let matched = match cp1 {
          0x0945U | 0x0946U | 0x0947U => true
          _ => false
        }
        if matched { 2 } else { 0 }
      } else {
        0
      }
  } else if script == @common.script_bengali {
      let matched = if cp == 0x0985U {
        cp1 == 0x09BEU
      } else if cp == 0x098BU {
        cp1 == 0x09C3U
      } else if cp == 0x098CU {
        cp1 == 0x09E2U
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_gurmukhi {
      let matched = if cp == 0x0A05U {
        match cp1 {
          0x0A3EU | 0x0A48U | 0x0A4CU => true
          _ => false
        }
      } else if cp == 0x0A72U {
        match cp1 {
          0x0A3FU | 0x0A40U | 0x0A47U => true
          _ => false
        }
      } else if cp == 0x0A73U {
        match cp1 {
          0x0A41U | 0x0A42U | 0x0A4BU => true
          _ => false
        }
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_gujarati {
      let matched = if cp == 0x0A85U {
        match cp1 {
          0x0ABEU | 0x0AC5U | 0x0AC7U | 0x0AC8U | 0x0AC9U | 0x0ACBU | 0x0ACCU => true
          _ => false
        }
      } else {
        cp == 0x0AC5U && cp1 == 0x0ABEU
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_oriya {
      let matched = if cp == 0x0B05U {
        cp1 == 0x0B3EU
      } else if cp == 0x0B0FU || cp == 0x0B13U {
        cp1 == 0x0B57U
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_tamil {
      if cp == 0x0B85U && cp1 == 0x0BC2U { 2 } else { 0 }
  } else if script == @common.script_telugu {
      let matched = if cp == 0x0C12U {
        cp1 == 0x0C4CU || cp1 == 0x0C55U
      } else if cp == 0x0C3FU || cp == 0x0C46U || cp == 0x0C4AU {
        cp1 == 0x0C55U
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_kannada {
      let matched = if cp == 0x0C89U || cp == 0x0C8BU {
        cp1 == 0x0CBEU
      } else {
        cp == 0x0C92U && cp1 == 0x0CCCU
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_malayalam {
      let matched = if cp == 0x0D07U || cp == 0x0D09U {
        cp1 == 0x0D57U
      } else if cp == 0x0D0EU {
        cp1 == 0x0D46U
      } else if cp == 0x0D12U {
        cp1 == 0x0D3EU || cp1 == 0x0D57U
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_sinhala {
      let matched = if cp == 0x0D85U {
        match cp1 {
          0x0DCFU | 0x0DD0U | 0x0DD1U => true
          _ => false
        }
      } else if cp == 0x0D8BU || cp == 0x0D8FU || cp == 0x0D94U {
        cp1 == 0x0DDFU
      } else if cp == 0x0D8DU {
        cp1 == 0x0DD8U
      } else if cp == 0x0D91U {
        match cp1 {
          0x0DCAU | 0x0DD9U | 0x0DDAU | 0x0DDCU | 0x0DDDU | 0x0DDEU => true
          _ => false
        }
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_brahmi {
      let matched = if cp == 0x11005U {
        cp1 == 0x11038U
      } else if cp == 0x1100BU {
        cp1 == 0x1103EU
      } else if cp == 0x1100FU {
        cp1 == 0x11042U
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_khojki {
      let matched = if cp == 0x11200U {
        cp1 == 0x1122CU || cp1 == 0x11231U || cp1 == 0x11233U
      } else if cp == 0x11206U {
        cp1 == 0x1122CU
      } else if cp == 0x1122CU {
        cp1 == 0x11230U || cp1 == 0x11231U
      } else {
        cp == 0x11240U && cp1 == 0x1122EU
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_khudawadi {
      let matched = if cp == 0x112B0U {
        match cp1 {
          0x112E0U | 0x112E5U | 0x112E6U | 0x112E7U | 0x112E8U => true
          _ => false
        }
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_tirhuta {
      let matched = if cp == 0x11481U {
        cp1 == 0x114B0U
      } else if cp == 0x1148BU || cp == 0x1148DU {
        cp1 == 0x114BAU
      } else if cp == 0x114AAU {
        cp1 == 0x114B5U || cp1 == 0x114B6U
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_modi {
      let matched = if cp == 0x11600U || cp == 0x11601U {
        cp1 == 0x11639U || cp1 == 0x1163AU
      } else {
        false
      }
      if matched { 2 } else { 0 }
  } else if script == @common.script_takri {
      let matched = if cp == 0x11680U {
        cp1 == 0x116ADU || cp1 == 0x116B4U || cp1 == 0x116B5U
      } else {
        cp == 0x11686U && cp1 == 0x116B2U
      }
      if matched { 2 } else { 0 }
  } else {
    0
  }
}

fn preprocess_vowel_constraints(buffer : Buffer) -> Unit {
  if !has_vowel_constraints(buffer.script) {
    return ()
  }
  let infos = buffer.infos
  if infos.length() < 2 {
    return ()
  }
  let positions = buffer.positions
  let dotted_position = GlyphPosition::{ x_advance: 0, y_advance: 0, x_offset: 0, y_offset: 0 }
  let next_infos : Array[GlyphInfo] = []
  let next_positions : Array[GlyphPosition] = []
  let mut i = 0
  while i < infos.length() {
    let match_len = vowel_constraint_match(buffer.script, infos, i)
    if match_len == 3 {
      next_infos.push(infos[i])
      next_positions.push(positions[i])
      next_infos.push(infos[i + 1])
      next_positions.push(positions[i + 1])
      let cluster = infos[i + 2].cluster
      next_infos.push(GlyphInfo::{ codepoint: dotted_circle_codepoint, cluster, unicode: dotted_circle_codepoint })
      next_positions.push(dotted_position)
      next_infos.push(infos[i + 2])
      next_positions.push(positions[i + 2])
      i = i + 3
      continue
    }
    if match_len == 2 {
      next_infos.push(infos[i])
      next_positions.push(positions[i])
      let cluster = infos[i + 1].cluster
      next_infos.push(GlyphInfo::{ codepoint: dotted_circle_codepoint, cluster, unicode: dotted_circle_codepoint })
      next_positions.push(dotted_position)
      next_infos.push(infos[i + 1])
      next_positions.push(positions[i + 1])
      i = i + 2
      continue
    }
    next_infos.push(infos[i])
    next_positions.push(positions[i])
    i = i + 1
  }
  buffer.infos = next_infos
  buffer.positions = next_positions
}