///|
/// DCE/RPC header — 80 bytes.
pub(all) struct DceHeader {
  version : Byte
  packet_type : Byte
  flags1 : Byte
  flags2 : Byte
  drep : Bytes
  serial_high : Byte
  object_uuid : Uuid
  interface_uuid : Uuid
  activity_uuid : Uuid
  server_boot_time : Int
  interface_version : Int
  sequence_number : Int
  opnum : Int
  interface_hint : Int
  activity_hint : Int
  body_length : Int
  fragment_number : Int
  auth_proto : Byte
  serial_low : Byte
} derive(Eq, Debug)

///|
pub fn DceHeader::default() -> DceHeader {
  DceHeader::{
    version: b'\x04',
    packet_type: packet_type_request,
    flags1: b'\x20',
    flags2: b'\x00',
    drep: Bytes::from_array([b'\x10', b'\x00', b'\x00']),
    serial_high: b'\x00',
    object_uuid: Uuid::empty(),
    interface_uuid: Uuid::empty(),
    activity_uuid: Uuid::empty(),
    server_boot_time: 0,
    interface_version: 3,
    sequence_number: 1111,
    opnum: 0,
    interface_hint: 0xFFFF,
    activity_hint: 0xFFFF,
    body_length: 0,
    fragment_number: 0,
    auth_proto: b'\x00',
    serial_low: b'\x00',
  }
}

///|
/// Encode a DCE/RPC header to bytes (80 bytes).
pub fn encode_dce_header(hdr : DceHeader) -> Bytes {
  let output : Array[Byte] = []
  output.push(hdr.version)
  output.push(hdr.packet_type)
  output.push(hdr.flags1)
  output.push(hdr.flags2)
  append_bytes(output, hdr.drep)
  output.push(hdr.serial_high)
  append_bytes(output, hdr.object_uuid.to_dce_bytes_le())
  append_bytes(output, hdr.interface_uuid.to_dce_bytes_le())
  append_bytes(output, hdr.activity_uuid.to_dce_bytes_le())
  push_u32_le(output, hdr.server_boot_time)
  push_u32_le(output, hdr.interface_version)
  push_u32_le(output, hdr.sequence_number)
  push_u16_le(output, hdr.opnum)
  push_u16_le(output, hdr.interface_hint)
  push_u16_le(output, hdr.activity_hint)
  push_u16_le(output, hdr.body_length)
  push_u16_le(output, hdr.fragment_number)
  output.push(hdr.auth_proto)
  output.push(hdr.serial_low)
  Bytes::from_array(output)
}

///|
/// Parse a DCE/RPC header from bytes (requires at least 80 bytes from offset).
pub fn parse_dce_header(
  data : Bytes,
  offset : Int,
) -> DceHeader raise @frame.FrameError {
  guard data.length() >= offset + 80 else {
    raise @frame.FrameError::InvalidMacLength(data.length())
  }
  let drep = Bytes::from_array([
    data[offset + 4],
    data[offset + 5],
    data[offset + 6],
  ])
  DceHeader::{
    version: data[offset],
    packet_type: data[offset + 1],
    flags1: data[offset + 2],
    flags2: data[offset + 3],
    drep,
    serial_high: data[offset + 7],
    object_uuid: Uuid::from_dce_bytes_le(data, offset + 8),
    interface_uuid: Uuid::from_dce_bytes_le(data, offset + 24),
    activity_uuid: Uuid::from_dce_bytes_le(data, offset + 40),
    server_boot_time: read_u32_le(data, offset + 56),
    interface_version: read_u32_le(data, offset + 60),
    sequence_number: read_u32_le(data, offset + 64),
    opnum: read_u16_le(data, offset + 68),
    interface_hint: read_u16_le(data, offset + 70),
    activity_hint: read_u16_le(data, offset + 72),
    body_length: read_u16_le(data, offset + 74),
    fragment_number: read_u16_le(data, offset + 76),
    auth_proto: data[offset + 78],
    serial_low: data[offset + 79],
  }
}

