/// Execution, callback, exception, fiber-stack, and GC operations for managed JIT contexts.

///|
fn throw_exception_tag(context : JITContext, tag_addr : Int) -> Unit {
  c_jit_exception_throw_tag_managed(context, tag_addr)
}

///|
fn throw_exception_values(
  context : JITContext,
  tag_addr : Int,
  values : Array[Int64],
) -> Unit {
  let payload = FixedArray::makei(values.length(), fn(i) { values[i] })
  c_jit_exception_throw_values_managed(
    context,
    tag_addr,
    payload,
    values.length(),
  )
}

///|
fn setup_segments(
  context : JITContext,
  datas : Array[@types.Data],
  elem_segments : Array[Array[Int64]],
  data_dropped? : Array[Bool] = [],
  elem_dropped? : Array[Bool] = [],
) -> Unit {
  c_jit_ctx_init_data_segments_managed(context, datas.length())
  for i, data in datas {
    let bytes = data.init
    let size = bytes.length()
    let dropped = if i < data_dropped.length() && data_dropped[i] {
      1
    } else {
      0
    }
    let buf = FixedArray::make(size.max(1), b'\x00')
    for j in 0.. Unit {
  c_jit_ctx_clear_segments_managed(context)
}

///|
pub fn NativeJITContext::func_count(self : NativeJITContext) -> Int {
  context_func_count(self.handle)
}

///|
pub fn NativeJITContext::throw_exception_tag(
  self : NativeJITContext,
  tag_addr : Int,
) -> Unit {
  throw_exception_tag(self.handle, tag_addr)
}

///|
pub fn NativeJITContext::throw_exception_values(
  self : NativeJITContext,
  tag_addr : Int,
  values : Array[Int64],
) -> Unit {
  throw_exception_values(self.handle, tag_addr, values)
}

///|
pub fn NativeJITContext::setup_segments(
  self : NativeJITContext,
  datas : Array[@types.Data],
  elem_segments : Array[Array[Int64]],
  data_dropped? : Array[Bool] = [],
  elem_dropped? : Array[Bool] = [],
) -> Unit {
  setup_segments(
    self.handle,
    datas,
    elem_segments,
    data_dropped~,
    elem_dropped~,
  )
}

///|
pub fn NativeJITContext::clear_segments(self : NativeJITContext) -> Unit {
  clear_segments(self.handle)
}

///|
pub fn NativeJITContext::clear_gc_heap(self : NativeJITContext) -> Unit {
  c_jit_gc_clear_heap_managed(self.handle)
}

///|
pub fn NativeJITContext::set_func(
  self : NativeJITContext,
  idx : Int,
  func_ptr : Int64,
) -> Unit {
  c_jit_ctx_set_func_managed(self.handle, idx, func_ptr)
}

///|
pub fn NativeJITContext::alloc_indirect_table(
  self : NativeJITContext,
  count : Int,
) -> Bool {
  c_jit_ctx_alloc_indirect_table_managed(self.handle, count) != 0
}

///|
pub fn NativeJITContext::set_indirect(
  self : NativeJITContext,
  table_idx : Int,
  func_idx : Int,
  type_idx : Int,
) -> Unit {
  c_jit_ctx_set_indirect_managed(self.handle, table_idx, func_idx, type_idx)
}

///|
pub fn NativeJITContext::set_globals(
  self : NativeJITContext,
  globals_ptr : Int64,
) -> Unit {
  c_jit_ctx_set_globals_managed(self.handle, globals_ptr)
}

///|
pub fn NativeJITContext::alloc_wasm_stack(
  self : NativeJITContext,
  stack_size : Int64,
) -> Bool {
  c_jit_alloc_wasm_stack_managed(self.handle, stack_size) == 0
}

///|
pub fn NativeJITContext::has_wasm_stack(self : NativeJITContext) -> Bool {
  c_jit_get_wasm_stack_top_managed(self.handle) != 0L
}

///|
/// Typed result returned by a JIT hostcall dispatcher.
pub(all) enum NativeHostcallOutcome {
  HostcallCompleted
  HostcallTrap(Int)
  HostcallSuspended
} derive(Debug, Eq)

///|
pub fn NativeJITContext::set_hostcall_callback(
  self : NativeJITContext,
  callback : () -> NativeHostcallOutcome,
) -> Unit {
  let encoded = fn() {
    match callback() {
      HostcallCompleted => 0
      HostcallTrap(code) => if code > 0 { code } else { 8 }
      HostcallSuspended => -1
    }
  }
  let call_closure : FuncRef[(() -> Int) -> Int] = fn(f : () -> Int) { f() }
  c_jit_set_hostcall_callback_managed(self.handle, call_closure, encoded)
}

///|
pub fn NativeJITContext::clear_hostcall_callback(
  self : NativeJITContext,
) -> Unit {
  c_jit_clear_hostcall_callback_managed(self.handle)
}

///|
pub fn NativeJITContext::set_cancellation_callback(
  self : NativeJITContext,
  callback : () -> Bool,
) -> Unit {
  let call_closure : FuncRef[(() -> Bool) -> Int] = fn(f : () -> Bool) {
    if f() {
      1
    } else {
      0
    }
  }
  c_jit_set_cancellation_callback_managed(self.handle, call_closure, callback)
}

///|
pub fn NativeJITContext::clear_cancellation_callback(
  self : NativeJITContext,
) -> Unit {
  c_jit_clear_cancellation_callback_managed(self.handle)
}

///|
pub fn NativeJITContext::call_trampoline(
  self : NativeJITContext,
  trampoline_ptr : Int64,
  func_ptr : Int64,
  values_vec : FixedArray[Int64],
  values_len : Int,
  use_stack_switch : Bool,
) -> Int {
  if use_stack_switch {
    c_jit_call_with_stack_switch_managed(
      self.handle,
      trampoline_ptr,
      func_ptr,
      values_vec,
      values_len,
    )
  } else {
    c_jit_call_trampoline_managed(
      self.handle,
      trampoline_ptr,
      func_ptr,
      values_vec,
      values_len,
    )
  }
}