// Copyright (c) 2023 Artur Schütz

#pragma once

#include "CoreMinimal.h"
#include "EventManagerDemo/Public/Identifier.h"
#include "EventManagerDemo/Public/IEvent.h"

// Definiere eine Delegate mit einem EventPtr-Parameter.
DECLARE_DELEGATE_OneParam(EventCallback, const EventPtr&);

/**
 * \class EventManager
 * \brief Der zentrale Event-Manager als Singleton implementiert.
 *
 * Diese Klasse verwaltet das Hinzufügen und Entfernen von Event-Listeners,
 * das Warteschlangen von Events und das unmittelbare Auslösen von Events.
 */
class EVENTMANAGERDEMO_API EventManager
{
public:
	/// returns singleton instance
	static EventManager& getInstance() { return oneAndOnly; }

	void Init();    ///< Initialisiert den Event-Manager.
	void Release(); ///< Gibt alle Ressourcen frei und bereinigt.

	/**
	 * Fügt einen Listener für einen spezifischen Event-Typ hinzu.
	 *
	 * \param callback Der Callback, der aufgerufen wird, wenn das Event ausgelöst wird.
	 * \param eventHash Der Hashwert des Event-Typs, auf den gehört werden soll.
	 * \param listenerName Ein einzigartiger Identifier für den neuen Listener.
	 * \throw std::exception bei Hash-Kollisionen oder wenn ein Listener mit demselben Namen bereits existiert.
	 */
	void addListener(const EventCallback& callback, const uint32 eventHash, const Identifier& listenerName);

	/**
	 * Entfernt den angegebenen Listener.
	 *
	 * \param listenerName Der Identifier des zu entfernenden Listeners.
	 * \throw std::exception, wenn der Listener nicht existiert.
	 */
	void removeListener(const Identifier& listenerName);

	/**
	 * Reit ein Event zur späteren Auslösung in die Warteschlange ein.
	 *
	 * \param pEvent Das zu warteschlangende Event.
	 * \param pSender Der Sender dieses Events - nullptr ist nicht erlaubt!
	 * \note Die tatsächliche Auslösung erfolgt durch 'triggerQueuedEvents'.
	 */
	void queueEvent(const EventPtr& pEvent);

	/**
	 * Löst ein Event sofort aus - ruft alle Callbacks direkt auf.
	 *
	 * \param pEvent Das auszulösende Event.
	 * \param pSender Der Sender dieses Events - nullptr ist nicht erlaubt!
	 */
	void triggerEvent(const EventPtr& pEvent);

	/**
	 * Löst alle in der Warteschlange stehenden Events aus.
	 *
	 * \note Sollte nach 'queueEvent' aufgerufen werden, um die Events zu verarbeiten.
	 */
	void triggerQueuedEvents();

private:
	EventManager();
	~EventManager();

	/// singleton instance - initialized at programm startup; appropriate in this case!
	static EventManager oneAndOnly;

	TArray<EventPtr> queuedEvents; ///< Liste der in der Warteschlange stehenden Events.

	/**
	 * Verzeichnis für Event-Callbacks.
	 * Event-Identifier -> (Callback-Identifier -> Callback)
	 */
	TMap<HashValue, TMap<Identifier, EventCallback>> eventRegistry;

	FCriticalSection m_eventManagerCritialSection;
};