// Hexadecimal helpers.
//
// The fingerprints of the package are hex, and the keys of the encryption are
// stored as hex in the manifest and in the key files.

///|
/// The hex digit of a nibble.
fn hex_char_of(n : Int32) -> Char {
  match n {
    0 => '0'
    1 => '1'
    2 => '2'
    3 => '3'
    4 => '4'
    5 => '5'
    6 => '6'
    7 => '7'
    8 => '8'
    9 => '9'
    10 => 'a'
    11 => 'b'
    12 => 'c'
    13 => 'd'
    14 => 'e'
    15 => 'f'
    _ => '0'
  }
}

///|
/// Lowercase hex of `data`.
pub fn bytes_to_hex(data : Bytes) -> String {
  let mut out = ""
  let mut i = 0
  let n = data.length()
  while i < n {
    let b = data.at(i).to_int()
    out = out +
      String::make(1, hex_char_of(b >> 4)) +
      String::make(1, hex_char_of(b & 0x0f))
    i = i + 1
  }
  out
}

///|
/// The value of a hex digit, or -1 when it is not one.
///
/// A `String` yields a code unit when it is indexed, and the hex digits are
/// all in the ASCII range, so the code unit is compared by its number.
fn hex_value(code : Int32) -> Int32 {
  match code {
    48 => 0
    49 => 1
    50 => 2
    51 => 3
    52 => 4
    53 => 5
    54 => 6
    55 => 7
    56 => 8
    57 => 9
    97 => 10
    98 => 11
    99 => 12
    100 => 13
    101 => 14
    102 => 15
    65 => 10
    66 => 11
    67 => 12
    68 => 13
    69 => 14
    70 => 15
    _ => -1
  }
}

///|
/// Decode a hex string.
///
/// Returns None when the text is not a hex string of even length.
pub fn hex_to_bytes(text : String) -> Bytes? {
  let n = text.length()
  if n % 2 != 0 {
    return None
  }
  let out = FixedArray::make(n / 2, b'\x00')
  let mut i = 0
  while i < n / 2 {
    let hi = hex_value(text[i * 2].to_int())
    let lo = hex_value(text[i * 2 + 1].to_int())
    if hi < 0 || lo < 0 {
      return None
    }
    out[i] = ((hi << 4) | lo).to_byte()
    i = i + 1
  }
  Some(Bytes::from_array(out[:]))
}

///|
/// The bytes of two buffers, one after the other.
pub fn bytes_concat(a : Bytes, b : Bytes) -> Bytes {
  let out = FixedArray::make(a.length() + b.length(), b'\x00')
  let mut i = 0
  while i < a.length() {
    out[i] = a.at(i)
    i = i + 1
  }
  let mut j = 0
  while j < b.length() {
    out[a.length() + j] = b.at(j)
    j = j + 1
  }
  Bytes::from_array(out[:])
}

///|
/// The bytes of `count` zeros.
pub fn zero_bytes(count : Int32) -> Bytes {
  let out = FixedArray::make(count, b'\x00')
  Bytes::from_array(out[:])
}