///|
/// A delayed event with a countdown measured in logical ticks.
pub(all) struct ScheduledEvent {
  id : Int
  event : Event
  remaining_ticks : Int
} derive(Eq, Debug)

///|
pub fn ScheduledEvent::new(
  id : Int,
  event : Event,
  delay_ticks : Int,
) -> ScheduledEvent {
  { id, event, remaining_ticks: delay_ticks }
}

///|
pub fn EventBus::schedule_in(
  self : EventBus,
  event : Event,
  delay_ticks : Int,
) -> EventBus {
  let scheduled : Array[ScheduledEvent] = []
  for item in self.scheduled {
    scheduled.push(item)
  }
  scheduled.push(ScheduledEvent::new(self.next_id, event, delay_ticks))
  { ..self, scheduled, next_id: self.next_id + 1 }.append_trace(
    TraceSchedule,
    event.name,
    self.name,
    "scheduled in " + delay_ticks.to_string() + " tick(s)",
  )
}

///|
pub fn EventBus::tick(self : EventBus) -> (EventBus, Array[DispatchReport]) {
  let due : Array[Event] = []
  let pending : Array[ScheduledEvent] = []
  for item in self.scheduled {
    let remaining = item.remaining_ticks - 1
    if remaining <= 0 {
      due.push(item.event)
    } else {
      pending.push({ ..item, remaining_ticks: remaining })
    }
  }
  let bus = { ..self, scheduled: pending }
  bus.publish_many(due)
}

///|
pub fn EventBus::tick_many(
  self : EventBus,
  count : Int,
) -> (EventBus, Array[DispatchReport]) {
  let all_reports : Array[DispatchReport] = []
  let mut bus = self
  let mut index = 0
  while index < count {
    let (next, reports) = bus.tick()
    bus = next
    for report in reports {
      all_reports.push(report)
    }
    index = index + 1
  }
  (bus, all_reports)
}