/// Reference encoding helpers for JIT/runtime interop.
///
/// There are multiple representations used for references:
/// - Raw lowered values: null = 0
/// - Funcref (index form): raw = -(func_idx + 1)  (so -1 is func_idx 0, not null)
/// - Funcref (pointer form): raw = func_ptr | FUNCREF_TAG
/// - Externref: raw = EXTERNREF_TAG | (host_idx << 1)
/// - GC refs: raw = (heap_idx0 + 1) << 1
/// - i31: raw = (value << 1) | 1

///|
pub fn is_null_ref(raw : Int64) -> Bool {
  raw == @wasm_milkir.NULL_REF
}

///|
pub fn encode_null_ref() -> Int64 {
  @wasm_milkir.NULL_REF
}

///|
pub fn encode_funcref_idx(func_idx : Int) -> Int64 {
  -(func_idx.to_int64() + 1L)
}

///|
pub fn decode_funcref_idx(raw : Int64) -> Int? {
  if raw < 0L {
    Some((-(raw + 1L)).to_int())
  } else {
    None
  }
}

///|
pub fn is_funcref_ptr(raw : Int64) -> Bool {
  (raw & @wasm_milkir.FUNCREF_TAG) != 0L
}

///|
pub fn tag_funcref_ptr(func_ptr : Int64) -> Int64 {
  func_ptr | @wasm_milkir.FUNCREF_TAG
}

///|
pub fn untag_funcref_ptr(tagged_ptr : Int64) -> Int64 {
  // Clear bit 61
  tagged_ptr & 0xDFFFFFFFFFFFFFFFL
}

///|
pub fn encode_externref(host_idx : Int) -> Int64 {
  @wasm_milkir.EXTERNREF_TAG | (host_idx.to_int64() << 1)
}

///|
pub fn decode_externref(raw : Int64) -> Int? {
  if (raw & @wasm_milkir.EXTERNREF_TAG) != 0L {
    // Clear the tag bit and shift back.
    Some(((raw ^ @wasm_milkir.EXTERNREF_TAG) >> 1).to_int())
  } else {
    None
  }
}

///|
pub fn encode_gc_heap_ref(heap_idx0 : Int) -> Int64 {
  (heap_idx0 + 1).to_int64() << 1
}

///|
pub fn decode_gc_heap_ref(raw : Int64) -> Int? {
  if raw == @wasm_milkir.NULL_REF {
    None
  } else if (raw & (@wasm_milkir.FUNCREF_TAG | @wasm_milkir.EXTERNREF_TAG)) !=
    0L {
    None
  } else if (raw & 1L) == 1L {
    None
  } else {
    Some(((raw >> 1) - 1L).to_int())
  }
}