///|
/// 紧凑字节协议:输入帧 / 输入区间帧 / 确认帧 / 校验和帧 / 在线状态帧 /
/// 全量状态帧 / 状态请求帧。
///
/// 线上格式(小端,varint 为 LEB128):
/// ```
/// Input    : [0x01][varint player][varint frame][varint count][varint bits] * count
/// Confirm  : [0x02][varint frame]
/// Checksum : [0x03][varint frame][8 bytes hash]
/// Active   : [0x04][varint player][varint active]
/// InputSpan: [0x05][varint player][varint start][varint count][varint mask][varint bits] * popcount(mask)
/// Snap     : [0x06][varint frame][varint len][byte] * len
/// SnapAsk  : [0x07][varint frame]
/// ```
///
/// `Input` 里的 `inputs[i]` 是玩家 `player + i` 在同一帧的输入位图。
/// 常见情况是「自己一个人的一帧」,即 `count = 1`;多人同帧批量补齐时
/// `count` 才会大于 1。
///
/// `InputSpan` 是同一个玩家的一段连续帧:`count` 是**帧数**,`mask` 的第 i 位
/// 表示 `start + i` 这一帧的输入是否随包发出,未置位的帧沿用包里前一个值。
/// 重发冗余输入时,一个人一帧一条 `Input` 要 4 字节,一段 8 帧且输入只变两次
/// 时 `InputSpan` 只要 7 字节。
pub(all) struct InputFrame {
  player : Int32
  frame : Int32
  inputs : Array[UInt64]
} derive(Eq)

///|
pub(all) struct ConfirmFrame {
  frame : Int32
} derive(Eq)

///|
pub(all) struct ChecksumFrame {
  frame : Int32
  hash : UInt64
} derive(Eq)

///|
/// 同一个玩家的一段连续帧输入。
///
/// `count` 是帧数,`mask` 的第 i 位表示 `start + i` 帧的输入是否在 `values`
/// 里显式给出;未置位表示「与包里前一个显式值相同」。首帧必须置位,
/// 否则这段就没有起点值。
///
/// 它是 `Input` 的补充而不是替代:`Input` 适合「同一帧、多个玩家」,
/// `InputSpan` 适合「同一个玩家、多帧重发」。
pub(all) struct InputSpanFrame {
  player : Int32
  start : Int32
  count : Int32
  mask : UInt64
  values : Array[UInt64]
} derive(Eq)

///|
/// 某个玩家上线 / 掉线的声明。`active = 1` 是上线,`0` 是掉线。
///
/// 掉线本身不是协议层的判断(网络断没断只有上层知道),这里只负责把
/// 上层的决定可靠地同步给其他对等体,让所有人用同一个在线集合推进确认帧。
pub(all) struct ActiveFrame {
  player : Int32
  active : Bool
} derive(Eq)

///|
/// 某一帧的**完整状态**,用于分叉之后的全量重同步。
///
/// `state` 是 `Sim::save` 的原始字节,协议层不解释它——只有跑同一个 `Sim`
/// 的对等体才读得懂。所以它是「可信对端之间」的帧,不是公开数据:
/// 接收方要么把它原样交给 `Session::resync_from`,要么丢掉。
pub(all) struct SnapFrame {
  frame : Int32
  state : Bytes
} derive(Eq)

///|
/// 索取某一帧的完整状态。分叉被检测到之后,落后的一端发这个包,
/// 由持有权威状态的那一端回一条 `Snap`。
///
/// 单独一个请求帧而不是把请求塞进 `Checksum`,是因为「报告不一致」和
/// 「请求重同步」是两件事:前者可以每帧发,后者只该在对端明确落后时发一次。
pub(all) struct SnapRequestFrame {
  frame : Int32
} derive(Eq)

///|
pub(all) enum Message {
  Input(InputFrame)
  Confirm(ConfirmFrame)
  Checksum(ChecksumFrame)
  Active(ActiveFrame)
  Span(InputSpanFrame)
  Snap(SnapFrame)
  SnapRequest(SnapRequestFrame)
} derive(Eq)

///|
/// 显式声明 `derive(Eq)` 生成的方法提升为普通方法(消除 W0079)。
pub extend InputFrame with Eq::{not_equal, equal}