///|
/// Format DCE header as human-readable summary.
pub fn format_dce_header(hdr : DceHeader) -> String {
  let lines : Array[String] = []
  lines.push("version=" + hdr.version.to_int().to_string())
  lines.push("packet_type=" + packet_type_label(hdr.packet_type))
  lines.push("object_uuid=" + hdr.object_uuid.to_hex())
  lines.push("interface_uuid=" + hdr.interface_uuid.to_hex())
  lines.push("activity_uuid=" + hdr.activity_uuid.to_hex())
  lines.push("sequence_number=" + hdr.sequence_number.to_string())
  lines.push("opnum=" + hdr.opnum.to_string())
  lines.push("body_length=" + hdr.body_length.to_string())
  lines.join("\n")
}

///|
/// IO Request structure — wraps RPC payload blocks.
pub(all) struct IoRequest {
  args_maximum : Int
  args_length : Int
  max_count : Int
  offset : Int
  actual_count : Int
  payload : Bytes
} derive(Eq, Debug)

///|
pub fn IoRequest::default() -> IoRequest {
  IoRequest::{
    args_maximum: 0xFE4,
    args_length: 0,
    max_count: 0xFE4,
    offset: 0,
    actual_count: 0,
    payload: Bytes::default(),
  }
}

///|
/// Encode an IO Request wrapper (20-byte header + payload).
pub fn encode_io_request(req : IoRequest) -> Bytes {
  let output : Array[Byte] = []
  push_u32_le(output, req.args_maximum)
  push_u32_le(output, req.args_length)
  push_u32_le(output, req.max_count)
  push_u32_le(output, req.offset)
  push_u32_le(output, req.actual_count)
  append_bytes(output, req.payload)
  Bytes::from_array(output)
}

///|
/// Parse an IO Request wrapper from bytes.
pub fn parse_io_request(
  data : Bytes,
  offset : Int,
) -> IoRequest raise @frame.FrameError {
  guard data.length() >= offset + 20 else {
    raise @frame.FrameError::InvalidMacLength(data.length())
  }
  let actual_count = read_u32_le(data, offset + 16)
  let payload_end = if data.length() > offset + 20 + actual_count {
    offset + 20 + actual_count
  } else {
    data.length()
  }
  let payload_bytes : Array[Byte] = []
  for i = offset + 20; i < payload_end; i = i + 1 {
    payload_bytes.push(data[i])
  }
  IoRequest::{
    args_maximum: read_u32_le(data, offset),
    args_length: read_u32_le(data, offset + 4),
    max_count: read_u32_le(data, offset + 8),
    offset: read_u32_le(data, offset + 12),
    actual_count,
    payload: Bytes::from_array(payload_bytes),
  }
}

///|
/// IO Response structure — wraps RPC response blocks.
pub(all) struct IoResponse {
  status : Int
  args_length : Int
  max_count : Int
  offset : Int
  actual_count : Int
  payload : Bytes
} derive(Eq, Debug)

///|
/// Encode an IO Response wrapper (20-byte header + payload).
pub fn encode_io_response(res : IoResponse) -> Bytes {
  let output : Array[Byte] = []
  push_u32_le(output, res.status)
  push_u32_le(output, res.args_length)
  push_u32_le(output, res.max_count)
  push_u32_le(output, res.offset)
  push_u32_le(output, res.actual_count)
  append_bytes(output, res.payload)
  Bytes::from_array(output)
}

///|
/// Parse an IO Response wrapper from bytes.
pub fn parse_io_response(
  data : Bytes,
  offset : Int,
) -> IoResponse raise @frame.FrameError {
  guard data.length() >= offset + 20 else {
    raise @frame.FrameError::InvalidMacLength(data.length())
  }
  let actual_count = read_u32_le(data, offset + 16)
  let payload_end = if data.length() > offset + 20 + actual_count {
    offset + 20 + actual_count
  } else {
    data.length()
  }
  let payload_bytes : Array[Byte] = []
  for i = offset + 20; i < payload_end; i = i + 1 {
    payload_bytes.push(data[i])
  }
  IoResponse::{
    status: read_u32_le(data, offset),
    args_length: read_u32_le(data, offset + 4),
    max_count: read_u32_le(data, offset + 8),
    offset: read_u32_le(data, offset + 12),
    actual_count,
    payload: Bytes::from_array(payload_bytes),
  }
}