///|
/// Hand everything one end has queued to the other, both ways, until neither has more.
fn shuttle(a : @http2.Conn, b : @http2.Conn) -> Unit raise {
  for ;; {
    let ab = a.take()
    let ba = b.take()
    if ab.length() == 0 && ba.length() == 0 {
      break
    }
    b.receive(ab[:])
    a.receive(ba[:])
  }
}

///|
/// The events waiting on one end, up to `NeedData`.
fn drain(conn : @http2.Conn) -> Array[@http2.Event] raise {
  let out = []
  for ;; {
    let event = conn.next()
    if event is NeedData {
      break
    }
    out.push(event)
  }
  out
}

///|
fn fields(list : Array[@header.Header]) -> String {
  list.map(f => f.ascii()).map(p => "\{p.0}: \{p.1}").join(", ")
}

///|
/// One HTTP/2 exchange as mooncat serves it, run in-process: two of moonhttp's connection
/// state machines pass octets to each other, the server end with mooncat's default settings.
/// The server turns the request head into a moonasgi request, the application answers, and the
/// answer goes back as a head and DATA. `serve_h2c_config` does the same over a socket, with
/// each stream in a task of its own.
///
///     moon run src/examples/06-http2-h2c --target native
#coverage.skip
fn main raise {
  let limits = @mooncat.H2Config::new()
  let server = @http2.Conn::new(
    Server,
    settings=@http2.Settings::new(
      max_concurrent_streams=Some(limits.max_streams),
      initial_window_size=limits.window,
      max_frame_size=limits.max_frame,
      max_header_list_size=Some(limits.max_header_list),
    ),
  )
  server.widen(0, limits.connection_window - @http2.initial.initial_window_size)
  let client = @http2.Conn::new(Client)
  let id = client.next_stream()
  client.headers(id, [
    @header.Header::of(":method", "GET"),
    @header.Header::of(":scheme", "http"),
    @header.Header::of(":path", "/hello"),
    @header.Header::of(":authority", "127.0.0.1"),
  ])
  shuttle(client, server)
  println(
    "server settings: \{limits.max_streams} streams, \{limits.window}-octet stream window, \{limits.max_frame}-octet frames",
  )
  let app : @moonasgi.Handler = req => {
    @moonasgi.Response::text("hello over h2c, path \{req.path}")
  }
  for event in drain(server) {
    guard event is Request(stream~, fields=head) else { continue }
    println("request on stream \{stream}: \{fields(head)}")
    let path = head
      .iter()
      .find_first(f => f.ascii().0 == ":path")
      .map(f => f.ascii().1)
      .unwrap_or("/")
    let resp = app({
      http_method: "GET",
      path,
      query_string: b"",
      headers: [],
      body: b"",
    })
    server.headers(stream, [
      @header.Header::of(":status", resp.status.to_string()),
      @header.Header::of("content-type", "text/plain"),
    ])
    server.data(stream, resp.body[:], end=true)
  }
  shuttle(client, server)
  for event in drain(client) {
    match event {
      Response(stream~, fields=head) =>
        println("response on stream \{stream}: \{fields(head)}")
      Data(data~, ..) => println("body: " + @utf8.decode_lossy(data))
      End(stream~) => println("stream \{stream} ended")
      _ => ()
    }
  }
}