// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

#ifndef FENNEC_CORE_EVENT_H
#define FENNEC_CORE_EVENT_H

#include <fennec/lang/types.h>
#include <fennec/rtti/enable.h>

#include <fennec/rtti/typeid.h>

namespace fennec
{

struct event;

///
/// \brief Class outlining the interface for an object that listens for events
class event_listener {
// Constructors & Destructor ===========================================================================================
public:

	/// \name Constructors & Destructor
	/// @{

	///
	/// \brief Virtual Destructor
	virtual ~event_listener();

	/// @}


// Event Handling ======================================================================================================
public:

	/// \name Event Handling
	/// @{

	///
	/// \brief event handler callback
	/// \param event the event to handle
	virtual void handle_event(event* event) = 0;

	/// @}


// Private Member Variables ============================================================================================
private:
	FENNEC_RTTI_CLASS_ENABLE() {
	}
};

///
/// \brief Main event interface, includes static methods for registering listeners and dispatching events
struct event {

// Constructor & Destructor ============================================================================================
public:

	/// \name Constructors & Destructor
	/// @{

	///
	/// \brief Default Constructor
	event() = default;

	///
	/// \brief Copy Constructor
	event(const event&) = default;

	///
	/// \brief Move Constructor
	event(event&&) noexcept = default;

	///
	/// \brief Virtual Destructor
	virtual ~event() = default;

	/// @}


// Static Event System Interface =======================================================================================
public:

	/// \name Event System Interface
	/// @{

	///
	/// \brief Handles the event loop to distribute events.
	///
	/// \details Blocking
	static void handle_events();

	///
	/// \brief Registers a listener for the event type
	///
	/// \details Blocking
	/// \tparam EventT the event type
	/// \param listener the listener to register
	template<typename EventT>
	static void add_listener(event_listener* listener) {
		event::_add_listener(listener, type::get<EventT>().id());
	}

	///
	/// \brief Removes a listener from the event system
	///
	/// \details Blocking
	/// \param listener the listener to remove
	static void remove_listener(event_listener* listener);

	///
	/// \brief Dispatch an event at the beginning of the next tick.
	///
	/// \details Non-Blocking, Lock-Free, Wait-Free
	/// \tparam EventT The event type
	/// \tparam ArgsT The argument types
	/// \param args The arguments to construct the event with
	template<typename EventT, typename...ArgsT>
	static void dispatch(ArgsT&&...args) {
		event::_dispatch(fennec::make_unique<EventT>(fennec::forward<ArgsT>(args)...));
	}

	///
	/// \brief Dispatch an event immediately, on the current thread.
	///
	/// \details Blocking
	/// \tparam EventT The event type
	/// \tparam ArgsT The argument types
	/// \param args The arguments to construct the event with
	template<typename EventT, typename...ArgsT>
	static void dispatch_immediate(ArgsT&&...args) {
		event::_dispatch_immediate(fennec::make_unique<EventT>(fennec::forward<ArgsT>(args)...));
	}

	/// @}


// Private Helpers =====================================================================================================
private:
	static void _add_listener(event_listener* listener, uint64_t type);
	static void _handle_event(unique_ptr<event>& event);
	static void _dispatch(unique_ptr<event>&& event);
	static void _dispatch_immediate(unique_ptr<event>&& event);

#ifndef FENNEC_DOXYGEN
	FENNEC_RTTI_CLASS_ENABLE() {
	}
#endif
};

}

#endif // FENNEC_CORE_EVENT_H
