///|
/// Binary state snapshot, used by rollback netcode: every body's full motion
/// state, the persistent contact impulses (warm starting), and the world clock.
///
/// Layout (little endian):
///
/// ```text
/// magic   u32 = 0x4D504859 ("MPHY")
/// version u32 = 1
/// count   u32
/// time    f64
/// body*   { type u8, shape, position f64x2, rotation f64, velocity f64x2,
///           angular_velocity f64, force f64x2, torque f64, mass f64,
///           inv_mass f64, inertia f64, inv_inertia f64, friction f64,
///           restitution f64, linear_damping f64, angular_damping f64,
///           allow_sleep u8, awake u8, sleep_time f64, user_data i32 }
/// arb*    { body_a u32, body_b u32, contacts, impulses, bias... }
/// hash    u64  (FNV-1a over everything above)
/// ```
///
/// Joints are deliberately not serialized: they carry motor/solver state that is
/// not worth the format surface, so `load_state` refuses a world that has any.
const STATE_MAGIC : Int = 0x4D504859

///|
/// Snapshot layout version; bump on any change to the field order below.
const STATE_VERSION : Int = 1

///|
/// FNV-1a 64 offset basis, same constants as `state_hash`.
const STATE_FNV_BASIS : UInt64 = 0xcbf29ce484222325UL

///|
/// FNV-1a 64 prime.
const STATE_FNV_PRIME : UInt64 = 0x100000001b3UL

///|
/// Upper bound on bodies per snapshot; anything above is treated as corruption.
const STATE_MAX_BODIES : Int = 4096

///|
/// Upper bound on contact manifolds per snapshot.
const STATE_MAX_ARBITERS : Int = 8192

///|
/// Upper bound on contact points in one manifold.
const STATE_MAX_CONTACTS : Int = 16

///|
fn put_u8(out : Array[Byte], value : Int) -> Unit {
  out.push((value & 0xFF).to_byte())
}

///|
fn put_u32(out : Array[Byte], value : Int) -> Unit {
  put_u8(out, value)
  put_u8(out, value >> 8)
  put_u8(out, value >> 16)
  put_u8(out, value >> 24)
}

///|
fn put_u64(out : Array[Byte], value : UInt64) -> Unit {
  let mut i = 0
  while i < 8 {
    put_u8(out, ((value >> (i * 8)) & 0xFFUL).to_int())
    i = i + 1
  }
}

///|
fn put_f64(out : Array[Byte], value : Double) -> Unit {
  put_u64(out, value.reinterpret_as_uint64())
}

///|
fn put_doubles(out : Array[Byte], values : Array[Double]) -> Unit {
  put_u32(out, values.length())
  for value in values {
    put_f64(out, value)
  }
}

///|
fn put_vec2(out : Array[Byte], value : Vec2) -> Unit {
  put_f64(out, value.x)
  put_f64(out, value.y)
}

///|
fn state_hash_of(out : Array[Byte]) -> UInt64 {
  let mut hash = STATE_FNV_BASIS
  for byte in out {
    hash = (hash ^ byte.to_int().to_uint64()) * STATE_FNV_PRIME
  }
  hash
}

///|
/// Cursor over a snapshot. Reading past the end yields zeros and keeps counting,
/// so a truncated buffer fails the final length check instead of panicking.
priv struct StateReader {
  buf : Bytes
  mut pos : Int
}

///|
fn StateReader::new(buf : Bytes) -> StateReader {
  { buf, pos: 0 }
}

///|
fn StateReader::remaining(self : StateReader) -> Int {
  self.buf.length() - self.pos
}

///|
/// Mark the buffer as invalid; every later check fails.
fn StateReader::poison(self : StateReader) -> Unit {
  self.pos = self.buf.length() + 1
}

///|
fn StateReader::u8(self : StateReader) -> Int {
  let value = if self.pos >= 0 && self.pos < self.buf.length() {
    self.buf[self.pos].to_int() & 0xFF
  } else {
    0
  }
  self.pos = self.pos + 1
  value
}

///|
fn StateReader::u32(self : StateReader) -> Int {
  let b0 = self.u8()
  let b1 = self.u8()
  let b2 = self.u8()
  let b3 = self.u8()
  b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
}

