///|
/// Encode Unicode text as UTF-16LE code units without a terminator.
pub fn utf16_units(text : String) -> Array[Int] {
  let units : Array[Int] = []
  for i = 0; i < text.length(); i = i + 1 {
    units.push(text[i].to_int())
  }
  units
}

///|
fn put_utf16(buf : Array[Byte], text : String) -> Unit {
  let units = utf16_units(text)
  for i = 0; i < units.length(); i = i + 1 {
    put_u16(buf, units[i])
  }
}

///|
fn utf16_byte_length(text : String) -> Int {
  utf16_units(text).length() * 2
}

///|
fn read_utf16_units(
  data : Bytes,
  pos : Int,
  units : Int,
) -> Result[String, EvtxError] {
  let _ = match need(data, pos, units * 2, "utf16") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let out = StringBuilder()
  let mut i = 0
  while i < units {
    let (unit, _) = match read_u16(data, pos + i * 2) {
      Ok(v) => v
      Err(e) => return Err(e)
    }
    if unit >= 0xD800 && unit <= 0xDBFF && i + 1 < units {
      let (low, _) = match read_u16(data, pos + (i + 1) * 2) {
        Ok(v) => v
        Err(e) => return Err(e)
      }
      if low >= 0xDC00 && low <= 0xDFFF {
        let cp = 0x10000 + ((unit - 0xD800) << 10) + (low - 0xDC00)
        out.write_char(cp.unsafe_to_char())
        i = i + 2
      } else {
        out.write_char(unit.unsafe_to_char())
        i = i + 1
      }
    } else {
      out.write_char(unit.unsafe_to_char())
      i = i + 1
    }
  }
  Ok(out.to_string())
}

///|
fn read_utf16_bytes(
  data : Bytes,
  pos : Int,
  nbytes : Int,
) -> Result[String, EvtxError] {
  if nbytes < 0 || nbytes % 2 != 0 {
    return Err(InvalidSubstitution("UTF-16 payload must be even"))
  }
  read_utf16_units(data, pos, nbytes / 2)
}

///|
fn rstrip_utf16_nuls(text : String) -> String {
  let mut end = text.length()
  while end > 0 && text[end - 1].to_int() == 0 {
    end = end - 1
  }
  if end == text.length() {
    text
  } else {
    let out = StringBuilder()
    for i = 0; i < end; i = i + 1 {
      out.write_char(text[i].to_int().unsafe_to_char())
    }
    out.to_string()
  }
}

///|
pub fn bytes_from_ascii(text : String) -> Bytes {
  Bytes::makei(text.length(), i => text[i].to_int().to_byte())
}

///|
fn read_i8_at(data : Bytes, pos : Int) -> Result[Int, EvtxError] {
  let _ = match need(data, pos, 1, "i8") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let v = data[pos].to_int()
  if v >= 128 {
    Ok(v - 256)
  } else {
    Ok(v)
  }
}

///|
fn read_i16_at(data : Bytes, pos : Int) -> Result[Int, EvtxError] {
  let (v, _) = match read_u16(data, pos) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  if v >= 32768 {
    Ok(v - 65536)
  } else {
    Ok(v)
  }
}

///|
fn read_i32_at(data : Bytes, pos : Int) -> Result[Int, EvtxError] {
  let (v, _) = match read_i32(data, pos) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  Ok(v)
}

///|
fn read_i64_at(data : Bytes, pos : Int) -> Result[Int64, EvtxError] {
  let (v, _) = match read_i64(data, pos) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  Ok(v)
}