///|
pub extend ConfirmFrame with Eq::{not_equal, equal}

///|
pub extend ChecksumFrame with Eq::{not_equal, equal}

///|
pub extend ActiveFrame with Eq::{not_equal, equal}

///|
pub extend InputSpanFrame with Eq::{not_equal, equal}

///|
pub extend SnapFrame with Eq::{not_equal, equal}

///|
pub extend SnapRequestFrame with Eq::{not_equal, equal}

///|
pub extend Message with Eq::{not_equal, equal}

///|
/// 默认把 `inputs[0]` 当作玩家 0 的输入。
pub fn InputFrame::new(frame : Int32, inputs : Array[UInt64]) -> InputFrame {
  { player: 0, frame, inputs, }
}

///|
/// `inputs[i]` 是玩家 `player + i` 的输入。
pub fn InputFrame::new_from(
  player : Int32,
  frame : Int32,
  inputs : Array[UInt64],
) -> InputFrame {
  { player, frame, inputs, }
}

///|
pub fn ConfirmFrame::new(frame : Int32) -> ConfirmFrame {
  { frame, }
}

///|
pub fn ChecksumFrame::new(frame : Int32, hash : UInt64) -> ChecksumFrame {
  { frame, hash, }
}

///|
pub fn ActiveFrame::new(player : Int32, active : Bool) -> ActiveFrame {
  { player, active, }
}

///|
pub fn SnapFrame::new(frame : Int32, state : Bytes) -> SnapFrame {
  { frame, state, }
}

///|
pub fn SnapRequestFrame::new(frame : Int32) -> SnapRequestFrame {
  { frame, }
}

///|
/// 一条 `Snap` 帧能携带的状态字节上限(64 KiB)。
///
/// 全量状态是「一整帧的世界」,正常只有几 KB;60fps 下再大的快照也不该
/// 塞进单包。给一个明确上限,解码端就能在读到 `len` 的瞬间拒绝畸形包,
/// 而不是为它分配一块任意大的内存。
pub const MAX_SNAPSHOT : Int32 = 65536

///|
/// 一段帧的输入上限:掩码是 64 位,再多就用两个包。
pub const MAX_SPAN : Int32 = 64

///|
/// 由「玩家 `player` 从 `start` 起的连续帧输入」构造。
///
/// 首帧总是显式,之后只有与前一帧不同才显式——所以「按住同一个键 8 帧」
/// 在线上只是一个值加一个掩码。超过 `MAX_SPAN` 帧的部分会被截断。
pub fn InputSpanFrame::of(
  player : Int32,
  start : Int32,
  inputs : Array[UInt64],
) -> InputSpanFrame {
  let n = if inputs.length() > MAX_SPAN { MAX_SPAN } else { inputs.length() }
  let values : Array[UInt64] = []
  let mut mask = 0UL
  let mut last = 0UL
  let mut i = 0
  while i < n {
    let v = inputs[i]
    if i == 0 || v != last {
      mask = mask | (1UL << i)
      values.push(v)
      last = v
    }
    i = i + 1
  }
  { player, start, count: n, mask, values, }
}

///|
/// 逐帧全给的版本:掩码全 1,不做任何折叠。
///
/// 用来把「区间帧」和「变化掩码」两个优化的收益分开测——
/// `raw` 与 `of` 的差就是掩码省下的那部分。
pub fn InputSpanFrame::raw(
  player : Int32,
  start : Int32,
  inputs : Array[UInt64],
) -> InputSpanFrame {
  let n = if inputs.length() > MAX_SPAN { MAX_SPAN } else { inputs.length() }
  let values : Array[UInt64] = []
  let mut mask = 0UL
  let mut i = 0
  while i < n {
    values.push(inputs[i])
    mask = mask | (1UL << i)
    i = i + 1
  }
  { player, start, count: n, mask, values, }
}

///|
/// 展开成逐帧输入,长度等于 `count`。
///
/// 未置位的帧沿用包里前一个显式值;因此解码合法的段必定以显式帧开头。
pub fn InputSpanFrame::expand(self : InputSpanFrame) -> Array[UInt64] {
  let out : Array[UInt64] = []
  let mut cur = 0UL
  let mut k = 0
  let mut i = 0
  while i < self.count {
    if (self.mask & (1UL << i)) != 0UL && k < self.values.length() {
      cur = self.values[k]
      k = k + 1
    }
    out.push(cur)
    i = i + 1
  }
  out
}

