///|
/// WebAssembly linear memory.
///
/// Native builds use an authoritative C descriptor shared with JIT. Wasm builds
/// use managed storage selected at compile time; both reuse checked guest access.
#cfg(target="native")
priv enum MemoryDescriptorState {
  Open(@native_bridge.MemoryDescriptor)
  Closed
} derive(Debug)

///|
struct Memory {
  mut binding : ResourceBinding?
  descriptor_state : Ref[MemoryDescriptorState]
  max : Int64? // max size in logical pages
  is_memory64 : Bool // true for memory64 (i64-indexed)
  is_shared : Bool
  page_size_log2 : Int32
  page_size_bytes : Int64
} derive(Debug)

///|
/// Retain the native memory descriptor.
///
/// Existing handles stay live across Store close; closed Memory aliases return
/// an empty handle and reject further access through the Memory API.
#cfg(target="native")
pub fn Memory::desc_ptr(self : Memory) -> @native_bridge.MemoryDescriptor {
  if self.binding is Some(binding) && (binding.store.val || !binding.active.val) {
    return @native_bridge.MemoryDescriptor::null()
  }
  match self.descriptor_state.val {
    Open(pointer) => pointer
    Closed => @native_bridge.MemoryDescriptor::null()
  }
}

///|
#cfg(target="native")
fn Memory::require_desc_ptr(
  self : Memory,
) -> @native_bridge.MemoryDescriptor raise RuntimeError {
  if self.descriptor_state.val is Closed {
    raise MemoryClosed
  }
  check_resource_open(self.binding)
  match self.descriptor_state.val {
    Open(pointer) if !pointer.is_null() => pointer
    Open(_) => raise OutOfMemory
    Closed => raise MemoryClosed
  }
}

///|
#cfg(target="native")
pub fn Memory::close(self : Memory) -> Unit {
  match self.descriptor_state.val {
    Open(_) => self.descriptor_state.val = Closed
    Closed => ()
  }
}

///|
#cfg(target="native")
pub fn Memory::byte_len(self : Memory) -> Int64 {
  let pointer = self.desc_ptr()
  if pointer.is_null() {
    0L
  } else {
    @native_bridge.memory_descriptor_length(pointer)
  }
}

///|
pub fn Memory::page_size_bytes(self : Memory) -> Int64 {
  self.page_size_bytes
}

///|
/// Convert a WASM address to a byte offset.
///
/// For memory32, addresses are u32, so negative Int values represent large
/// addresses (high bit set) and must be treated as unsigned.
fn Memory::addr_to_offset(self : Memory, addr : Int32) -> Int64 {
  if self.is_memory64 {
    addr.to_int64()
  } else {
    // Interpret as u32 and widen to i64.
    addr.to_int64() & 0xFFFF_FFFFL
  }
}

///|
pub fn Memory::Memory(
  min : Int32,
  max : Int32?,
  is_memory64~ : Bool = false,
  is_shared~ : Bool = false,
  page_size_log2~ : Int32 = 16,
  resource_budget~ : ResourceBudget? = None,
) -> Memory raise RuntimeError {
  Memory::allocate(
    min.to_int64() & 0xFFFF_FFFFL,
    max.map(fn(value) { value.to_int64() & 0xFFFF_FFFFL }),
    is_memory64~,
    is_shared~,
    page_size_log2~,
    resource_budget~,
  )
}

///|
fn Memory::allocate(
  min : Int64,
  max : Int64?,
  is_memory64~ : Bool,
  is_shared~ : Bool,
  page_size_log2~ : Int32,
  resource_budget~ : ResourceBudget? = None,
) -> Memory raise RuntimeError {
  let l2 = page_size_log2
  if l2 < 0 || l2 > 30 {
    raise InvalidMemoryType("invalid page size")
  }
  if max is Some(maximum) &&
    maximum.reinterpret_as_uint64() < min.reinterpret_as_uint64() {
    raise InvalidMemoryType("maximum is below minimum")
  }
  if is_shared && max is None {
    raise InvalidMemoryType("shared memory requires a maximum")
  }
  let page_size_bytes = 1L << l2
  let valid_size = min >= 0L &&
    min <= 0x7FFF_FFFF_FFFF_FFFFL / page_size_bytes &&
    (
      is_memory64 ||
      min <=
      (if l2 == 0 { 0xFFFF_FFFEL } else { 0x1_0000_0000L / page_size_bytes })
    )
  guard valid_size else { raise MemoryMinimumExceedsLimits(min) }
  let reservation = resource_budget.map(budget => {
    budget.reservation(MemoryBytes, min * page_size_bytes)
  })
  errdefer (if reservation is Some(reservation) { reservation.close() })
  let descriptor_state = allocate_memory_storage(
    min, max, is_memory64, is_shared, l2,
  )
  let memory = {
    binding: None,
    descriptor_state: Ref(descriptor_state),
    max,
    is_memory64,
    is_shared,
    page_size_log2: l2,
    page_size_bytes,
  }
  memory.ensure_memory_open()
  if reservation is Some(reservation) {
    memory.bind_memory_reservation(reservation)
  }
  memory
}

