//! APIs to handle runtime-based events.

use std::{
    collections::HashMap,
    any::{Any, TypeId},
};
use super::world::World;

/// An in-memory publisher-subscriber eventing system.
#[derive(Default)]
pub struct Events {
    /// Contains [`EventHandlerChannel`]s keyed by the event they're triggered by.
    channels: HashMap<TypeId, Box<dyn ErasedEventChannel>>,
}

impl Events {
    /// Registers a system which responds to a specific event.
    pub fn register<TEvent, TEventHandler>(&mut self, handler: TEventHandler)
        where
            TEvent: 'static,
            TEventHandler: EventHandler<TEvent> + 'static,
    {
        let id = TypeId::of::<TEvent>();

        self.channels
            .entry(id)
            .or_insert_with(|| {
                let handler = EventChannel::<TEvent>::new();

                Box::new(handler)
            })
            .as_any_mut()
            .downcast_mut::<EventChannel<TEvent>>()
            .map(|c| c.register(handler));
    }

    /// Queues an event.
    ///
    /// The event is dropped if there are no registered event handlers for it.
    /// Event handlers will be called at the end of the current execution context.
    pub fn trigger<TEvent>(&self, event: &TEvent, world: &mut World)
        where
            TEvent: 'static,
    {
        let id = TypeId::of::<TEvent>();

        self.channels
            .get(&id)
            .map(|c| c.as_any())
            .and_then(|c| c.downcast_ref::<EventChannel<TEvent>>())
            .map(|c| c.trigger(event, world));
    }
}

/// A system which responds to an event.
pub trait EventHandler<T> {
    /// Executes the system with an event.
    fn trigger(&self, event: &T, world: &mut World);
}

impl<TFunc, TEvent> EventHandler<TEvent> for TFunc
    where
        TFunc: Fn(&TEvent, &mut World),
{
    fn trigger(&self, event: &TEvent, world: &mut World) {
        self(event, world)
    }
}

/// Contains event handlers of a specific type.
struct EventChannel<T> {
    /// Registered handlers for the event channel.
    handlers: Vec<Box<dyn EventHandler<T>>>,
}

impl<T> EventChannel<T> {
    /// Initializes a new, empty instance of [`self`].
    pub fn new() -> Self {
        let handlers = vec![];

        Self { handlers }
    }

    /// Adds an [`EventHandler`] to the channel.
    pub fn register<TEventHandler>(&mut self, handler: TEventHandler)
        where
            TEventHandler: EventHandler<T> + 'static,
    {
        let handler = Box::new(handler);

        self.handlers.push(handler);
    }

    /// Queues an event onto the back of [`self`].
    pub fn trigger(&self, event: &T, world: &mut World) {
        self.handlers
            .iter()
            .for_each(|h| h.trigger(event, world))
    }
}

/// A type-erased wrapper over a channel of typed event handlers.
trait ErasedEventChannel {
    /// Casts [`self`] to [`Any`].
    fn as_any(&self) -> &dyn Any;

    /// Casts [`self`] to [`Any`].
    fn as_any_mut(&mut self) -> &mut dyn Any;
}

impl<T> ErasedEventChannel for EventChannel<T>
    where
        T: 'static,
{
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}
