///|
pub let bytes_methods : Map[String, RuntimeFunction] = {
  "length": bytes_length_fn,
  "at": bytes_at_fn,
  "compare": bytes_compare_fn,
  "equal": bytes_equal_fn,
  "make": bytes_make_fn,
  "makei": bytes_makei_fn,
  "new": bytes_new_fn,
  "op_equal": bytes_op_equal_fn,
  "to_unchecked_string": bytes_to_unchecked_string_fn,
  "unsafe_get": bytes_unsafe_get_fn,
}

///|
let bytes_at_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Bytes(b), .. }, { val: Int(i), .. }] => Byte(b[i])
  _ => Unit
}

///|
let bytes_compare_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Bytes(a), .. }, { val: Bytes(b), .. }] => Int(a.compare(b))
  _ => Unit
}

///|
let bytes_equal_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Bytes(a), .. }, { val: Bytes(b), .. }] => Bool(a == b)
  _ => Unit
}

///|
let bytes_make_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Int(len), .. }, { val: Byte(byte), .. }] =>
    Bytes(Bytes::make(len, byte))
  _ => Unit
}

///|
let bytes_makei_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Int(len), .. }, { val: Fn(f), .. }] => {
    let result = Bytes::makei(len, fn(i) {
      let arg = Int(i)
      let ret = ctx.context.call(f.val, ctx.pkg, [
        { val: arg, kind: Positional },
      ])
      match ret {
        Byte(b) => b
        _ => 0
      }
    })
    Bytes(result)
  }
  _ => Unit
}

///|
let bytes_new_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Int(len), .. }] => Bytes(Bytes::new(len))
  _ => Unit
}

///|
let bytes_op_equal_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Bytes(a), .. }, { val: Bytes(b), .. }] => Bool(a == b)
  _ => Unit
}

///|
let bytes_to_unchecked_string_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Bytes(b), .. }] => {
    let offset = if ctx.named("offset") is Some(Int(i)) {
      Some(i)
    } else {
      None
    }
    let length = if ctx.named("length") is Some(Int(i)) {
      Some(i)
    } else {
      None
    }
    String(b.to_unchecked_string(offset?, length?))
  }
  _ => Unit
}

///|
let bytes_unsafe_get_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Bytes(b), .. }, { val: Int(i), .. }] => Byte(b.unsafe_get(i))
  _ => Unit
}

///|
let bytes_length_fn : RuntimeFunction = ctx => match ctx.args {
  [{ val: Bytes(b), .. }] => Int(b.length())
  _ => Unit
}