use std::collections::BTreeMap;

use crate::events::{ProcessEvent, ProcessEventKind};

type ProcessEventListener = Box<dyn FnMut(&ProcessEvent)>;

#[derive(Default)]
pub struct ProcessEventBus {
    listeners: BTreeMap<ProcessEventKind, Vec<ProcessEventListener>>,
    any_listeners: Vec<ProcessEventListener>,
}

impl ProcessEventBus {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn add_listener<F>(&mut self, kind: ProcessEventKind, listener: F)
    where
        F: FnMut(&ProcessEvent) + 'static,
    {
        self.listeners
            .entry(kind)
            .or_default()
            .push(Box::new(listener));
    }

    pub fn prepend_listener<F>(&mut self, kind: ProcessEventKind, listener: F)
    where
        F: FnMut(&ProcessEvent) + 'static,
    {
        self.listeners
            .entry(kind)
            .or_default()
            .insert(0, Box::new(listener));
    }

    pub fn add_any_listener<F>(&mut self, listener: F)
    where
        F: FnMut(&ProcessEvent) + 'static,
    {
        self.any_listeners.push(Box::new(listener));
    }

    pub fn publish(&mut self, event: &ProcessEvent) {
        if let Some(listeners) = self.listeners.get_mut(&event.kind) {
            for listener in listeners {
                listener(event);
            }
        }
        for listener in &mut self.any_listeners {
            listener(event);
        }
    }
}
