///|
fn swap(s : Array[Int], i : Int, j : Int) -> Unit {
  let tmp = s[i]
  s[i] = s[j]
  s[j] = tmp
}

///|
fn ksa(s : Array[Int], key : Array[Int]) -> Unit {
  let keylength = key.length()
  for i in 0..<256 {
    s[i] = i
  }
  let mut j = 0
  for i in 0..<256 {
    j = (j + s[i] + key[i % keylength]) % 256
    swap(s, i, j)
  }
}

///|
fn prga(s : Array[Int], pi : Ref[Int], pj : Ref[Int]) -> Int {
  pi.val = (pi.val + 1) % 256
  pj.val = (pj.val + s[pi.val]) % 256
  swap(s, pi.val, pj.val)
  s[(s[pi.val] + s[pj.val]) % 256]
}

///|
/// ARC4 encryption/decryption given a key and some data.
/// The same function performs encryption and decryption.
pub fn PdfCryptPrimitives::crypt(
  _self : PdfCryptPrimitives,
  key : Array[Int],
  data : @pdfio.MutableBytes,
) -> @pdfio.MutableBytes {
  if key.length() == 0 {
    return @pdfio.copybytes(data)
  }
  let s = Array::make(256, 0)
  let pi : Ref[Int] = Ref::new(0)
  let pj : Ref[Int] = Ref::new(0)
  let out = @pdfio.mkbytes(@pdfio.bytes_size(data))
  ksa(s, key)
  let mut x = 0
  while x < @pdfio.bytes_size(data) {
    let v = @pdfio.bget(data, x)
    let k = prga(s, pi, pj)
    @pdfio.bset(out, x, v ^ k)
    x = x + 1
  }
  out
}