// ============================================================
// Ordered event queue
//
// EventBus is ideal for one-shot flags. EventQueue preserves multiplicity and
// order, which is needed by input processing, damage notifications, and agent
// message delivery.
// ============================================================

///|
/// One ordered event with an optional typed payload.
pub struct EventRecord {
  id : Int
  name : String
  payload : Value?
}

///|
/// Create an event record.
pub fn EventRecord::new(
  id : Int,
  name : String,
  payload : Value?,
) -> EventRecord {
  { id, name, payload }
}

///|
/// Event sequence number.
pub fn EventRecord::id(self : EventRecord) -> Int {
  self.id
}

///|
/// Event name.
pub fn EventRecord::name(self : EventRecord) -> String {
  self.name
}

///|
/// Optional payload.
pub fn EventRecord::payload(self : EventRecord) -> Value? {
  self.payload
}

///|
/// FIFO event queue.
pub struct EventQueue {
  items : Array[EventRecord]
  next_id : Ref[Int]
}

///|
/// Create an empty queue.
pub fn EventQueue::new() -> EventQueue {
  { items: [], next_id: Ref::new(1) }
}

///|
/// Append an event without a payload.
pub fn EventQueue::emit(self : EventQueue, name : String) -> Int {
  let id = self.next_id.get()
  self.next_id.set(id + 1)
  self.items.push(EventRecord::new(id, name, None))
  id
}

///|
/// Append an event with a payload.
pub fn EventQueue::emit_value(
  self : EventQueue,
  name : String,
  payload : Value,
) -> Int {
  let id = self.next_id.get()
  self.next_id.set(id + 1)
  self.items.push(EventRecord::new(id, name, Some(payload)))
  id
}

///|
/// Number of pending events.
pub fn EventQueue::size(self : EventQueue) -> Int {
  self.items.length()
}

///|
/// Whether at least one event with this name is pending.
pub fn EventQueue::contains(self : EventQueue, name : String) -> Bool {
  for event in self.items {
    if event.name == name {
      return true
    }
  }
  false
}

///|
/// Remove and return the first event, if any.
pub fn EventQueue::poll(self : EventQueue) -> EventRecord? {
  if self.items.length() == 0 {
    None
  } else {
    let event = self.items[0]
    let _ = self.items.remove(0)
    Some(event)
  }
}

///|
/// Remove and return the first matching event.
pub fn EventQueue::poll_name(self : EventQueue, name : String) -> EventRecord? {
  let mut i = 0
  while i < self.items.length() {
    if self.items[i].name == name {
      let event = self.items[i]
      let _ = self.items.remove(i)
      return Some(event)
    }
    i = i + 1
  }
  None
}

///|
/// Copy pending events in FIFO order without consuming them.
pub fn EventQueue::pending(self : EventQueue) -> Array[EventRecord] {
  let copy : Array[EventRecord] = []
  for event in self.items {
    copy.push(event)
  }
  copy
}

///|
/// Remove every pending event and return the removed records.
pub fn EventQueue::drain(self : EventQueue) -> Array[EventRecord] {
  let result = self.pending()
  self.items.clear()
  result
}

///|
/// Discard pending events but preserve monotonically increasing IDs.
pub fn EventQueue::clear(self : EventQueue) -> Unit {
  self.items.clear()
}