// ===== encode:EtfTerm → ETF 字节流(带 VERSION_MAGIC)=====
// 规范形输出:atom→'w'(SMALL_ATOM_UTF8,OTP26 主形);float→'F';list→108+106;
// 整数→97(<256)/98;pid→'X';ref→'Z'(creation u32)。

///|
pub fn encode(t : EtfTerm) -> Bytes raise EtfError {
  let out : Array[Byte] = [VERSION_MAGIC.to_byte()]
  encode_term(t, out)
  Bytes::from_array(out)
}

///|
/// 段内编码(无 magic):组合用
pub fn encode_term(t : EtfTerm, out : Array[Byte]) -> Unit raise EtfError {
  match t {
    EtfTerm::EAtom(s) => encode_atom(s, out)
    EtfTerm::EInt(v) =>
      if v >= 0 && v < 256 {
        out.push(TAG_SMALL_INTEGER.to_byte())
        out.push((v & 255).to_byte())
      } else {
        out.push(TAG_INTEGER.to_byte())
        put_u32(out, v)
      }
    EtfTerm::EFloat(f) => {
      out.push(TAG_NEW_FLOAT.to_byte())
      put_u64(out, f.reinterpret_as_uint64())
    }
    EtfTerm::ETuple(items) => {
      out.push(TAG_SMALL_TUPLE.to_byte())
      out.push(items.length().to_byte())
      for it in items {
        encode_term(it, out)
      }
    }
    EtfTerm::EList(items) => {
      out.push(TAG_LIST.to_byte())
      put_u32(out, items.length())
      for it in items {
        encode_term(it, out)
      }
      out.push(TAG_NIL.to_byte())
    }
    EtfTerm::ENil => out.push(TAG_NIL.to_byte())
    EtfTerm::EString(s) => {
      out.push(TAG_STRING.to_byte())
      put_u16(out, s.length())
      for ch in s {
        out.push((ch.to_int() & 255).to_byte())
      }
    }
    EtfTerm::EBinary(b) => {
      out.push(TAG_BINARY.to_byte())
      put_u32(out, b.length())
      for byte in b {
        out.push(byte)
      }
    }
    EtfTerm::EMap(pairs) => {
      out.push(TAG_MAP.to_byte())
      put_u32(out, pairs.length())
      for pair in pairs {
        encode_term(pair.0, out)
        encode_term(pair.1, out)
      }
    }
    EtfTerm::EPid(node, id, serial, creation) => {
      out.push(TAG_NEW_PID.to_byte())
      encode_atom(node, out)
      put_u32(out, id)
      put_u32(out, serial)
      put_u32(out, creation)
    }
    EtfTerm::EPort(node, id, creation) => {
      out.push(TAG_NEW_PORT.to_byte())
      encode_atom(node, out)
      put_u32(out, id)
      put_u32(out, creation)
    }
    EtfTerm::ERef(node, id, _, creation) => {
      // 'Z' NEW_REFERENCE:idlen=1(骨架 v0 单字 id)
      out.push(TAG_NEW_REFERENCE.to_byte())
      put_u16(out, 1)
      encode_atom(node, out)
      put_u32(out, creation)
      put_u32(out, id)
    }
  }
}

///|
/// atom 规范形:'w'(len u8;>255 退 'v' len u16)
fn encode_atom(s : String, out : Array[Byte]) -> Unit raise EtfError {
  let n = s.length()
  if n < 256 {
    out.push(TAG_SMALL_ATOM_UTF8.to_byte())
    out.push(n.to_byte())
  } else {
    out.push(TAG_ATOM_UTF8.to_byte())
    put_u16(out, n)
  }
  for ch in s {
    let code = ch.to_int()
    if code > 255 {
      raise EtfError::Malformed("atom 非 latin1 域(骨架 v0)")
    }
    out.push((code & 255).to_byte())
  }
}

///|
fn put_u16(out : Array[Byte], v : Int32) -> Unit {
  out.push(((v >> 8) & 255).to_byte())
  out.push((v & 255).to_byte())
}

///|
fn put_u32(out : Array[Byte], v : Int32) -> Unit {
  out.push(((v >> 24) & 255).to_byte())
  out.push(((v >> 16) & 255).to_byte())
  out.push(((v >> 8) & 255).to_byte())
  out.push((v & 255).to_byte())
}

///|
fn put_u64(out : Array[Byte], v : UInt64) -> Unit {
  for k in 0..<8 {
    let sh = 56 - k * 8
    out.push(((v >> sh).to_int() & 255).to_byte())
  }
}