///|
pub struct Events[N, T] {
  priv mut count : Int
  priv listeners : Map[N, Map[Int, (T) -> Unit]]
  priv middlewares : Map[Int, (N, T, () -> Unit) -> Unit]
}

///|
/// Executes the middlewares and the listeners for the given event and data.
fn[N, T] Events::exec(
  self : Events[N, T],
  event : N,
  data : T,
  executor : () -> Unit,
) -> Unit {
  let mut next = executor
  self.middlewares
  .values()
  .each(middleware => next = () => middleware(event, data, next))
  next()
}

///|
/// Creates a new instance of `Events`.
pub fn[N : Eq + Hash, T] Events::new() -> Events[N, T] {
  { count: 0, listeners: {}, middlewares: {} }
}

///|
/// Registers a listener for the given event.
pub fn[N : Eq + Hash, T] Events::on(
  self : Events[N, T],
  event : N,
  listener : (T) -> Unit,
  once? : Bool,
) -> () -> Unit {
  let map = self.listeners
    .get(event)
    .unwrap_or_else(() => {
      let map = {}
      self.listeners[event] = map
      map
    })
  let { count, .. } = self
  self.count += 1
  let off = () => map.remove(count)
  map[count] = if once == Some(true) {
    data => {
      data |> listener
      off()
    }
  } else {
    listener
  }
  off
}

///|
/// Registers a listener for the given event that will be executed only once.
pub fn[N : Eq + Hash, T] Events::once(
  self : Events[N, T],
  event : N,
  listener : (T) -> Unit,
) -> () -> Unit {
  self.on(event, listener, once=true)
}

///|
/// Emits the given event with the given data.
pub fn[N : Eq + Hash, T] Events::emit(
  self : Events[N, T],
  event : N,
  data : T,
) -> Unit {
  let listeners = self.listeners.get(event)
  guard listeners is Some(listeners)
  self.exec(event, data, () => listeners
    .values()
    .each(listener => data |> listener))
  // self.exec(event, data, () => listeners
  //   .values()
  //   .each(listener => data |> listener))
}

///|
/// Emits the given event with the given data to all listeners.
pub fn[N, T] Events::emitAll(self : Events[N, T], data : T) -> Unit {
  self.listeners.each((event, listeners) => self.exec(event, data, () => listeners
    .values()
    .each(listener => data |> listener)))
}

///|
/// Registers a middleware for the given event.
pub fn[N, T] Events::middleware(
  self : Events[N, T],
  middleware : (N, T, () -> Unit) -> Unit,
) -> () -> Unit {
  let { count, .. } = self
  self.count += 1
  self.middlewares[count] = middleware
  () => self.middlewares.remove(count)
}

///|
/// Checks if the given event has any listeners.
pub fn[N, T] Events::check(self : Events[N, T], event : N, data : T) -> Bool {
  let mut pass = false
  self.exec(event, data, () => pass = true)
  pass
}