#pragma once

#include <memory>
#include <typeindex>
#include <unordered_map>
#include <vector>

#include "../api/Event.h"
#include "../api/EventHandler.h"

namespace crepe {

//! Event listener unique ID
typedef size_t subscription_t;

/**
 * \brief Event channel
 *
 * Events can be sent to a specific channel, which prevents listeners on other channels from
 * being called. The default channel is EventManager::CHANNEL_ALL, which calls all listeners.
 */
typedef size_t event_channel_t;

/**
 * \class EventManager
 * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling.
 * 
 * The `EventManager` acts as a centralized event system. It allows for registering callbacks
 * for specific event types, triggering events synchronously, queueing events for later
 * processing, and managing subscriptions via unique identifiers.
 */
class EventManager {
public:
	static constexpr const event_channel_t CHANNEL_ALL = -1;

	/**
	 * \brief Get the singleton instance of the EventManager.
	 * 
	 * This method returns the unique instance of the EventManager, creating it if it
	 * doesn't already exist. Ensures only one instance is active in the program.
	 * 
	 * \return Reference to the singleton instance of the EventManager.
	 */
	static EventManager & get_instance();

	/**
	 * \brief Subscribe to a specific event type.
	 * 
	 * Registers a callback for a given event type and optional channel. Each callback
	 * is assigned a unique subscription ID that can be used for later unsubscription.
	 * 
	 * \tparam EventType The type of the event to subscribe to.
	 * \param callback The callback function to be invoked when the event is triggered.
	 * \param channel The channel number to subscribe to (default is CHANNEL_ALL, which listens to all channels).
	 * \return A unique subscription ID associated with the registered callback.
	 */
	template <typename EventType>
	subscription_t subscribe(const EventHandler<EventType> & callback,
							 event_channel_t channel = CHANNEL_ALL);

	/**
	 * \brief Unsubscribe a previously registered callback.
	 * 
	 * Removes a callback from the subscription list based on its unique subscription ID.
	 * 
	 * \param event_id The unique subscription ID of the callback to remove.
	 */
	void unsubscribe(subscription_t event_id);

	/**
	 * \brief Trigger an event immediately.
	 * 
	 * Synchronously invokes all registered callbacks for the given event type on the specified channel.
	 * 
	 * \tparam EventType The type of the event to trigger.
	 * \param event The event instance to pass to the callbacks.
	 * \param channel The channel to trigger the event on (default is CHANNEL_ALL, which triggers on all channels).
	 */
	template <typename EventType>
	void trigger_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);

	/**
	 * \brief Queue an event for later processing.
	 * 
	 * Adds an event to the event queue to be processed during the next call to `dispatch_events`.
	 * 
	 * \tparam EventType The type of the event to queue.
	 * \param event The event instance to queue.
	 * \param channel The channel to associate with the event (default is CHANNEL_ALL).
	 */
	template <typename EventType>
	void queue_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);

	/**
	 * \brief Process all queued events.
	 * 
	 * Iterates through the event queue and triggers callbacks for each queued event.
	 * Events are removed from the queue once processed.
	 */
	void dispatch_events();

	/**
	 * \brief Clear all subscriptions.
	 * 
	 * Removes all registered event handlers and clears the subscription list.
	 */
	void clear();

private:
	/**
	 * \brief Default constructor for the EventManager.
	 * 
	 * Constructor is private to enforce the singleton pattern.
	 */
	EventManager() = default;

	/**
	 * \struct QueueEntry
	 * \brief Represents an entry in the event queue.
	 */
	struct QueueEntry {
		std::unique_ptr<Event> event; ///< The event instance.
		event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event.
		std::type_index type; ///< The type of the event.
	};

	/**
	 * \brief Internal event handler
	 *
	 * This function processes a single event, and is used to process events both during
	 * EventManager::dispatch_events and inside EventManager::trigger_event
	 *
	 * \param type \c typeid of concrete Event class
	 * \param channel Event channel
	 * \param data Event data
	 */
	void handle_event(std::type_index type, event_channel_t channel, const Event & data);

	/**
	 * \struct CallbackEntry
	 * \brief Represents a registered event handler callback.
	 */
	struct CallbackEntry {
		std::unique_ptr<IEventHandlerWrapper> callback; ///< The callback function wrapper.
		event_channel_t channel = CHANNEL_ALL; ///< The channel this callback listens to.
		subscription_t id = -1; ///< Unique subscription ID.
	};

	//! The queue of events to be processed during dispatch.
	std::vector<QueueEntry> events_queue;

	//! A map of event type to registered callbacks.
	std::unordered_map<std::type_index, std::vector<CallbackEntry>> subscribers;

	//! Counter to generate unique subscription IDs.
	subscription_t subscription_counter = 0;
};

} // namespace crepe

#include "EventManager.hpp"