///|
/// Two connections wired through a pair of one-way links.
///
/// The wiring is the interesting part. A connection cannot be handed a link at
/// construction time, because the link needs the *other* connection in its
/// delivery callback, and that connection does not exist yet. Instead each
/// connection starts with a no-op sender and is connected to its link
/// afterwards, which keeps the dependency a straight line rather than a cycle
/// and leaves the wiring visible in one place.
pub(all) struct LinkPair {
  client : TcpConnection
  server : TcpConnection
  up : @net.Link[Segment]
  down : @net.Link[Segment]
}

///|
pub fn LinkPair::new(
  sim : @sim.Sim,
  client_config : TcpConfig,
  server_config : TcpConfig,
  client_to_server : @net.LinkSpec,
  server_to_client : @net.LinkSpec,
  seed : UInt64,
) -> LinkPair {
  let client = TcpConnection::new(client_config)
  let server = TcpConnection::new(server_config)
  LinkPair::build(sim, client, server, client_to_server, server_to_client, seed)
}

///|
/// The same wiring, with a chosen congestion control algorithm on the client.
///
/// Only the client is given one because only the client sends bulk data here;
/// the server's algorithm would never be consulted. Handing the same object to
/// both ends would be a mistake anyway: the window and threshold are per
/// connection, so sharing one would let the two ends corrupt each other's state.
pub fn LinkPair::new_with_cc(
  sim : @sim.Sim,
  client_config : TcpConfig,
  server_config : TcpConfig,
  client_to_server : @net.LinkSpec,
  server_to_client : @net.LinkSpec,
  seed : UInt64,
  cc : &CongestionControl,
) -> LinkPair {
  let client = TcpConnection::new_with_cc(client_config, cc)
  let server = TcpConnection::new(server_config)
  LinkPair::build(sim, client, server, client_to_server, server_to_client, seed)
}

///|
/// Wire two existing connections through a pair of links.
fn LinkPair::build(
  sim : @sim.Sim,
  client : TcpConnection,
  server : TcpConnection,
  client_to_server : @net.LinkSpec,
  server_to_client : @net.LinkSpec,
  seed : UInt64,
) -> LinkPair {
  let up = @net.Link::new(client_to_server, seed, packet => {
    server.on_segment(sim, packet.payload)
  })
  let down = @net.Link::new(server_to_client, seed, packet => {
    client.on_segment(sim, packet.payload)
  })
  let mut packet_id = 0
  client.set_emit(segment => {
    packet_id = packet_id + 1
    let packet = @net.Packet::new(
      packet_id,
      0,
      @net.Data,
      segment.wire_size(),
      segment.tag,
      segment,
    )
    up.send(sim, packet) |> ignore
  })
  server.set_emit(segment => {
    packet_id = packet_id + 1
    let packet = @net.Packet::new(
      packet_id,
      0,
      @net.Data,
      segment.wire_size(),
      segment.tag,
      segment,
    )
    down.send(sim, packet) |> ignore
  })
  { client, server, up, down, }
}

///|
/// Start the handshake from the client side.
pub fn LinkPair::connect(self : LinkPair, sim : @sim.Sim) -> Unit {
  self.server.listen()
  self.client.connect(sim)
}

///|
/// A one-line-per-end summary.
pub fn LinkPair::summary(self : LinkPair) -> String {
  "client " +
  self.client.state().render() +
  " snd_una=" +
  self.client.snd_una().to_string() +
  " rcv_nxt=" +
  self.client.rcv_nxt().to_string() +
  " | server " +
  self.server.state().render() +
  " snd_una=" +
  self.server.snd_una().to_string() +
  " rcv_nxt=" +
  self.server.rcv_nxt().to_string()
}