///|
/// Validate invariants that are independent of a concrete transport.
pub fn validate_frame(frame : Frame) -> Result[Unit, String] {
  if frame.send_sequence < 0 || frame.send_sequence >= 32768 {
    return Err("send sequence must fit 15 bits")
  }
  if frame.receive_sequence < 0 || frame.receive_sequence >= 32768 {
    return Err("receive sequence must fit 15 bits")
  }
  if frame.payload.length() > 249 {
    return Err("APDU payload exceeds 249 bytes")
  }
  match frame.kind {
    Information =>
      if frame.payload.is_empty() {
        Err("I frame payload must not be empty")
      } else {
        Ok(())
      }
    Supervisory =>
      if frame.payload.is_empty() {
        Ok(())
      } else {
        Err("S frame cannot contain an ASDU")
      }
    Unnumbered => {
      let control = frame.control.to_int()
      if (control & 1) != 0 && (control & 2) != 0 {
        Ok(())
      } else {
        Err("invalid U frame control bits")
      }
    }
  }
}

///|
/// A protocol event useful for deterministic simulation and diagnostics.
pub enum ProtocolEvent {
  Connected
  Started
  Stopped
  Sent(Int)
  Received(Int)
  Acknowledged(Int)
  Fault(String)
} derive(Eq, Debug)

///|
/// Standard event constructors for host integrations.
pub fn protocol_event_examples() -> Array[ProtocolEvent] {
  [
    Connected,
    Started,
    Stopped,
    Sent(0),
    Received(0),
    Acknowledged(0),
    Fault("example"),
  ]
}

///|
/// In-memory event log for examples, tests and host applications.
pub struct EventLog {
  events : Array[ProtocolEvent]
}

///|
pub fn EventLog::new() -> EventLog {
  { events: [] }
}

///|
pub fn EventLog::push(self : EventLog, event : ProtocolEvent) -> Unit {
  self.events.push(event)
}

///|
pub fn EventLog::len(self : EventLog) -> Int {
  self.events.length()
}

///|
pub fn EventLog::all(self : EventLog) -> Array[ProtocolEvent] {
  self.events.copy()
}