///|
/// A tiny in-memory queue abstraction used by examples and simple applications.
pub(all) enum QueueTake {
  Empty
  Item(Event)
} derive(Eq, Debug)

///|
pub(all) struct EventQueue {
  events : Array[Event]
} derive(Eq, Debug)

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

///|
pub fn EventQueue::from(events : Array[Event]) -> EventQueue {
  { events, }
}

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

///|
pub fn EventQueue::is_empty(self : EventQueue) -> Bool {
  self.events.length() == 0
}

///|
pub fn EventQueue::push(self : EventQueue, event : Event) -> EventQueue {
  let next : Array[Event] = []
  for old in self.events {
    next.push(old)
  }
  next.push(event)
  { events: next }
}

///|
pub fn EventQueue::push_many(
  self : EventQueue,
  events : Array[Event],
) -> EventQueue {
  let mut next = self
  for event in events {
    next = next.push(event)
  }
  next
}

///|
pub fn EventQueue::peek(self : EventQueue) -> Event? {
  self.events.get(0)
}

///|
pub fn EventQueue::take(self : EventQueue) -> (QueueTake, EventQueue) {
  match self.events.get(0) {
    None => (Empty, self)
    Some(first) => {
      let rest : Array[Event] = []
      let mut index = 0
      for event in self.events {
        if index > 0 {
          rest.push(event)
        }
        index = index + 1
      }
      (Item(first), { events: rest })
    }
  }
}

///|
pub fn EventQueue::drain(
  self : EventQueue,
  subscriptions : Array[Subscription],
) -> Array[DispatchReport] {
  let reports : Array[DispatchReport] = []
  for event in self.events {
    reports.push(dispatch(event, subscriptions))
  }
  reports
}

///|
pub fn EventQueue::drain_bus(
  self : EventQueue,
  bus : EventBus,
) -> (EventBus, Array[DispatchReport]) {
  bus.publish_many(self.events)
}

///|
pub fn EventQueue::filter_topic(
  self : EventQueue,
  topic : String,
) -> EventQueue {
  let kept : Array[Event] = []
  for event in self.events {
    if event.topic == topic {
      kept.push(event)
    }
  }
  { events: kept }
}

///|
pub fn EventQueue::filter_tag(self : EventQueue, tag : String) -> EventQueue {
  let kept : Array[Event] = []
  for event in self.events {
    if event.has_tag(tag) {
      kept.push(event)
    }
  }
  { events: kept }
}

///|
pub fn EventQueue::highest_priority(self : EventQueue) -> Event? {
  let mut best : Event? = None
  for event in self.events {
    match best {
      None => best = Some(event)
      Some(current) =>
        if event.priority > current.priority {
          best = Some(event)
        }
    }
  }
  best
}