///|
fn put_varint(out : Array[Byte], v : UInt64) -> Unit {
  let mut x = v
  while x >= 128UL {
    out.push(((x & 0x7FUL).to_int() | 0x80).to_byte())
    x = x >> 7
  }
  out.push(x.to_int().to_byte())
}

///|
fn get_varint(buf : Bytes, start : Int32) -> (UInt64, Int32)? {
  let mut x = 0UL
  let mut shift = 0
  let mut pos = start
  while pos < buf.length() && shift <= 63 {
    let b = buf[pos].to_int()
    x = x | ((b.to_uint64() & 0x7FUL) << shift)
    pos = pos + 1
    if b < 128 {
      return Some((x, pos))
    }
    shift = shift + 7
  }
  None
}

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

///|
fn get_u64(buf : Bytes, pos : Int32) -> UInt64? {
  if pos < 0 || pos + 8 > buf.length() {
    return None
  }
  let mut v = 0UL
  let mut i = 0
  while i < 8 {
    v = v | (buf[pos + i].to_int().to_uint64() << (i * 8))
    i = i + 1
  }
  Some(v)
}

///|
fn to_bytes(arr : Array[Byte]) -> Bytes {
  Bytes::makei(arr.length(), fn(i) { arr[i] })
}

///|
fn popcount64(x : UInt64) -> Int32 {
  let mut v = x
  let mut n = 0
  while v != 0UL {
    v = v & (v - 1UL)
    n = n + 1
  }
  n
}

///|
fn encode_into(out : Array[Byte], msg : Message) -> Unit {
  match msg {
    Input(f) => {
      out.push((1).to_byte())
      put_varint(out, f.player.to_uint64())
      put_varint(out, f.frame.to_uint64())
      put_varint(out, f.inputs.length().to_uint64())
      for bits in f.inputs {
        put_varint(out, bits)
      }
    }
    Confirm(f) => {
      out.push((2).to_byte())
      put_varint(out, f.frame.to_uint64())
    }
    Checksum(f) => {
      out.push((3).to_byte())
      put_varint(out, f.frame.to_uint64())
      put_u64(out, f.hash)
    }
    Active(f) => {
      out.push((4).to_byte())
      put_varint(out, f.player.to_uint64())
      put_varint(out, if f.active { 1UL } else { 0UL })
    }
    Span(f) => {
      out.push((5).to_byte())
      put_varint(out, f.player.to_uint64())
      put_varint(out, f.start.to_uint64())
      put_varint(out, f.count.to_uint64())
      put_varint(out, f.mask)
      for v in f.values {
        put_varint(out, v)
      }
    }
    Snap(f) => {
      out.push((6).to_byte())
      put_varint(out, f.frame.to_uint64())
      put_varint(out, f.state.length().to_uint64())
      let mut i = 0
      while i < f.state.length() {
        out.push(f.state[i])
        i = i + 1
      }
    }
    SnapRequest(f) => {
      out.push((7).to_byte())
      put_varint(out, f.frame.to_uint64())
    }
  }
}

///|
pub fn encode(msg : Message) -> Bytes {
  let out : Array[Byte] = []
  encode_into(out, msg)
  to_bytes(out)
}

///|
pub fn encode_batch(msgs : Array[Message]) -> Bytes {
  let out : Array[Byte] = []
  for m in msgs {
    encode_into(out, m)
  }
  to_bytes(out)
}

