// 自写 base64(RFC 4648 标准字母表,带 `=` 补位)。
//
// 为什么不复用 `moonbitlang/core/encoding/base64`:
// "什么算规范形式必须由本项目定义,否则会随标准库实现漂移"
// (见计划 §3.2 / §5.5,以及上一项目 payload/base64.mbt 的结论)。
// 词表文本是要被 fingerprint、被 diff 的,编码形式不能依赖外部实现。

///|
let b64_alphabet : Array[Char] = [
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
]

///|
fn b64_char(idx : Int) -> Char {
  b64_alphabet[idx & 0x3F]
}

///|
/// 把 base64 字符映射回 6 位值;非法字符返回 `-1`。
fn b64_value(c : Char) -> Int {
  let v = c.to_int()
  if v >= 0x41 && v <= 0x5A {
    v - 0x41
  } else if v >= 0x61 && v <= 0x7A {
    v - 0x61 + 26
  } else if v >= 0x30 && v <= 0x39 {
    v - 0x30 + 52
  } else if v == 0x2B {
    62
  } else if v == 0x2F {
    63
  } else {
    -1
  }
}

///|
/// 标准 base64 编码(带 `=` 补位,规范形式)。
pub fn b64_encode(bytes : Bytes) -> String {
  let n = bytes.length()
  let out : Array[Char] = []
  let mut i = 0
  while i + 3 <= n {
    let b0 = bytes[i].to_int() & 0xFF
    let b1 = bytes[i + 1].to_int() & 0xFF
    let b2 = bytes[i + 2].to_int() & 0xFF
    out.push(b64_char(b0 >> 2))
    out.push(b64_char(((b0 & 0x03) << 4) | (b1 >> 4)))
    out.push(b64_char(((b1 & 0x0F) << 2) | (b2 >> 6)))
    out.push(b64_char(b2 & 0x3F))
    i = i + 3
  }
  let rem = n - i
  if rem == 1 {
    let b0 = bytes[i].to_int() & 0xFF
    out.push(b64_char(b0 >> 2))
    out.push(b64_char((b0 & 0x03) << 4))
    out.push('=')
    out.push('=')
  } else if rem == 2 {
    let b0 = bytes[i].to_int() & 0xFF
    let b1 = bytes[i + 1].to_int() & 0xFF
    out.push(b64_char(b0 >> 2))
    out.push(b64_char(((b0 & 0x03) << 4) | (b1 >> 4)))
    out.push(b64_char((b1 & 0x0F) << 2))
    out.push('=')
  }
  String::from_array(out)
}

///|
/// 标准 base64 解码。`=` 及其后的内容被忽略;非法字符报错。
pub fn b64_decode(text : StringView) -> Bytes raise VocabError {
  let chars = text.to_array()
  let out : Array[Byte] = []
  let mut acc = 0
  let mut bits = 0
  for c in chars {
    if c == '=' {
      break
    }
    let v = b64_value(c)
    if v < 0 {
      raise InvalidBase64(c)
    }
    acc = ((acc << 6) | v) & 0xFFFFFF
    bits = bits + 6
    if bits >= 8 {
      bits = bits - 8
      out.push(((acc >> bits) & 0xFF).to_byte())
      acc = acc & ((1 << bits) - 1)
    }
  }
  Bytes::from_array(out)
}