/// WASI state and symbol access for managed native JIT contexts.

///|
fn take_wasi_exit_code(ctx_ptr : Int64) -> Int {
  let raw_code = c_jit_get_wasi_exit_code(ctx_ptr)
  c_jit_clear_wasi_exit(ctx_ptr)
  if raw_code < 0 {
    0
  } else {
    raw_code
  }
}

///|
pub fn make_cstring(s : String) -> FixedArray[Byte] {
  let len = s.length()
  let mut size = 0
  let mut i = 0
  while i < len {
    let c = s.code_unit_at(i).to_int()
    if c < 0x80 {
      size += 1
      i += 1
    } else if c < 0x800 {
      size += 2
      i += 1
    } else if c >= 0xD800 && c <= 0xDBFF && i + 1 < len {
      let c2 = s.code_unit_at(i + 1).to_int()
      if c2 >= 0xDC00 && c2 <= 0xDFFF {
        size += 4
        i += 2
      } else {
        size += 3
        i += 1
      }
    } else {
      size += 3
      i += 1
    }
  }
  let result = FixedArray::make(size + 1, b'\x00')
  let mut pos = 0
  i = 0
  while i < len {
    let c = s.code_unit_at(i).to_int()
    if c < 0x80 {
      result[pos] = c.to_byte()
      pos += 1
      i += 1
    } else if c < 0x800 {
      result[pos] = (0xC0 | (c >> 6)).to_byte()
      result[pos + 1] = (0x80 | (c & 0x3F)).to_byte()
      pos += 2
      i += 1
    } else if c >= 0xD800 && c <= 0xDBFF && i + 1 < len {
      let c2 = s.code_unit_at(i + 1).to_int()
      if c2 >= 0xDC00 && c2 <= 0xDFFF {
        let codepoint = 0x10000 + ((c - 0xD800) << 10) + (c2 - 0xDC00)
        result[pos] = (0xF0 | (codepoint >> 18)).to_byte()
        result[pos + 1] = (0x80 | ((codepoint >> 12) & 0x3F)).to_byte()
        result[pos + 2] = (0x80 | ((codepoint >> 6) & 0x3F)).to_byte()
        result[pos + 3] = (0x80 | (codepoint & 0x3F)).to_byte()
        pos += 4
        i += 2
      } else {
        result[pos] = b'\xEF'
        result[pos + 1] = b'\xBF'
        result[pos + 2] = b'\xBD'
        pos += 3
        i += 1
      }
    } else {
      result[pos] = (0xE0 | (c >> 12)).to_byte()
      result[pos + 1] = (0x80 | ((c >> 6) & 0x3F)).to_byte()
      result[pos + 2] = (0x80 | (c & 0x3F)).to_byte()
      pos += 3
      i += 1
    }
  }
  result[size] = b'\x00'
  result
}

///|
fn set_wasi_stdout_capture(ctx_ptr : Int64, enabled : Bool) -> Unit {
  c_jit_set_wasi_stdout_capture(ctx_ptr, if enabled { 1 } else { 0 })
}

///|
fn set_wasi_stderr_capture(ctx_ptr : Int64, enabled : Bool) -> Unit {
  c_jit_set_wasi_stderr_capture(ctx_ptr, if enabled { 1 } else { 0 })
}

///|
fn set_wasi_stdin_buffer(ctx_ptr : Int64, data : Bytes) -> Unit {
  c_jit_set_wasi_stdin_buffer(ctx_ptr, data, data.length())
}

///|
fn set_wasi_stdin_callback(ctx_ptr : Int64, callback : () -> Bytes) -> Unit {
  let call_closure : FuncRef[(() -> Bytes) -> Bytes] = fn(f : () -> Bytes) {
    f()
  }
  c_jit_set_wasi_stdin_callback(ctx_ptr, call_closure, callback)
}

///|
fn clear_wasi_stdin_callback(ctx_ptr : Int64) -> Unit {
  c_jit_clear_wasi_stdin_callback(ctx_ptr)
}

///|
fn clear_wasi_stdin_buffer(ctx_ptr : Int64) -> Unit {
  c_jit_clear_wasi_stdin_buffer(ctx_ptr)
}

///|
fn take_wasi_stdout(ctx_ptr : Int64) -> Bytes {
  c_jit_take_wasi_stdout(ctx_ptr)
}

///|
fn take_wasi_stderr(ctx_ptr : Int64) -> Bytes {
  c_jit_take_wasi_stderr(ctx_ptr)
}

