// Binding the QUIC datagram transport to moonbitlang/async's UDP socket (RFC 9000 §5): a
// QUIC endpoint is a UDP socket that receives and sends whole datagrams, each carrying one
// or more QUIC packets. This wraps `@socket.UdpServer` — which can both receive from and
// send to arbitrary peers, exactly the connectionless model QUIC needs — behind the two
// operations the connection loop drives: pull the next datagram (with its sender's address)
// and push a datagram to a peer. The packet protection, handshake, and HTTP/3 codecs run on
// the bytes these move; this is the seam where they meet a real socket.

///|
/// A QUIC UDP endpoint: a bound datagram socket.
pub struct QuicUdpEndpoint {
  server : @socket.UdpServer
}

///|
/// Bind an endpoint to `addr` (e.g. `"0.0.0.0:443"`, or `"127.0.0.1:0"` for an OS-assigned
/// port). The bound address is available from `local_addr`.
pub async fn QuicUdpEndpoint::bind(addr : String) -> QuicUdpEndpoint {
  { server: @socket.UdpServer::UdpServer(@socket.Addr::parse(addr)), }
}

///|
/// The address the endpoint is bound to (the concrete port when bound to port 0).
pub fn QuicUdpEndpoint::local_addr(self : QuicUdpEndpoint) -> @socket.Addr {
  self.server.addr
}

///|
/// Receive the next datagram and its sender's address, copying up to `max` bytes.
pub async fn QuicUdpEndpoint::recv_datagram(
  self : QuicUdpEndpoint,
  max? : Int = 2048,
) -> (Bytes, @socket.Addr) {
  let buf = FixedArray::make(max, b'\x00')
  let (n, from) = self.server.recvfrom(buf)
  let out = Buffer()
  for i = 0; i < n; i = i + 1 {
    out.write_byte(buf[i])
  }
  (out.to_bytes(), from)
}

///|
/// Send `data` as a single datagram to `to`.
pub async fn QuicUdpEndpoint::send_datagram(
  self : QuicUdpEndpoint,
  data : Bytes,
  to : @socket.Addr,
) -> Unit {
  self.server.sendto(data, to)
}

///|
/// Close the endpoint's socket.
pub fn QuicUdpEndpoint::close(self : QuicUdpEndpoint) -> Unit {
  self.server.close()
}