export class ValueEmitter<T> {
  private listeners: Set<(value: T) => void> = new Set()

  static empty(): NullEmitter {
    return new ValueEmitter() as any
  }

  listen(listener: (value: T) => void): () => void {
    this.listeners.add(listener)
    return () => this.listeners.delete(listener)
  }

  clear() {
    this.listeners.clear()
  }

  emit = (value: T): void => {
    for (const listener of this.listeners) {
      listener(value)
    }
  }
}

export interface NullEmitter {
  listen(listener: () => void): () => void
  clear(): void
  emit(): void
}

export type EventMap = {
  [key: string]: any[]
}

export class TinyEventEmitter<Events extends EventMap> {
  #listeners = new Map<keyof Events, Set<(...args: any) => void>>()

  #getListeners(event: keyof Events) {
    let listeners = this.#listeners.get(event)
    if (!listeners) {
      listeners = new Set()
      this.#listeners.set(event, listeners)
    }
    return listeners
  }

  on<E extends keyof Events>(event: E, listener: (...args: Events[E]) => void): this {
    this.#getListeners(event).add(listener)
    return this
  }

  once<E extends keyof Events>(event: E, listener: (...args: Events[E]) => void): this {
    const once = (...args: Events[E]) => {
      this.off(event, once)
      listener(...args)
    }
    return this.on(event, once)
  }

  off<E extends keyof Events>(event: E, listener: (...args: Events[E]) => void): this {
    this.#getListeners(event).delete(listener)
    return this
  }

  emit<E extends keyof Events>(event: E, ...args: Events[E]): boolean {
    const listeners = this.#getListeners(event)
    for (const listener of listeners) {
      listener(...args)
    }
    return listeners.size > 0
  }
}

export { TinyEventEmitter as EventEmitter }