///|
fn StateReader::u64(self : StateReader) -> UInt64 {
  let mut value = 0UL
  let mut i = 0
  while i < 8 {
    value = value | (self.u8().to_uint64() << (i * 8))
    i = i + 1
  }
  value
}

///|
fn StateReader::f64(self : StateReader) -> Double {
  self.u64().reinterpret_as_double()
}

///|
fn StateReader::vec2(self : StateReader) -> Vec2 {
  let x = self.f64()
  let y = self.f64()
  Vec2::new(x, y)
}

///|
fn StateReader::doubles(self : StateReader) -> Array[Double] {
  let count = self.u32()
  let out : Array[Double] = []
  if count < 0 || count > self.remaining() / 8 {
    self.poison()
    return out
  }
  let mut i = 0
  while i < count {
    out.push(self.f64())
    i = i + 1
  }
  out
}

///|
fn encode_state_body(out : Array[Byte], body : Body) -> Unit {
  match body.body_type {
    BodyType::Static => put_u8(out, 0)
    BodyType::Dynamic => put_u8(out, 1)
  }
  match body.shape {
    Shape::Circle(radius) => {
      put_u8(out, 0)
      put_f64(out, radius)
    }
    Shape::Polygon(vertices) => {
      put_u8(out, 1)
      put_u32(out, vertices.length())
      for vertex in vertices {
        put_vec2(out, vertex)
      }
    }
  }
  put_vec2(out, body.position)
  put_f64(out, body.rotation)
  put_vec2(out, body.velocity)
  put_f64(out, body.angular_velocity)
  put_vec2(out, body.force)
  put_f64(out, body.torque)
  put_f64(out, body.mass)
  put_f64(out, body.inv_mass)
  put_f64(out, body.inertia)
  put_f64(out, body.inv_inertia)
  put_f64(out, body.friction)
  put_f64(out, body.restitution)
  put_f64(out, body.linear_damping)
  put_f64(out, body.angular_damping)
  put_u8(out, if body.allow_sleep { 1 } else { 0 })
  put_u8(out, if body.awake { 1 } else { 0 })
  put_f64(out, body.sleep_time)
  put_u32(out, body.user_data)
}

///|
fn decode_state_body(reader : StateReader) -> Body {
  let body_type = if reader.u8() == 1 {
    BodyType::Dynamic
  } else {
    BodyType::Static
  }
  let shape = if reader.u8() == 0 {
    Shape::Circle(reader.f64())
  } else {
    let count = reader.u32()
    // A convex hull needs at least three vertices; refuse anything absurd.
    if count < 3 || count > 64 || count > reader.remaining() / 16 {
      reader.poison()
      Shape::Circle(0.5)
    } else {
      let vertices : Array[Vec2] = []
      let mut i = 0
      while i < count {
        vertices.push(reader.vec2())
        i = i + 1
      }
      Shape::Polygon(vertices)
    }
  }
  let body : Body = {
    body_type,
    shape,
    position: reader.vec2(),
    rotation: reader.f64(),
    velocity: reader.vec2(),
    angular_velocity: reader.f64(),
    force: reader.vec2(),
    torque: reader.f64(),
    mass: reader.f64(),
    inv_mass: reader.f64(),
    inertia: reader.f64(),
    inv_inertia: reader.f64(),
    friction: reader.f64(),
    restitution: reader.f64(),
    linear_damping: reader.f64(),
    angular_damping: reader.f64(),
    allow_sleep: reader.u8() == 1,
    awake: reader.u8() == 1,
    sleep_time: reader.f64(),
    user_data: reader.u32(),
    world_aabb: Aabb::new(Vec2::zero(), Vec2::zero()),
  }
  body.sync_aabb()
  body
}

///|
fn encode_state_arbiter(out : Array[Byte], arbiter : Arbiter) -> Unit {
  put_u32(out, arbiter.body_a)
  put_u32(out, arbiter.body_b)
  put_u32(out, arbiter.contacts.length())
  for contact in arbiter.contacts {
    put_vec2(out, contact.position)
    put_vec2(out, contact.normal)
    put_f64(out, contact.separation)
    put_u32(out, contact.feature_id)
  }
  put_doubles(out, arbiter.normal_impulses)
  put_doubles(out, arbiter.tangent_impulses)
  put_doubles(out, arbiter.mass_normal)
  put_doubles(out, arbiter.mass_tangent)
  put_doubles(out, arbiter.bias)
  put_doubles(out, arbiter.restitution_bias)
  put_f64(out, arbiter.friction)
  put_f64(out, arbiter.restitution)
}

