///|
pub struct Peer {
  bgp_id : UInt
  address : String
  asn : UInt
} derive(ToJson)

///|
pub extend Peer with ToJson::{to_json}

///|
pub struct PeerTable {
  collector_id : UInt
  view_name : Bytes
  peers : Array[Peer]
}

///|
pub struct RouteEvent {
  schema_version : String
  source : Source?
  timestamp : UInt
  microseconds : Int?
  action : String
  prefix : String
  path_id : UInt?
  peer_asn : UInt
  peer_ip : String
  outbound : Bool
  originated : UInt?
  attributes : Attributes
} derive(ToJson)

///|
pub extend RouteEvent with ToJson::{to_json}

///|
pub fn parse_peers(data : Bytes) -> PeerTable raise ParseError {
  let c = Cursor::new(data)
  let collector_id = c.u32()
  let view_name = c.bytes(c.u16())
  let count = c.u16()
  let peers = []
  for i = 0; i < count; i = i + 1 {
    let flags = c.u8()
    if (flags & 252) != 0 {
      raise Invalid(c.pos - 1, "Peer Type 保留位非零")
    }
    let bgp_id = c.u32()
    let address = ip_text(c.bytes(if (flags & 1) == 0 { 4 } else { 16 }))
    let asn = if (flags & 2) == 0 {
      c.u16().reinterpret_as_uint()
    } else {
      c.u32()
    }
    peers.push({ bgp_id, address, asn, })
  }
  c.finish()
  { collector_id, view_name, peers, }
}

///|
/// TABLE_DUMP_V2 的 AS_PATH 始终使用四字节 ASN。
pub fn parse_rib(
  frame : Frame,
  peers : Array[Peer],
) -> Array[RouteEvent] raise ParseError {
  let afi = match frame.header.subtype {
    2 | 8 => 1
    4 | 10 => 2
    _ => raise Invalid(0, "只支持 IPv4/IPv6 单播 RIB 子类型")
  }
  if frame.header.kind != 13 {
    raise Invalid(0, "不是 TABLE_DUMP_V2")
  }
  let c = Cursor::new(frame.payload)
  ignore(c.u32())
  let prefix = read_prefix(c, afi).text()
  let count = c.u16()
  let events = []
  for i = 0; i < count; i = i + 1 {
    let index = c.u16()
    let peer = match peers.get(index) {
      Some(peer) => peer
      None => raise Invalid(c.pos - 2, "RIB 引用了无效 peer 索引")
    }
    let originated = c.u32()
    let path_id = if frame.header.subtype == 8 || frame.header.subtype == 10 {
      Some(c.u32())
    } else {
      None
    }
    let raw = c.bytes(c.u16())
    let attributes = rib_next_hop(parse_attributes(raw, 4), afi)
    events.push({
      schema_version: "2.0",
      source: None,
      timestamp: frame.header.timestamp,
      microseconds: frame.microseconds,
      action: "rib",
      prefix,
      path_id,
      peer_asn: peer.asn,
      peer_ip: peer.address,
      outbound: false,
      originated: Some(originated),
      attributes,
    })
  }
  c.finish()
  events
}