///|
fn decode_at(buf : Bytes, start : Int32) -> (Message, Int32)? {
  if start >= buf.length() {
    return None
  }
  let tag = buf[start].to_int()
  let pos = start + 1
  if tag == 1 {
    let (player, p1) = match get_varint(buf, pos) {
      Some(pair) => pair
      None => return None
    }
    let (frame, p2) = match get_varint(buf, p1) {
      Some(pair) => pair
      None => return None
    }
    let (count, p3) = match get_varint(buf, p2) {
      Some(pair) => pair
      None => return None
    }
    // 玩家索引 0..7,且 player + count 不能越过 8 人上限,否则视为损坏
    if player > 7UL || count > 8UL || player.to_int() + count.to_int() > 8 {
      return None
    }
    let inputs : Array[UInt64] = []
    let mut p = p3
    let mut i = 0
    while i < count.to_int() {
      match get_varint(buf, p) {
        Some((bits, np)) => {
          inputs.push(bits)
          p = np
          i = i + 1
        }
        None => return None
      }
    }
    Some(
      (Input({ player: player.to_int(), frame: frame.to_int(), inputs, }), p),
    )
  } else if tag == 2 {
    match get_varint(buf, pos) {
      Some((frame, p)) => Some((Confirm({ frame: frame.to_int(), }), p))
      None => None
    }
  } else if tag == 3 {
    match get_varint(buf, pos) {
      Some((frame, p1)) =>
        match get_u64(buf, p1) {
          Some(hash) =>
            Some((Checksum({ frame: frame.to_int(), hash, }), p1 + 8))
          None => None
        }
      None => None
    }
  } else if tag == 4 {
    let (player, p1) = match get_varint(buf, pos) {
      Some(pair) => pair
      None => return None
    }
    let (active, p2) = match get_varint(buf, p1) {
      Some(pair) => pair
      None => return None
    }
    // 玩家索引 0..7,状态只能是 0 / 1;其他一律视为损坏
    if player > 7UL || active > 1UL {
      return None
    }
    Some((Active({ player: player.to_int(), active: active == 1UL, }), p2))
  } else if tag == 5 {
    let (player, p1) = match get_varint(buf, pos) {
      Some(pair) => pair
      None => return None
    }
    let (start, p2) = match get_varint(buf, p1) {
      Some(pair) => pair
      None => return None
    }
    let (count, p3) = match get_varint(buf, p2) {
      Some(pair) => pair
      None => return None
    }
    let (mask, p4) = match get_varint(buf, p3) {
      Some(pair) => pair
      None => return None
    }
    // 玩家索引 0..7;帧号从 1 起;帧数 1..64(掩码只有 64 位);
    // 首帧必须显式,否则这段没有起点值;掩码不能超出帧数。
    if player > 7UL || start < 1UL || count < 1UL || count > 64UL {
      return None
    }
    if (mask & 1UL) == 0UL {
      return None
    }
    if count < 64UL && mask >> count.to_int() != 0UL {
      return None
    }
    let values : Array[UInt64] = []
    let mut p = p4
    let mut i = 0
    let want = popcount64(mask)
    while i < want {
      match get_varint(buf, p) {
        Some((bits, np)) => {
          values.push(bits)
          p = np
          i = i + 1
        }
        None => return None
      }
    }
    Some(
      (
        Span({
          player: player.to_int(),
          start: start.to_int(),
          count: count.to_int(),
          mask,
          values,
        }),
        p,
      ),
    )
  } else if tag == 6 {
    let (frame, p1) = match get_varint(buf, pos) {
      Some(pair) => pair
      None => return None
    }
    let (len, p2) = match get_varint(buf, p1) {
      Some(pair) => pair
      None => return None
    }
    // 帧号从 1 起;长度不能超过单包上限;声明的长度必须真的在包里
    if frame < 1UL || len > MAX_SNAPSHOT.to_uint64() {
      return None
    }
    let n = len.to_int()
    if p2 + n > buf.length() {
      return None
    }
    let state = Bytes::makei(n, fn(i) { buf[p2 + i] })
    Some((Snap({ frame: frame.to_int(), state, }), p2 + n))
  } else if tag == 7 {
    match get_varint(buf, pos) {
      Some((frame, p)) =>
        if frame < 1UL {
          None
        } else {
          Some((SnapRequest({ frame: frame.to_int(), }), p))
        }
      None => None
    }
  } else {
    None
  }
}

///|
pub fn decode(buf : Bytes) -> Message? {
  match decode_at(buf, 0) {
    Some((msg, _)) => Some(msg)
    None => None
  }
}

///|
pub fn decode_batch(buf : Bytes) -> Array[Message] {
  let out : Array[Message] = []
  let mut pos = 0
  while pos < buf.length() {
    match decode_at(buf, pos) {
      Some((msg, next)) => {
        out.push(msg)
        pos = next
      }
      None => break
    }
  }
  out
}