///|
fn decode_state_arbiter(reader : StateReader) -> Arbiter {
  let body_a = reader.u32()
  let body_b = reader.u32()
  let count = reader.u32()
  let contacts : Array[ContactPoint] = []
  if count > STATE_MAX_CONTACTS || count > reader.remaining() / 36 {
    reader.poison()
  } else {
    let mut i = 0
    while i < count {
      contacts.push({
        position: reader.vec2(),
        normal: reader.vec2(),
        separation: reader.f64(),
        feature_id: reader.u32(),
      })
      i = i + 1
    }
  }
  {
    body_a,
    body_b,
    contacts,
    normal_impulses: reader.doubles(),
    tangent_impulses: reader.doubles(),
    mass_normal: reader.doubles(),
    mass_tangent: reader.doubles(),
    bias: reader.doubles(),
    restitution_bias: reader.doubles(),
    friction: reader.f64(),
    restitution: reader.f64(),
  }
}

///|
/// Serialize the full simulation state: bodies (including velocities, forces and
/// sleep timers), persistent contact impulses, and the world clock.
///
/// `hash` at the tail makes a corrupted or truncated buffer detectable.
pub fn World::save_state(self : World) -> Bytes {
  let out : Array[Byte] = []
  put_u32(out, STATE_MAGIC)
  put_u32(out, STATE_VERSION)
  put_u32(out, self.bodies.length())
  put_f64(out, self.time)
  for body in self.bodies {
    encode_state_body(out, body)
  }
  put_u32(out, self.arbiters.length())
  for arbiter in self.arbiters {
    encode_state_arbiter(out, arbiter)
  }
  put_u64(out, state_hash_of(out))
  Bytes::makei(out.length(), fn(i) { out[i] })
}

///|
/// Restore a snapshot produced by `save_state`.
///
/// Returns `false` and leaves the world untouched when the buffer is truncated,
/// corrupted, from another format version, or when the world has joints (joint
/// solver state is not part of the format).
pub fn World::load_state(self : World, bytes : Bytes) -> Bool {
  if self.joints.length() > 0 {
    return false
  }
  let total = bytes.length()
  if total < 20 {
    return false
  }
  let payload = total - 8
  let mut hash = STATE_FNV_BASIS
  let mut i = 0
  while i < payload {
    hash = (hash ^ bytes[i].to_int().to_uint64()) * STATE_FNV_PRIME
    i = i + 1
  }
  let mut stored = 0UL
  let mut k = 0
  while k < 8 {
    stored = stored | (bytes[payload + k].to_int().to_uint64() << (k * 8))
    k = k + 1
  }
  if hash != stored {
    return false
  }
  let reader = StateReader::new(bytes)
  if reader.u32() != STATE_MAGIC {
    return false
  }
  if reader.u32() != STATE_VERSION {
    return false
  }
  let body_count = reader.u32()
  if body_count < 0 || body_count > STATE_MAX_BODIES {
    return false
  }
  let time = reader.f64()
  let bodies : Array[Body] = []
  let mut b = 0
  while b < body_count && reader.pos <= total {
    bodies.push(decode_state_body(reader))
    b = b + 1
  }
  let arbiter_count = reader.u32()
  if arbiter_count < 0 || arbiter_count > STATE_MAX_ARBITERS {
    return false
  }
  let arbiters : Array[Arbiter] = []
  let mut a = 0
  while a < arbiter_count && reader.pos <= total {
    let arbiter = decode_state_arbiter(reader)
    // A body index out of range would panic in the next step.
    if arbiter.body_a >= body_count || arbiter.body_b >= body_count {
      return false
    }
    arbiters.push(arbiter)
    a = a + 1
  }
  if reader.pos != payload {
    return false
  }
  // Commit only after every field parsed cleanly.
  self.bodies.clear()
  for body in bodies {
    self.bodies.push(body)
  }
  self.arbiters = arbiters
  self.time = time
  true
}