///|
/// Allocate a validated module memory without narrowing its page limits.
pub fn Memory::from_type(
  ty : @types.MemoryType,
  resource_budget~ : ResourceBudget? = None,
) -> Memory raise RuntimeError {
  Memory::allocate(
    ty.limits.min,
    ty.limits.max,
    is_memory64=ty.is_memory64,
    is_shared=ty.is_shared,
    page_size_log2=ty.page_size_log2,
    resource_budget~,
  )
}

///|
pub fn Memory::new_guarded_memory0(
  min : Int32,
  max : Int32?,
  is_shared~ : Bool = false,
) -> Memory raise RuntimeError {
  Memory(min, max, is_shared~)
}

// Compatibility entry points for callers that hold 32-bit addresses.
// All typed accesses use the checked 64-bit byte-offset implementation.

///|
pub fn Memory::load_byte(
  self : Memory,
  addr : Int32,
) -> Byte raise RuntimeError {
  self.load_byte_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::store_byte(
  self : Memory,
  addr : Int32,
  value : Byte,
) -> Unit raise RuntimeError {
  self.store_byte_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::load_i32(
  self : Memory,
  addr : Int32,
) -> Int32 raise RuntimeError {
  self.load_i32_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::store_i32(
  self : Memory,
  addr : Int32,
  value : Int32,
) -> Unit raise RuntimeError {
  self.store_i32_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::load_i64(
  self : Memory,
  addr : Int32,
) -> Int64 raise RuntimeError {
  self.load_i64_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::store_i64(
  self : Memory,
  addr : Int32,
  value : Int64,
) -> Unit raise RuntimeError {
  self.store_i64_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::load_f32(
  self : Memory,
  addr : Int32,
) -> Float raise RuntimeError {
  self.load_f32_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::store_f32(
  self : Memory,
  addr : Int32,
  value : Float,
) -> Unit raise RuntimeError {
  self.store_f32_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::load_f64(
  self : Memory,
  addr : Int32,
) -> Double raise RuntimeError {
  self.load_f64_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::store_f64(
  self : Memory,
  addr : Int32,
  value : Double,
) -> Unit raise RuntimeError {
  self.store_f64_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::load_i32_8s(
  self : Memory,
  addr : Int32,
) -> Int32 raise RuntimeError {
  self.load_i32_8s_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::load_i32_8u(
  self : Memory,
  addr : Int32,
) -> Int32 raise RuntimeError {
  self.load_i32_8u_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::load_i32_16s(
  self : Memory,
  addr : Int32,
) -> Int32 raise RuntimeError {
  self.load_i32_16s_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::load_i32_16u(
  self : Memory,
  addr : Int32,
) -> Int32 raise RuntimeError {
  self.load_i32_16u_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::load_i64_8s(
  self : Memory,
  addr : Int32,
) -> Int64 raise RuntimeError {
  self.load_i64_8s_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::load_i64_8u(
  self : Memory,
  addr : Int32,
) -> Int64 raise RuntimeError {
  self.load_i64_8u_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::load_i64_16s(
  self : Memory,
  addr : Int32,
) -> Int64 raise RuntimeError {
  self.load_i64_16s_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::load_i64_16u(
  self : Memory,
  addr : Int32,
) -> Int64 raise RuntimeError {
  self.load_i64_16u_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::load_i64_32s(
  self : Memory,
  addr : Int32,
) -> Int64 raise RuntimeError {
  self.load_i64_32s_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::load_i64_32u(
  self : Memory,
  addr : Int32,
) -> Int64 raise RuntimeError {
  self.load_i64_32u_at(self.addr_to_offset(addr))
}

///|
pub fn Memory::store_i32_8(
  self : Memory,
  addr : Int32,
  value : Int32,
) -> Unit raise RuntimeError {
  self.store_i32_8_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::store_i32_16(
  self : Memory,
  addr : Int32,
  value : Int32,
) -> Unit raise RuntimeError {
  self.store_i32_16_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::store_i64_8(
  self : Memory,
  addr : Int32,
  value : Int64,
) -> Unit raise RuntimeError {
  self.store_i64_8_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::store_i64_16(
  self : Memory,
  addr : Int32,
  value : Int64,
) -> Unit raise RuntimeError {
  self.store_i64_16_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::store_i64_32(
  self : Memory,
  addr : Int32,
  value : Int64,
) -> Unit raise RuntimeError {
  self.store_i64_32_at(self.addr_to_offset(addr), value)
}

///|
pub fn Memory::size_pages(self : Memory) -> Int32 {
  let bytes = self.byte_len()
  if self.page_size_bytes <= 0L {
    0
  } else {
    (bytes / self.page_size_bytes).to_int()
  }
}

///|
pub fn Memory::is_memory64(self : Memory) -> Bool {
  self.is_memory64
}

///|
pub fn Memory::grow(self : Memory, delta : Int32) -> Int32 {
  self.grow64(delta.to_int64() & 0xFFFF_FFFFL).to_int()
}

///|
/// Grow by an unsigned memory64 page count; negative bit patterns cannot fit.
#cfg(target="native")
pub fn Memory::grow64(self : Memory, delta : Int64) -> Int64 {
  let pointer = self.desc_ptr()
  if pointer.is_null() || delta < 0L {
    return -1L
  }
  @native_bridge.memory_descriptor_grow64(pointer, delta)
}

///|
/// Initialize memory with data segment
pub fn Memory::init_data(
  self : Memory,
  dest : Int32,
  data : Bytes,
) -> Unit raise RuntimeError {
  self.init_data_at(self.addr_to_offset(dest), data)
}

///|
/// Copy memory within the same memory instance
/// Handles overlapping regions correctly
pub fn Memory::copy(
  self : Memory,
  dest : Int32,
  src : Int32,
  len : Int32,
) -> Unit raise RuntimeError {
  self.copy_at(
    self.addr_to_offset(dest),
    self.addr_to_offset(src),
    len.to_int64(),
  )
}

///|
/// Fill memory region with a byte value
pub fn Memory::fill(
  self : Memory,
  dest : Int32,
  value : Byte,
  len : Int32,
) -> Unit raise RuntimeError {
  self.fill_at(self.addr_to_offset(dest), value, len.to_int64())
}

///|
/// Read i32 from memory (non-raising version for WASI)
pub fn Memory::read_i32(self : Memory, addr : Int32) -> Int32 {
  let offset = addr.reinterpret_as_uint().to_int64()
  if offset + 4L > self.byte_len() {
    return 0
  }
  self.load_i32_at(offset) catch {
    _ => 0
  }
}

///|
/// Write i32 to memory (non-raising version for WASI)
pub fn Memory::write_i32(self : Memory, addr : Int32, value : Int32) -> Unit {
  let offset = addr.reinterpret_as_uint().to_int64()
  if offset + 4L > self.byte_len() {
    return
  }
  try self.store_i32_at(offset, value) |> ignore catch {
    _ => ()
  }
}

///|
/// Read i64 from memory (non-raising version for WASI)
pub fn Memory::read_i64(self : Memory, addr : Int32) -> Int64 {
  let offset = addr.reinterpret_as_uint().to_int64()
  if offset + 8L > self.byte_len() {
    return 0L
  }
  self.load_i64_at(offset) catch {
    _ => 0L
  }
}

///|
/// Write i64 to memory (non-raising version for WASI)
pub fn Memory::write_i64(self : Memory, addr : Int32, value : Int64) -> Unit {
  let offset = addr.reinterpret_as_uint().to_int64()
  if offset + 8L > self.byte_len() {
    return
  }
  try self.store_i64_at(offset, value) |> ignore catch {
    _ => ()
  }
}

///|
/// Read a byte from memory (non-raising version for WASI)
pub fn Memory::read_byte(self : Memory, addr : Int32) -> Byte {
  let offset = addr.reinterpret_as_uint().to_int64()
  if offset >= self.byte_len() {
    return b'\x00'
  }
  self.load_byte_unchecked_at(offset)
}

///|
/// Write a byte to memory (non-raising version for WASI)
pub fn Memory::write_byte(self : Memory, addr : Int32, value : Byte) -> Unit {
  let offset = addr.reinterpret_as_uint().to_int64()
  if offset >= self.byte_len() {
    return
  }
  self.store_byte_unchecked_at(offset, value)
}

///|
/// Read bytes from memory (non-raising version for WASI)
pub fn Memory::read_bytes(self : Memory, addr : Int32, len : Int32) -> Bytes {
  let offset = addr.reinterpret_as_uint().to_int64()
  if len < 0 || offset + len.to_int64() > self.byte_len() {
    return b""
  }
  if len == 0 {
    return b""
  }
  let fixed = self.read_fixed_at(offset, len)
  let arr : Array[Byte] = Array::make(len, b'\x00')
  for i in 0.. (Int32, Int32?) {
  (
    self.size_pages(),
    match self.max {
      Some(value) if value >= 0L && value <= 0xFFFF_FFFFL =>
        Some(value.to_int())
      _ => None
    },
  )
}

///|
pub fn Memory::is_shared(self : Memory) -> Bool {
  self.is_shared
}

///|
#cfg(target="native")
fn Memory::share(self : Memory) -> Memory raise RuntimeError {
  let pointer = self.require_desc_ptr()
  if !self.is_shared {
    raise LinkErrorDetail("only shared memory can be imported across Stores")
  }
  {
    binding: None,
    descriptor_state: Ref(Open(pointer)),
    max: self.max,
    is_memory64: self.is_memory64,
    is_shared: true,
    page_size_log2: self.page_size_log2,
    page_size_bytes: self.page_size_bytes,
  }
}

///|
/// Return the full logical page limits for module import compatibility.
pub fn Memory::get_limits64(self : Memory) -> (Int64, Int64?) {
  (self.byte_len() / self.page_size_bytes, self.max)
}