///|
fn init_wasi_context(
  ctx_ptr : Int64,
  args : Array[String],
  env : Array[String],
  preopens : Array[(String, String)],
  quiet? : Bool = false,
) -> Unit {
  if quiet {
    c_jit_init_wasi_fds_quiet(ctx_ptr, preopens.length())
  } else {
    c_jit_init_wasi_fds(ctx_ptr, preopens.length())
  }
  set_wasi_stdout_capture(ctx_ptr, false)
  set_wasi_stderr_capture(ctx_ptr, false)
  clear_wasi_stdin_callback(ctx_ptr)
  clear_wasi_stdin_buffer(ctx_ptr)
  c_jit_set_wasi_args(ctx_ptr, args.length())
  for i, arg in args {
    c_jit_set_wasi_arg(ctx_ptr, i, make_cstring(arg))
  }
  c_jit_set_wasi_envs(ctx_ptr, env.length())
  for i, e in env {
    c_jit_set_wasi_env(ctx_ptr, i, make_cstring(e))
  }
  for i, entry in preopens {
    let (host, guest) = entry
    c_jit_add_preopen(ctx_ptr, i, make_cstring(host), make_cstring(guest))
  }
}

///|
pub fn NativeJITContext::init_wasi(
  self : NativeJITContext,
  args : Array[String],
  env : Array[String],
  preopens : Array[(String, String)],
  quiet? : Bool = false,
) -> Unit {
  self.with_ptr(fn(ptr) { init_wasi_context(ptr, args, env, preopens, quiet~) })
}

///|
pub fn NativeJITContext::set_wasi_stdout_capture(
  self : NativeJITContext,
  enabled : Bool,
) -> Unit {
  self.with_ptr(fn(ptr) { set_wasi_stdout_capture(ptr, enabled) })
}

///|
pub fn NativeJITContext::set_wasi_stderr_capture(
  self : NativeJITContext,
  enabled : Bool,
) -> Unit {
  self.with_ptr(fn(ptr) { set_wasi_stderr_capture(ptr, enabled) })
}

///|
pub fn NativeJITContext::set_wasi_stdin_buffer(
  self : NativeJITContext,
  data : Bytes,
) -> Unit {
  self.with_ptr(fn(ptr) { set_wasi_stdin_buffer(ptr, data) })
}

///|
pub fn NativeJITContext::set_wasi_stdin_callback(
  self : NativeJITContext,
  callback : () -> Bytes,
) -> Unit {
  self.with_ptr(fn(ptr) { set_wasi_stdin_callback(ptr, callback) })
}

///|
pub fn NativeJITContext::clear_wasi_stdin_callback(
  self : NativeJITContext,
) -> Unit {
  self.with_ptr(fn(ptr) { clear_wasi_stdin_callback(ptr) })
}

///|
pub fn NativeJITContext::clear_wasi_stdin_buffer(
  self : NativeJITContext,
) -> Unit {
  self.with_ptr(fn(ptr) { clear_wasi_stdin_buffer(ptr) })
}

///|
pub fn NativeJITContext::take_wasi_stdout(self : NativeJITContext) -> Bytes {
  self.with_ptr(fn(ptr) { take_wasi_stdout(ptr) })
}

///|
pub fn NativeJITContext::take_wasi_stderr(self : NativeJITContext) -> Bytes {
  self.with_ptr(fn(ptr) { take_wasi_stderr(ptr) })
}

///|
pub fn NativeJITContext::take_wasi_exit_code(self : NativeJITContext) -> Int {
  self.with_ptr(fn(ptr) { take_wasi_exit_code(ptr) })
}

///|
fn get_fd_write_ptr() -> Int64 {
  c_jit_get_fd_write_ptr()
}

///|
fn get_proc_exit_ptr() -> Int64 {
  c_jit_get_proc_exit_ptr()
}

///|
fn get_fd_read_ptr() -> Int64 {
  c_jit_get_fd_read_ptr()
}

///|
fn get_args_sizes_get_ptr() -> Int64 {
  c_jit_get_args_sizes_get_ptr()
}

///|
fn get_args_get_ptr() -> Int64 {
  c_jit_get_args_get_ptr()
}

///|
fn get_environ_sizes_get_ptr() -> Int64 {
  c_jit_get_environ_sizes_get_ptr()
}

///|
fn get_environ_get_ptr() -> Int64 {
  c_jit_get_environ_get_ptr()
}

///|
fn get_clock_time_get_ptr() -> Int64 {
  c_jit_get_clock_time_get_ptr()
}

///|
fn get_random_get_ptr() -> Int64 {
  c_jit_get_random_get_ptr()
}

///|
fn get_fd_close_ptr() -> Int64 {
  c_jit_get_fd_close_ptr()
}

///|
fn get_fd_fdstat_get_ptr() -> Int64 {
  c_jit_get_fd_fdstat_get_ptr()
}

///|
fn get_fd_prestat_get_ptr() -> Int64 {
  c_jit_get_fd_prestat_get_ptr()
}

///|
fn get_fd_prestat_dir_name_ptr() -> Int64 {
  c_jit_get_fd_prestat_dir_name_ptr()
}

///|
fn get_fd_seek_ptr() -> Int64 {
  c_jit_get_fd_seek_ptr()
}

