///|
/// Store identity without retaining the Store or creating an ownership cycle.
priv struct ResourceBinding {
  store : Ref[Bool]
  active : Ref[Bool]
} derive(Debug)

///|
fn check_resource_open(binding : ResourceBinding?) -> Unit raise RuntimeError {
  if binding is Some(binding) {
    if binding.store.val {
      raise StoreClosed
    }
    if !binding.active.val {
      raise InvalidHandle
    }
  }
}

///|
fn resource_binding(
  owner : ResourceBinding?,
  store : Ref[Bool],
) -> ResourceBinding raise RuntimeError {
  check_resource_open(owner)
  if owner is Some(owner) {
    guard physical_equal(owner.store, store) else { raise ForeignStore }
    owner
  } else {
    { store, active: Ref(true), }
  }
}

///|
fn check_resource_copy(
  destination : ResourceBinding?,
  source : ResourceBinding?,
) -> Unit raise RuntimeError {
  check_resource_open(destination)
  check_resource_open(source)
  match (destination, source) {
    (Some(destination), Some(source)) =>
      if !physical_equal(destination.store, source.store) {
        raise ForeignStore
      }
    (None, None) => ()
    _ => raise InvalidHandle
  }
}

///|
/// Shape checks at raw host mutation seams. Concrete reference subtyping is
/// checked with Store type graphs at validated Wasm and rooted-value boundaries.
fn value_fits_kind(value : @types.Value, type_ : @types.ValueType) -> Bool {
  match (value, type_) {
    (I32(_), I32) | (I64(_), I64) | (F32(_), F32) | (F64(_), F64) => true
    (V128(bytes), V128) => bytes.length() == 16
    (Null, type_) => type_.is_nullable()
    (FuncRef(_), FuncRef | RefFunc | RefFuncTyped(_) | RefNullFuncTyped(_)) =>
      true
    (ExternRef(_), ExternRef | RefExtern | AnyRef | RefAny) => true
    (ExnRef(_), ExnRef) => true
    (ContRef(_), ContRef | RefCont | RefContTyped(_) | RefNullContTyped(_)) =>
      true
    (
      StructRef(_),
      StructRef
      | RefStructAbs
      | RefStruct(_)
      | RefNullStruct(_)
      | AnyRef
      | RefAny
      | RefEq
      | RefNullEq,
    ) => true
    (
      ArrayRef(_),
      ArrayRef
      | RefArrayAbs
      | RefArray(_)
      | RefNullArray(_)
      | AnyRef
      | RefAny
      | RefEq
      | RefNullEq,
    ) => true
    (I31(_), RefI31 | RefNullI31 | RefEq | RefNullEq | AnyRef | RefAny) => true
    _ => false
  }
}