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

///|
pub let codepoint_invalid : UInt = 0xFFFF_FFFFU

///|
pub let map_value_invalid : UInt = codepoint_invalid

///|
pub struct CodepointSet {
  set : Set[UInt]
}

///|
pub fn CodepointSet::new() -> CodepointSet {
  CodepointSet::{ set: Set::new() }
}

///|
pub fn CodepointSet::clear(self : CodepointSet) -> Unit {
  self.set.clear()
}

///|
pub fn CodepointSet::is_empty(self : CodepointSet) -> Bool {
  self.set.is_empty()
}

///|
pub fn CodepointSet::population(self : CodepointSet) -> Int {
  self.set.length()
}

///|
pub fn CodepointSet::get_min(self : CodepointSet) -> UInt {
  let values = sorted_values(self)
  if values.is_empty() { map_value_invalid } else { values[0] }
}

///|
pub fn CodepointSet::get_max(self : CodepointSet) -> UInt {
  let values = sorted_values(self)
  if values.is_empty() { map_value_invalid } else { values[values.length() - 1] }
}

///|
pub fn CodepointSet::add(self : CodepointSet, value : UInt) -> Unit {
  self.set.add(value)
}

///|
pub fn CodepointSet::add_array(self : CodepointSet, values : ArrayView[UInt]) -> Unit {
  for value in values {
    self.set.add(value)
  }
}

