///|
priv type Ref

///|
fn Ref::lua(self : Ref) -> Lua {
  @c.with_unsafe_borrowed(self, ptr => {
    let ptr : @c.Pointer[Byte] = ptr.cast()
    let state_ptr = (ptr.cast() : @c.Pointer[@c.Pointer[Unit]]).load()
    @c.unsafe_return(state_ptr)
  })
}

///|
fn Ref::int(self : Ref) -> Int {
  @c.with_unsafe_borrowed(self, ptr => {
    let ptr : @c.Pointer[Byte] = ptr.cast()
    ptr.add(@c.sizeof((ignore : (@c.Pointer[Unit]) -> Unit))).cast().load()
  })
}

///|
fn Lua::new_ref(lua : Lua) -> Ref {
  // ptr -> +---------+
  //        | lua ptr |
  //        +---------+
  //        | ref int |
  //        +---------+

  // Incref lua
  let lua_ptr = @c.unsafe_borrow(lua)
  // Creates reference
  let ref_int = @aux.ref_(@lua.State::of_pointer(lua_ptr), @lua.registry_index)
  let ptr = @moonbit.make_external_object(
    ptr => {
      let ptr : @c.Pointer[Byte] = ptr.cast()
      let state = (ptr.cast() : @c.Pointer[@c.Pointer[Unit]]).load()
      let ref_int : Int = ptr
        .add(@c.sizeof((ignore : (@c.Pointer[Unit]) -> Unit)))
        .cast()
        .load()
      // Release the reference
      @aux.unref(@lua.State::of_pointer(state), @lua.registry_index, ref_int)
      // Decref lua
      let lua : Lua = @c.unsafe_return(state)
      ignore(lua)
    },
    @c.sizeof((ignore : (@c.Pointer[Unit]) -> Unit)) +
    @c.sizeof((ignore : (Int) -> Unit)),
  )
  // Convert to char * for pointer arithmetic
  let ptr : @c.Pointer[Byte] = ptr.cast()
  // Store lua pointer
  (ptr.cast() : @c.Pointer[@c.Pointer[Unit]]).store(lua_ptr)
  // Store reference integer after the pointer
  ptr
  .add(@c.sizeof((ignore : (@c.Pointer[Unit]) -> Unit)))
  .cast()
  .store(ref_int)
  @c.unsafe_return(ptr.cast())
}