// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// An ordered set of digit characters defining a positional numeral system.
/// The character at index `i` is the digit of value `i`, so the first
/// character is always the zero digit. Build one with [`Alphabet::new`] and
/// hand it to [`encode`] / [`decode`]; the `base58` and `base62` packages are
/// thin wrappers that pin a specific alphabet.
pub struct Alphabet {
  digits : Array[Char]
  lookup : Array[Int]
  base : Int
}

///|
/// Build an [`Alphabet`] from its ordered digit characters, e.g.
/// `Alphabet::new("0123456789abcdef")` for lowercase hexadecimal. Aborts when
/// a character repeats or is not ASCII, since either makes the mapping
/// ambiguous.
pub fn Alphabet::new(chars : String) -> Alphabet {
  let digits = chars.to_array()
  let base = digits.length()
  let lookup = Array::make(128, -1)
  for i in 0..= 128 {
      abort("basex: alphabet characters must be ASCII")
    }
    if lookup[code] != -1 {
      abort("basex: duplicate alphabet character")
    }
    lookup[code] = i
  }
  { digits, lookup, base }
}

///|
/// The radix of this alphabet — the number of distinct digits.
pub fn Alphabet::base(self : Alphabet) -> Int {
  self.base
}

///|
/// The character for digit value `v`, where `0 <= v < base`.
pub fn Alphabet::char_of(self : Alphabet, v : Int) -> Char {
  self.digits[v]
}

///|
/// The digit value of `c` in this alphabet, or `None` if `c` is not one of its
/// characters.
pub fn Alphabet::value_of(self : Alphabet, c : Char) -> Int? {
  let code = c.to_int()
  if code < 0 || code >= 128 {
    return None
  }
  let v = self.lookup[code]
  if v < 0 {
    None
  } else {
    Some(v)
  }
}

///|
/// Encode raw bytes to a string over `alphabet`. Leading zero bytes map to
/// leading zero-digit characters; the rest is read as a big-endian integer and
/// rewritten in the target base. Round-trips with [`decode`].
pub fn encode(input : Bytes, alphabet : Alphabet) -> String {
  let n = input.length()
  let base = alphabet.base
  let mut zeros = 0
  while zeros < n && input[zeros].to_int() == 0 {
    zeros = zeros + 1
  }
  // Little-endian digits of the big integer; leading (most significant) zeros
  // are never stored, so an all-zero payload leaves this empty.
  let out_digits : Array[Int] = Array::new()
  for i in 0.. 0 {
      out_digits.push(carry % base)
      carry = carry / base
    }
  }
  let chars : Array[Char] = Array::new()
  for _ in 0..= 0 {
    chars.push(alphabet.digits[out_digits[k]])
    k = k - 1
  }
  String::from_array(chars)
}

///|
/// Decode a string over `alphabet` back to the original bytes. Returns `None`
/// if `input` holds any character outside `alphabet`. Round-trips with
/// [`encode`].
pub fn decode(input : String, alphabet : Alphabet) -> Bytes? {
  let chars = input.to_array()
  let n = chars.length()
  let base = alphabet.base
  let zero_char = alphabet.digits[0]
  let mut zeros = 0
  while zeros < n && chars[zeros] == zero_char {
    zeros = zeros + 1
  }
  let out_bytes : Array[Int] = Array::new()
  for i in 0..= 128 {
      return None
    }
    let d = alphabet.lookup[code]
    if d < 0 {
      return None
    }
    let mut carry = d
    for j in 0.. 0 {
      out_bytes.push(carry % 256)
      carry = carry / 256
    }
  }
  let bytes : Array[Byte] = Array::new()
  for _ in 0..= 0 {
    bytes.push(out_bytes[k].to_byte())
    k = k - 1
  }
  Some(Bytes::from_array(bytes))
}