///|
pub fn CodepointSet::add_sorted_array(
  self : CodepointSet,
  values : ArrayView[UInt],
) -> Bool {
  if values.is_empty() {
    return true
  }
  let mut prev = values[0]
  for i in 1.. Bool {
  if first > last {
    return false
  }
  let mut value = first
  while true {
    self.set.add(value)
    if value == last {
      break
    }
    value = value + 1U
  }
  true
}

///|
pub fn CodepointSet::del(self : CodepointSet, value : UInt) -> Unit {
  self.set.remove(value)
}

///|
pub fn CodepointSet::del_range(
  self : CodepointSet,
  first : UInt,
  last : UInt,
) -> Unit {
  if first > last {
    return ()
  }
  let mut value = first
  while true {
    self.set.remove(value)
    if value == last {
      break
    }
    value = value + 1U
  }
}

///|
pub fn CodepointSet::has(self : CodepointSet, value : UInt) -> Bool {
  self.set.contains(value)
}

///|
pub fn CodepointSet::to_sorted_array(self : CodepointSet) -> Array[UInt] {
  let values = self.set.to_array()
  values.sort()
  values
}

///|
pub fn CodepointSet::union(self : CodepointSet, other : CodepointSet) -> CodepointSet {
  let merged = CodepointSet::new()
  for value in self.set.iter() {
    merged.set.add(value)
  }
  for value in other.set.iter() {
    merged.set.add(value)
  }
  merged
}

///|
pub fn CodepointSet::set_from(self : CodepointSet, other : CodepointSet) -> Unit {
  self.set.clear()
  for value in other.set.iter() {
    self.set.add(value)
  }
}

///|
pub fn CodepointSet::union_in_place(self : CodepointSet, other : CodepointSet) -> Unit {
  for value in other.set.iter() {
    self.set.add(value)
  }
}

///|
pub fn CodepointSet::intersect_in_place(self : CodepointSet, other : CodepointSet) -> Unit {
  let to_remove : Array[UInt] = []
  for value in self.set.iter() {
    if !other.set.contains(value) {
      to_remove.push(value)
    }
  }
  for value in to_remove {
    self.set.remove(value)
  }
}

///|
pub fn CodepointSet::subtract_in_place(self : CodepointSet, other : CodepointSet) -> Unit {
  for value in other.set.iter() {
    self.set.remove(value)
  }
}

///|
pub fn CodepointSet::symmetric_difference_in_place(
  self : CodepointSet,
  other : CodepointSet,
) -> Unit {
  let to_remove : Array[UInt] = []
  for value in other.set.iter() {
    if self.set.contains(value) {
      to_remove.push(value)
    } else {
      self.set.add(value)
    }
  }
  for value in to_remove {
    self.set.remove(value)
  }
}

///|
pub fn CodepointSet::intersect(self : CodepointSet, other : CodepointSet) -> CodepointSet {
  let out = CodepointSet::new()
  for value in self.set.iter() {
    if other.set.contains(value) {
      out.set.add(value)
    }
  }
  out
}

///|
pub fn CodepointSet::subtract(self : CodepointSet, other : CodepointSet) -> CodepointSet {
  let out = CodepointSet::new()
  for value in self.set.iter() {
    if !other.set.contains(value) {
      out.set.add(value)
    }
  }
  out
}

///|
pub fn CodepointSet::is_equal(self : CodepointSet, other : CodepointSet) -> Bool {
  self.set.equal(other.set)
}

///|
pub fn CodepointSet::is_subset(self : CodepointSet, other : CodepointSet) -> Bool {
  for value in self.set.iter() {
    if !other.set.contains(value) {
      return false
    }
  }
  true
}

///|
pub fn CodepointSet::may_intersect(self : CodepointSet, other : CodepointSet) -> Bool {
  for value in self.set.iter() {
    if other.set.contains(value) {
      return true
    }
  }
  false
}

///|
pub fn CodepointSet::intersects(self : CodepointSet, first : UInt, last : UInt) -> Bool {
  if first > last {
    return false
  }
  let values = sorted_values(self)
  for value in values {
    if value < first {
      continue
    }
    if value > last {
      break
    }
    return true
  }
  false
}

///|
pub fn CodepointSet::hash(self : CodepointSet) -> Int {
  let values = sorted_values(self)
  let hasher = Hasher::new(seed=0)
  for value in values {
    hasher.combine_uint(value)
  }
  hasher.finalize()
}

fn sorted_values(set : CodepointSet) -> Array[UInt] {
  set.to_sorted_array()
}

///|
/// Return the next value after `after` (exclusive). Use `codepoint_invalid` to start.
pub fn CodepointSet::next(self : CodepointSet, after? : UInt = codepoint_invalid) -> UInt? {
  let values = sorted_values(self)
  if values.is_empty() {
    return None
  }
  if after == codepoint_invalid {
    return Some(values[0])
  }
  for value in values {
    if value > after {
      return Some(value)
    }
  }
  None
}

///|
pub fn CodepointSet::next_many(
  self : CodepointSet,
  after? : UInt = codepoint_invalid,
  size? : Int = 0,
) -> Array[UInt] {
  if size <= 0 {
    return []
  }
  let values = sorted_values(self)
  if values.is_empty() {
    return []
  }
  let out : Array[UInt] = []
  let mut started = after == codepoint_invalid
  for value in values {
    if !started {
      if value > after {
        started = true
      } else {
        continue
      }
    }
    out.push(value)
    if out.length() >= size {
      break
    }
  }
  out
}

///|
/// Return the previous value before `before` (exclusive). Use `codepoint_invalid` to start.
pub fn CodepointSet::previous(self : CodepointSet, before? : UInt = codepoint_invalid) -> UInt? {
  let values = sorted_values(self)
  if values.is_empty() {
    return None
  }
  if before == codepoint_invalid {
    return Some(values[values.length() - 1])
  }
  for value in values.rev_iter() {
    if value < before {
      return Some(value)
    }
  }
  None
}

///|
/// Return the next contiguous range at or after `start`.
pub fn CodepointSet::next_range(
  self : CodepointSet,
  start? : UInt = codepoint_invalid,
) -> (UInt, UInt)? {
  let values = sorted_values(self)
  if values.is_empty() {
    return None
  }
  let mut idx = 0
  if start != codepoint_invalid {
    while idx < values.length() && values[idx] < start {
      idx = idx + 1
    }
  }
  if idx >= values.length() {
    return None
  }
  let first = values[idx]
  let mut last = first
  idx = idx + 1
  while idx < values.length() {
    let value = values[idx]
    if value == last + 1U {
      last = value
      idx = idx + 1
    } else {
      break
    }
  }
  Some((first, last))
}

///|
/// Return the previous contiguous range ending at or before `end`.
pub fn CodepointSet::previous_range(
  self : CodepointSet,
  end? : UInt = codepoint_invalid,
) -> (UInt, UInt)? {
  let values = sorted_values(self)
  if values.is_empty() {
    return None
  }
  let mut idx = values.length() - 1
  if end != codepoint_invalid {
    while idx >= 0 && values[idx] > end {
      if idx == 0 {
        return None
      }
      idx = idx - 1
    }
  }
  let last = values[idx]
  let mut first = last
  while idx > 0 {
    let prev = values[idx - 1]
    if prev + 1U == first {
      first = prev
      idx = idx - 1
    } else {
      break
    }
  }
  Some((first, last))
}

///|
pub struct CodepointMap {
  map : Map[UInt, UInt]
}

///|
pub fn CodepointMap::new() -> CodepointMap {
  CodepointMap::{ map: Map::new() }
}

///|
pub fn CodepointMap::clear(self : CodepointMap) -> Unit {
  self.map.clear()
}

///|
pub fn CodepointMap::is_empty(self : CodepointMap) -> Bool {
  self.map.is_empty()
}

///|
pub fn CodepointMap::population(self : CodepointMap) -> Int {
  self.map.length()
}

///|
pub fn CodepointMap::is_equal(self : CodepointMap, other : CodepointMap) -> Bool {
  self.map.equal(other.map)
}

///|
pub fn CodepointMap::hash(self : CodepointMap) -> Int {
  let entries = Array::from_iter(self.map.iter())
  entries.sort_by((a, b) => {
    let (ak, av) = a
    let (bk, bv) = b
    if ak < bk {
      -1
    } else if ak > bk {
      1
    } else if av < bv {
      -1
    } else if av > bv {
      1
    } else {
      0
    }
  })
  let hasher = Hasher::new(seed=0)
  for pair in entries {
    let (key, value) = pair
    hasher.combine_uint(key)
    hasher.combine_uint(value)
  }
  hasher.finalize()
}

///|
pub fn CodepointMap::copy(self : CodepointMap) -> CodepointMap {
  CodepointMap::{ map: self.map.copy() }
}

///|
pub fn CodepointMap::set(self : CodepointMap, key : UInt, value : UInt) -> Unit {
  if key == map_value_invalid {
    return ()
  }
  if value == map_value_invalid {
    self.map.remove(key)
  } else {
    self.map.set(key, value)
  }
}

///|
pub fn CodepointMap::get(self : CodepointMap, key : UInt) -> UInt {
  match self.map.get(key) {
    None => map_value_invalid
    Some(value) => value
  }
}

///|
pub fn CodepointMap::del(self : CodepointMap, key : UInt) -> Unit {
  self.map.remove(key)
}

///|
pub fn CodepointMap::has(self : CodepointMap, key : UInt) -> Bool {
  self.map.contains(key)
}

///|
pub fn CodepointMap::update(self : CodepointMap, other : CodepointMap) -> Unit {
  for key, value in other.map.iter2() {
    self.map.set(key, value)
  }
}

///|
pub fn CodepointMap::keys(self : CodepointMap) -> Array[UInt] {
  Array::from_iter(self.map.keys())
}

///|
pub fn CodepointMap::values(self : CodepointMap) -> Array[UInt] {
  Array::from_iter(self.map.values())
}

///|
pub fn CodepointMap::keys_set(self : CodepointMap) -> CodepointSet {
  let set = CodepointSet::new()
  for key in self.map.keys() {
    set.add(key)
  }
  set
}

///|
pub fn CodepointMap::values_set(self : CodepointMap) -> CodepointSet {
  let set = CodepointSet::new()
  for value in self.map.values() {
    set.add(value)
  }
  set
}

///|
pub fn CodepointMap::iter(self : CodepointMap) -> Iter[(UInt, UInt)] {
  self.map.iter()
}

///|
/// Iterate entries in a deterministic order (by key).
/// Returns (next_index, key, value).
pub fn CodepointMap::next(
  self : CodepointMap,
  idx? : Int = -1,
) -> (Int, UInt, UInt)? {
  let entries = Array::from_iter(self.map.iter())
  entries.sort_by((a, b) => {
    let (ak, av) = a
    let (bk, bv) = b
    if ak < bk {
      -1
    } else if ak > bk {
      1
    } else if av < bv {
      -1
    } else if av > bv {
      1
    } else {
      0
    }
  })
  let start = if idx < -1 { -1 } else { idx }
  let next_idx = start + 1
  if next_idx < 0 || next_idx >= entries.length() {
    None
  } else {
    let (key, value) = entries[next_idx]
    Some((next_idx, key, value))
  }
}

///|
pub struct CodepointBiMap {
  forward : CodepointMap
  backward : CodepointMap
}

///|
pub fn CodepointBiMap::new() -> CodepointBiMap {
  CodepointBiMap::{ forward: CodepointMap::new(), backward: CodepointMap::new() }
}

///|
pub fn CodepointBiMap::clear(self : CodepointBiMap) -> Unit {
  self.forward.clear()
  self.backward.clear()
}

///|
pub fn CodepointBiMap::is_empty(self : CodepointBiMap) -> Bool {
  self.forward.is_empty()
}

///|
pub fn CodepointBiMap::population(self : CodepointBiMap) -> Int {
  self.forward.population()
}

///|
pub fn CodepointBiMap::set(self : CodepointBiMap, lhs : UInt, rhs : UInt) -> Unit {
  if lhs == map_value_invalid {
    return ()
  }
  if rhs == map_value_invalid {
    self.del(lhs)
    return ()
  }
  self.forward.set(lhs, rhs)
  self.backward.set(rhs, lhs)
}

///|
pub fn CodepointBiMap::get(self : CodepointBiMap, lhs : UInt) -> UInt {
  self.forward.get(lhs)
}

///|
pub fn CodepointBiMap::backward(self : CodepointBiMap, rhs : UInt) -> UInt {
  self.backward.get(rhs)
}

///|
pub fn CodepointBiMap::has(self : CodepointBiMap, lhs : UInt) -> Bool {
  self.forward.has(lhs)
}

///|
pub fn CodepointBiMap::del(self : CodepointBiMap, lhs : UInt) -> Unit {
  let rhs = self.forward.get(lhs)
  if rhs != map_value_invalid {
    self.backward.del(rhs)
    self.forward.del(lhs)
  }
}

///|
pub fn CodepointBiMap::keys(self : CodepointBiMap) -> Array[UInt] {
  self.forward.keys()
}

///|
pub fn CodepointBiMap::values(self : CodepointBiMap) -> Array[UInt] {
  self.forward.values()
}

///|
pub struct CodepointIncBiMap {
  forward : CodepointMap
  back : Array[UInt]
}

///|
pub fn CodepointIncBiMap::new() -> CodepointIncBiMap {
  CodepointIncBiMap::{ forward: CodepointMap::new(), back: [] }
}

///|
pub fn CodepointIncBiMap::clear(self : CodepointIncBiMap) -> Unit {
  self.forward.clear()
  self.back.clear()
}

///|
pub fn CodepointIncBiMap::population(self : CodepointIncBiMap) -> Int {
  self.forward.population()
}

///|
pub fn CodepointIncBiMap::get_next_value(self : CodepointIncBiMap) -> UInt {
  self.back.length().reinterpret_as_uint()
}

///|
pub fn CodepointIncBiMap::add(self : CodepointIncBiMap, lhs : UInt) -> UInt {
  let existing = self.forward.get(lhs)
  if existing != map_value_invalid {
    return existing
  }
  let rhs = self.back.length().reinterpret_as_uint()
  self.forward.set(lhs, rhs)
  self.back.push(lhs)
  rhs
}

///|
pub fn CodepointIncBiMap::skip(self : CodepointIncBiMap, count? : Int = 1) -> UInt {
  let start = self.back.length().reinterpret_as_uint()
  if count <= 0 {
    return start
  }
  for _ in 0.. UInt {
  self.forward.get(lhs)
}

///|
pub fn CodepointIncBiMap::backward(self : CodepointIncBiMap, rhs : UInt) -> UInt {
  let idx = rhs.reinterpret_as_int()
  if idx < 0 || idx >= self.back.length() {
    map_value_invalid
  } else {
    self.back[idx]
  }
}

///|
pub fn CodepointIncBiMap::has(self : CodepointIncBiMap, lhs : UInt) -> Bool {
  self.forward.has(lhs)
}

///|
pub fn CodepointIncBiMap::add_set(self : CodepointIncBiMap, set : CodepointSet) -> Unit {
  for value in set.to_sorted_array() {
    ignore(self.add(value))
  }
}

///|
pub fn CodepointIncBiMap::identity(self : CodepointIncBiMap, size : Int) -> Bool {
  self.clear()
  if size < 0 {
    return false
  }
  for i in 0.. Unit {
  let values = self.back.copy()
  values.sort()
  self.clear()
  for value in values {
    if value != map_value_invalid {
      ignore(self.add(value))
    } else {
      ignore(self.skip())
    }
  }
}