// 字节序列基础操作。
//
// 本模块刻意不依赖 `moonbitlang/core` 的 `encoding/utf8`:预切分需要的是
// "码点 + 原始字节跨度" 的流,而且必须对**非法 UTF-8 也保持字节保真**
// (不变量 V6:`decode(encode(b)) == b` 对任意 `Bytes` 成立)。
// 官方解码器遇到非法字节会替换成 U+FFFD,这会丢掉原始字节。

///|
/// 输入流中的一个码点单元。
///
/// * `cp`    —— 码点值;若 `valid` 为 `false`,则它是原始字节值(0…255)。
/// * `start` —— 在原始 `Bytes` 中的起始字节偏移。
/// * `size`  —— 占用的字节数(非法字节恒为 1)。
/// * `valid` —— 是否是合法 UTF-8 码点。
///
/// 非法字节在分类时一律按 "既非字母、也非数字、也非空白" 处理,
/// 从而落进预切分模式的 `[^\s\p{L}\p{N}]` 分支,字节原样保留。
pub(all) struct CodePoint {
  cp : UInt
  start : Int
  size : Int
  valid : Bool
} derive(Debug)

///|
fn is_cont(b : Int) -> Bool {
  b >= 0x80 && b <= 0xBF
}

///|
/// 宽松 UTF-8 解码:合法序列解出一个码点,非法字节各自成为一个单元。
///
/// 合法性判定与 Unicode 表 3-7 一致(拒绝过长编码、代理区、> U+10FFFF)。
pub fn decode_utf8_lenient(bytes : Bytes) -> Array[CodePoint] {
  let out : Array[CodePoint] = []
  let n = bytes.length()
  let mut i = 0
  while i < n {
    let b0 = bytes[i].to_int() & 0xFF
    if b0 < 0x80 {
      out.push({ cp: b0.reinterpret_as_uint(), start: i, size: 1, valid: true })
      i = i + 1
      continue
    }
    // 2 字节:C2..DF
    if b0 >= 0xC2 && b0 <= 0xDF && i + 1 < n {
      let b1 = bytes[i + 1].to_int() & 0xFF
      if is_cont(b1) {
        let cp = ((b0 & 0x1F) << 6) | (b1 & 0x3F)
        out.push({ cp: cp.reinterpret_as_uint(), start: i, size: 2, valid: true })
        i = i + 2
        continue
      }
    }
    // 3 字节:E0..EF
    if b0 >= 0xE0 && b0 <= 0xEF && i + 2 < n {
      let b1 = bytes[i + 1].to_int() & 0xFF
      let b2 = bytes[i + 2].to_int() & 0xFF
      let b1_ok = if b0 == 0xE0 {
        b1 >= 0xA0 && b1 <= 0xBF
      } else if b0 == 0xED {
        b1 >= 0x80 && b1 <= 0x9F
      } else {
        is_cont(b1)
      }
      if b1_ok && is_cont(b2) {
        let cp = ((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F)
        out.push({ cp: cp.reinterpret_as_uint(), start: i, size: 3, valid: true })
        i = i + 3
        continue
      }
    }
    // 4 字节:F0..F4
    if b0 >= 0xF0 && b0 <= 0xF4 && i + 3 < n {
      let b1 = bytes[i + 1].to_int() & 0xFF
      let b2 = bytes[i + 2].to_int() & 0xFF
      let b3 = bytes[i + 3].to_int() & 0xFF
      let b1_ok = if b0 == 0xF0 {
        b1 >= 0x90 && b1 <= 0xBF
      } else if b0 == 0xF4 {
        b1 >= 0x80 && b1 <= 0x8F
      } else {
        is_cont(b1)
      }
      if b1_ok && is_cont(b2) && is_cont(b3) {
        let cp = ((b0 & 0x07) << 18) |
          ((b1 & 0x3F) << 12) |
          ((b2 & 0x3F) << 6) |
          (b3 & 0x3F)
        out.push({ cp: cp.reinterpret_as_uint(), start: i, size: 4, valid: true })
        i = i + 4
        continue
      }
    }
    // 非法字节:原样作为一个单元
    out.push({ cp: b0.reinterpret_as_uint(), start: i, size: 1, valid: false })
    i = i + 1
  }
  out
}

///|
/// 拼接两个字节序列。
///
/// 注:本版本 `Bytes` 没有 `op_set`,写入一律经 `Array[Byte]` 中转
/// (`Bytes::from_array`)。这是工具链层面的约束,不是设计选择。
pub fn concat_bytes(a : Bytes, b : Bytes) -> Bytes {
  let la = a.length()
  let lb = b.length()
  if la == 0 {
    return b
  }
  if lb == 0 {
    return a
  }
  let out : Array[Byte] = Array::make(la + lb, (0).to_byte())
  for i = 0; i < la; i = i + 1 {
    out[i] = a.unsafe_get(i)
  }
  for i = 0; i < lb; i = i + 1 {
    out[la + i] = b.unsafe_get(i)
  }
  Bytes::from_array(out)
}

///|
/// 取 `bytes[from, to)` 的副本。
pub fn slice_bytes(bytes : Bytes, from : Int, to : Int) -> Bytes {
  let len = to - from
  let out : Array[Byte] = Array::make(len, (0).to_byte())
  for i = 0; i < len; i = i + 1 {
    out[i] = bytes.unsafe_get(from + i)
  }
  Bytes::from_array(out)
}