///|
fn get_fd_tell_ptr() -> Int64 {
  c_jit_get_fd_tell_ptr()
}

///|
fn get_fd_sync_ptr() -> Int64 {
  c_jit_get_fd_sync_ptr()
}

///|
fn get_fd_datasync_ptr() -> Int64 {
  c_jit_get_fd_datasync_ptr()
}

///|
fn get_fd_filestat_get_ptr() -> Int64 {
  c_jit_get_fd_filestat_get_ptr()
}

///|
fn get_fd_filestat_set_size_ptr() -> Int64 {
  c_jit_get_fd_filestat_set_size_ptr()
}

///|
fn get_path_open_ptr() -> Int64 {
  c_jit_get_path_open_ptr()
}

///|
fn get_path_create_directory_ptr() -> Int64 {
  c_jit_get_path_create_directory_ptr()
}

///|
fn get_path_unlink_file_ptr() -> Int64 {
  c_jit_get_path_unlink_file_ptr()
}

///|
fn get_path_remove_directory_ptr() -> Int64 {
  c_jit_get_path_remove_directory_ptr()
}

///|
fn get_path_rename_ptr() -> Int64 {
  c_jit_get_path_rename_ptr()
}

///|
fn get_clock_res_get_ptr() -> Int64 {
  c_jit_get_clock_res_get_ptr()
}

///|
fn get_sched_yield_ptr() -> Int64 {
  c_jit_get_sched_yield_ptr()
}

///|
fn get_proc_raise_ptr() -> Int64 {
  c_jit_get_proc_raise_ptr()
}

///|
fn get_fd_advise_ptr() -> Int64 {
  c_jit_get_fd_advise_ptr()
}

///|
fn get_fd_allocate_ptr() -> Int64 {
  c_jit_get_fd_allocate_ptr()
}

///|
fn get_fd_pread_ptr() -> Int64 {
  c_jit_get_fd_pread_ptr()
}

///|
fn get_fd_pwrite_ptr() -> Int64 {
  c_jit_get_fd_pwrite_ptr()
}

///|
fn get_fd_readdir_ptr() -> Int64 {
  c_jit_get_fd_readdir_ptr()
}

///|
fn get_fd_renumber_ptr() -> Int64 {
  c_jit_get_fd_renumber_ptr()
}

///|
fn get_fd_fdstat_set_flags_ptr() -> Int64 {
  c_jit_get_fd_fdstat_set_flags_ptr()
}

///|
fn get_fd_fdstat_set_rights_ptr() -> Int64 {
  c_jit_get_fd_fdstat_set_rights_ptr()
}

///|
fn get_fd_filestat_set_times_ptr() -> Int64 {
  c_jit_get_fd_filestat_set_times_ptr()
}

///|
fn get_path_filestat_get_ptr() -> Int64 {
  c_jit_get_path_filestat_get_ptr()
}

///|
fn get_path_filestat_set_times_ptr() -> Int64 {
  c_jit_get_path_filestat_set_times_ptr()
}

///|
fn get_path_link_ptr() -> Int64 {
  c_jit_get_path_link_ptr()
}

///|
fn get_path_readlink_ptr() -> Int64 {
  c_jit_get_path_readlink_ptr()
}

///|
fn get_path_symlink_ptr() -> Int64 {
  c_jit_get_path_symlink_ptr()
}

///|
fn get_sock_accept_ptr() -> Int64 {
  c_jit_get_sock_accept_ptr()
}

///|
fn get_sock_recv_ptr() -> Int64 {
  c_jit_get_sock_recv_ptr()
}

///|
fn get_sock_send_ptr() -> Int64 {
  c_jit_get_sock_send_ptr()
}

///|
fn get_sock_shutdown_ptr() -> Int64 {
  c_jit_get_sock_shutdown_ptr()
}

///|
fn get_spectest_print_ptr() -> Int64 {
  c_jit_get_spectest_print_ptr()
}

///|
fn get_spectest_print_i32_ptr() -> Int64 {
  c_jit_get_spectest_print_i32_ptr()
}

///|
fn get_spectest_print_i64_ptr() -> Int64 {
  c_jit_get_spectest_print_i64_ptr()
}

///|
fn get_spectest_print_f32_ptr() -> Int64 {
  c_jit_get_spectest_print_f32_ptr()
}

///|
fn get_spectest_print_f64_ptr() -> Int64 {
  c_jit_get_spectest_print_f64_ptr()
}

///|
fn get_spectest_print_i32_f32_ptr() -> Int64 {
  c_jit_get_spectest_print_i32_f32_ptr()
}

///|
fn get_spectest_print_f64_f64_ptr() -> Int64 {
  c_jit_get_spectest_print_f64_f64_ptr()
}

///|
fn get_spectest_print_char_ptr() -> Int64 {
  c_jit_get_spectest_print_char_ptr()
}