///|
/// AR lifecycle states (PN-AL-Services ยง4.7).
pub(all) enum ArState {
  Closed
  Init
  Open
  Active
} derive(Eq, Debug)

///|
/// AR (Application Relationship) block request.
pub(all) struct ARBlockReq {
  ar_type : Int
  ar_uuid : @rpc.Uuid
  session_key : Int
  cm_initiator_mac : @frame.MacAddress
  cm_initiator_object_uuid : @rpc.Uuid
  ar_properties : Int
  cm_initiator_activity_timeout_factor : Int
  cm_initiator_udp_rt_port : Int
  cm_initiator_station_name : String
} derive(Eq)

///|
fn clrpc_cm_initiator_object_uuid() -> @rpc.Uuid {
  @rpc.Uuid::{
    bytes: Bytes::from_array([
      b'\xDE', b'\xA0', b'\x00', b'\x00', b'\x6C', b'\x97', b'\x11', b'\xD1', b'\x82',
      b'\x71', b'\x00', b'\xA0', b'\x24', b'\x42', b'\xDF', b'\x7D',
    ]),
  }
}

///|
pub fn ARBlockReq::default(
  ar_uuid : @rpc.Uuid,
  cm_mac : @frame.MacAddress,
  station_name : String,
) -> ARBlockReq {
  ARBlockReq::{
    ar_type: ar_type_io_controller,
    ar_uuid,
    session_key: 0,
    cm_initiator_mac: cm_mac,
    cm_initiator_object_uuid: clrpc_cm_initiator_object_uuid(),
    ar_properties: 0x00000011,
    cm_initiator_activity_timeout_factor: 600,
    cm_initiator_udp_rt_port: 0x8892,
    cm_initiator_station_name: station_name,
  }
}

///|
/// Encode AR block request body (without block header).
pub fn encode_ar_block_req(req : ARBlockReq) -> Bytes {
  let output : Array[Byte] = []
  push_u16_be(output, req.ar_type)
  append_bytes(output, req.ar_uuid.to_dce_bytes_le())
  push_u16_be(output, req.session_key)
  append_bytes(output, req.cm_initiator_mac.octets())
  append_bytes(output, req.cm_initiator_object_uuid.to_bytes())
  push_u32_be(output, req.ar_properties)
  push_u16_be(output, req.cm_initiator_activity_timeout_factor)
  push_u16_be(output, req.cm_initiator_udp_rt_port)
  let name_bytes = req.cm_initiator_station_name
    .to_array()
    .map(fn(c) { c.to_int().to_byte() })
  push_u16_be(output, name_bytes.length())
  for b in name_bytes {
    output.push(b)
  }
  if name_bytes.length() % 2 == 1 {
    output.push(b'\x00')
  }
  Bytes::from_array(output)
}

///|
/// AR block response.
pub(all) struct ARBlockRes {
  ar_type : Int
  ar_uuid : @rpc.Uuid
  session_key : Int
  cm_responder_mac : @frame.MacAddress
  cm_responder_udp_rt_port : Int
} derive(Eq)

///|
/// Parse AR block response from body bytes.
pub fn parse_ar_block_res(
  data : Bytes,
  offset : Int,
) -> ARBlockRes raise @frame.FrameError {
  guard data.length() >= offset + 28 else {
    raise @frame.FrameError::InvalidMacLength(data.length())
  }
  let mac_bytes : Array[Byte] = []
  for i = 0; i < 6; i = i + 1 {
    mac_bytes.push(data[offset + 20 + i])
  }
  ARBlockRes::{
    ar_type: read_u16_be(data, offset),
    ar_uuid: @rpc.Uuid::from_dce_bytes_le(data, offset + 2),
    session_key: read_u16_be(data, offset + 18),
    cm_responder_mac: @frame.MacAddress::new(Bytes::from_array(mac_bytes)),
    cm_responder_udp_rt_port: read_u16_be(data, offset + 26),
  }
}

///|
/// Format AR block request summary.
pub fn format_ar_block_req(req : ARBlockReq) -> String {
  let lines : Array[String] = []
  lines.push("ar_type=" + req.ar_type.to_string())
  lines.push("ar_uuid=" + req.ar_uuid.to_hex())
  lines.push("session_key=" + req.session_key.to_string())
  lines.push("cm_mac=" + req.cm_initiator_mac.to_hex())
  lines.push("station_name=" + req.cm_initiator_station_name)
  lines.push("ar_properties=0x" + @frame.uint32_to_hex(req.ar_properties))
  lines.join("\n")
}