use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::collections::HashMap;

pub trait Event: std::fmt::Debug {}

type EventHandler<T> = Box<dyn FnMut(&T)>;

pub struct EventBus
{
    handlers: HashMap<TypeId, Box<dyn Any>>,
}

impl EventBus
{
    fn new() -> Self
    {
        Self {
            handlers: HashMap::new(),
        }
    }

    fn subscribe_internal<T: Event + 'static, F: FnMut(&T) + 'static>(&mut self, handler: F)
    {
        let type_id = TypeId::of::<T>();
        let handlers: &mut Vec<EventHandler<T>> = self
            .handlers
            .entry(type_id)
            .or_insert_with(|| Box::new(Vec::<EventHandler<T>>::new()))
            .downcast_mut()
            .unwrap();

        handlers.push(Box::new(handler));
    }

    fn publish_internal<T: Event + 'static>(&mut self, event: &T)
    {
        let type_id = TypeId::of::<T>();

        if let Some(handlers) = self.handlers.get_mut(&type_id)
        {
            let typed_handlers = handlers.downcast_mut::<Vec<EventHandler<T>>>().unwrap();
            for handler in typed_handlers
            {
                handler(event);
            }
        }
    }

    pub fn subscribe<T: Event + 'static, F: FnMut(&T) + 'static>(handler: F)
    {
        EVENT_BUS.with(|bus| bus.borrow_mut().subscribe_internal(handler));
    }

    pub fn publish<T: Event + 'static>(event: &T)
    {
        EVENT_BUS.with(|bus| bus.borrow_mut().publish_internal(event));
    }
}

thread_local! {
    static EVENT_BUS: RefCell<EventBus> = RefCell::new(EventBus::new());
}

#[derive(Debug, Clone)]
pub struct UpdateEvent
{
    pub delta: f32,
}
impl Event for UpdateEvent {}
