// The HTTP/2 frame layer (RFC 7540 §6): every one of the ten frame types encodes
// to its exact wire octets and decodes straight back. Each frame is a 9-octet
// header — 24-bit length, type, flags, 31-bit stream id — followed by a
// type-specific payload. This builds one of each, prints its bytes, and round-trips
// it through `decode_frame`, plus the standalone `@http2.FrameHeader` codec and the
// connection @http2.preface a server sniffs first.
//
//     moon run src/examples/02-http2-frames

///|
fn type_name(t : Int32) -> String {
  if t == @http2.frame_data {
    "DATA"
  } else if t == @http2.frame_headers {
    "HEADERS"
  } else if t == @http2.frame_priority {
    "PRIORITY"
  } else if t == @http2.frame_rst_stream {
    "RST_STREAM"
  } else if t == @http2.frame_settings {
    "SETTINGS"
  } else if t == @http2.frame_push_promise {
    "PUSH_PROMISE"
  } else if t == @http2.frame_ping {
    "PING"
  } else if t == @http2.frame_goaway {
    "GOAWAY"
  } else if t == @http2.frame_window_update {
    "WINDOW_UPDATE"
  } else if t == @http2.frame_continuation {
    "CONTINUATION"
  } else {
    "UNKNOWN"
  }
}

///|
fn roundtrip(frame : @http2.Frame) -> Unit {
  let wire = frame.encode()
  let name = type_name(frame.code())
  let status = try {
    let (back, consumed) = @http2.decode(wire)
    if back == frame && consumed == wire.length() {
      "round-trip ok"
    } else {
      "MISMATCH"
    }
  } catch {
    e => "decode raised \{e}"
  }
  println("\{name}: \{@base16.encode(wire)}  (\{status})")
}

///|
#coverage.skip
fn main {
  let prio : @http2.Priority = {
    exclusive: true,
    stream_dependency: 1,
    weight: 15,
  }
  // One frame of each of the ten types. A HEADERS fragment is opaque here — the
  // HPACK examples build a real one; the frame layer only carries the bytes.
  let frames : Array[@http2.Frame] = [
    Data(stream_id=1, data=b"qwq", end_stream=true, padding=4),
    Headers(
      stream_id=1,
      fragment=b"\x82\x86",
      end_stream=false,
      end_headers=true,
      priority=None,
      padding=0,
    ),
    @http2.Priority(stream_id=3, priority=prio),
    RstStream(stream_id=3, error_code=@http2.error_cancel),
    Settings(
      params=[
        (@http2.settings_max_concurrent_streams, 233),
        (@http2.settings_initial_window_size, 65535),
      ],
      ack=false,
    ),
    PushPromise(
      stream_id=1,
      promised_id=2,
      fragment=b"\x88",
      end_headers=true,
      padding=0,
    ),
    Ping(payload=b"heke1228", ack=false),
    GoAway(last_stream_id=23333, error_code=@http2.error_no_error, debug=b"bye"),
    WindowUpdate(stream_id=0, increment=66666),
    Continuation(stream_id=1, fragment=b"\x84", end_headers=true),
  ]
  for f in frames {
    roundtrip(f)
  }

  // A SETTINGS ack carries no payload; its ACK flag distinguishes it from the
  // parameter-bearing SETTINGS above.
  roundtrip(Settings(params=[], ack=true))

  // The bare 9-octet frame header round-trips on its own.
  let hdr : @http2.FrameHeader = {
    length: 3,
    ftype: @http2.frame_data,
    flags: @http2.flag_end_stream,
    stream_id: 1,
  }
  let hbytes = hdr.encode()
  let back = @http2.FrameHeader::decode(hbytes) catch { _ => hdr }
  println(
    "@http2.FrameHeader: \{@base16.encode(hbytes)}  (round-trip \{back == hdr})",
  )

  // A server reads the client connection @http2.preface before any frame (§3.5).
  println("preface: \{@base16.encode(@http2.preface)}")
  println("  present in a client stream: \{@http2.has_preface(@http2.preface)}")
  println(
    "  present in junk: \{@http2.has_preface(b"not a @http2.preface at all")}",
  )
}