/// RFC packet-shape rules kept separate from the binary codec.

///|
pub fn request_authenticator_is_present(packet : Packet) -> Bool {
  packet.header.authenticator.length() == 16
}

///|
pub fn request_has_identifier(packet : Packet) -> Bool {
  packet.header.identifier.to_int() >= 0
}

///|
pub fn packet_code_name(code : Code) -> String {
  match code {
    AccessRequest => "Access-Request"
    AccessAccept => "Access-Accept"
    AccessReject => "Access-Reject"
    AccountingRequest => "Accounting-Request"
    AccountingResponse => "Accounting-Response"
    AccessChallenge => "Access-Challenge"
    StatusServer => "Status-Server"
    StatusClient => "Status-Client"
    Unknown(value) => "Unknown(" + value.to_string() + ")"
  }
}

///|
pub fn required_attributes_for(code : Code) -> Array[Byte] {
  match code {
    AccessRequest => [User_Name]
    AccountingRequest => [Acct_Status_Type, Acct_Session_Id]
    _ => []
  }
}

///|
pub fn missing_attributes(packet : Packet) -> Array[Byte] {
  let missing = []
  for kind in required_attributes_for(packet.header.code) {
    if packet.attribute(kind) is None {
      missing.push(kind)
    }
  }
  missing
}

///|
pub fn packet_is_request(packet : Packet) -> Bool {
  packet.header.code == AccessRequest ||
  packet.header.code == AccountingRequest ||
  packet.header.code == StatusServer
}

///|
pub fn packet_is_response(packet : Packet) -> Bool {
  packet.header.code == AccessAccept ||
  packet.header.code == AccessReject ||
  packet.header.code == AccessChallenge ||
  packet.header.code == AccountingResponse ||
  packet.header.code == StatusClient
}

///|
pub fn validate_wire_rules(packet : Packet) -> Array[String] {
  let errors = []
  if !request_authenticator_is_present(packet) {
    errors.push("authenticator must contain 16 bytes")
  }
  if packet_is_request(packet) && missing_attributes(packet).length() > 0 {
    errors.push("required request attributes are missing")
  }
  for attribute in packet.attributes {
    match validate_attribute(attribute) {
      Some(message) => errors.push(message)
      None => ()
    }
  }
  errors
}

///|
pub fn packet_is_well_formed(packet : Packet) -> Bool {
  validate_wire_rules(packet).length() == 0
}