//-----------------------------------------------------------------------------------------------
// Events.hpp
//
#pragma once
#ifndef included_Events
#define included_Events


//-----------------------------------------------------------------------------------------------
// ToDo List:


//-----------------------------------------------------------------------------------------------
class NamedProperties;
class HashedCaseInsensitiveString;


//-----------------------------------------------------------------------------------------------
#include "Engine/Core/EngineBase.hpp"


/////////////////////////////////////////////////////////////////////////////////////////////////
class RegisteredEventRecipientBase
{
	friend class EventSystem;

protected:
	virtual void CallRegisteredFunction( const NamedProperties& ) = 0;
	virtual void* GetObjectAddress() const = 0;
};



/////////////////////////////////////////////////////////////////////////////////////////////////
template< typename T_RegisteredObjectType >
class RegisteredEventRecipient : public RegisteredEventRecipientBase
{
	friend class EventSystem;

private:
	typedef void (T_RegisteredObjectType::*ObjectMemberFunctionType)( const NamedProperties& params );

	RegisteredEventRecipient( T_RegisteredObjectType& object, ObjectMemberFunctionType func )
		: m_objectAddress( &object )
		, m_func( func )
	{}

	virtual void CallRegisteredFunction( const NamedProperties& params )
	{
		(m_objectAddress->*m_func)( params );
	}

	virtual void* GetObjectAddress() const
	{
		return static_cast< void* >( m_objectAddress );
	}

private:
	T_RegisteredObjectType*		m_objectAddress;
	ObjectMemberFunctionType	m_func;
};



/////////////////////////////////////////////////////////////////////////////////////////////////
class RegisteredEvent
{
	friend class EventSystem;

private:
	RegisteredEvent( const std::string& commandOrEventName, bool isCommand, const std::string& helpText )
		: m_eventName( commandOrEventName )
		, m_isCommand( isCommand )
		, m_helpText( helpText )
	{}

private:
	std::string										m_eventName;
	bool											m_isCommand;
	std::string										m_helpText;
	std::vector< RegisteredEventRecipientBase* >	m_eventRecipients;
};


/////////////////////////////////////////////////////////////////////////////////////////////////
// Objects which derive from EventRecipient automatically unregister from all events upon death.
//
class EventRecipient
{
public:
	virtual ~EventRecipient();
};


/////////////////////////////////////////////////////////////////////////////////////////////////
class EventSystem
{
public:
	int FireEvent( const std::string& eventName, const NamedProperties& params );

	template< typename T_RegisteringObjectType, typename T_ObjectMethodType >
	void RegisterEventCallbackForObject( const std::string& eventName, T_ObjectMethodType callbackFunc, T_RegisteringObjectType& callbackFuncObject );

	template< typename T_RegisteringObjectType, typename T_ObjectMethodType >
	void RegisterCommandCallbackForObject( const std::string& commandEventName, T_ObjectMethodType callbackFunc, T_RegisteringObjectType& callbackFuncObject, const std::string& helpText );

	template< typename T_RegisteredObjectType >
	void UnregisterAllEventCallbacksForObject( const T_RegisteredObjectType& registeredObject );

	template< typename T_RegisteredObjectType >
	void UnregisterEventCallbackForObject( const std::string& eventName, const T_RegisteredObjectType& registeredObject );

	int GetFilteredEventNameList( bool includeCommands, bool includeNonCommands, bool requireSubstringAtBeginning, const std::string& substringFilter, std::vector< std::string >& out_eventNames, std::vector< std::string >* out_eventDescriptions );
	void StartUp();
	void ShutDown();

private:
	void RegisterCommands();
	void RemoveAllEventRegistrations();
	void Command_Help( const NamedProperties& params );
	void UnregisterEventCallbacksForObjectAddress( CaseInsensitiveStringHashValue eventNameHash, const void* objectAddress );

	template< typename T_RegisteringObjectType, typename T_ObjectMethodType >
	void RegisterCallbackForObject( const std::string& commandOrEventName, T_ObjectMethodType callbackFunc, T_RegisteringObjectType& callbackFuncObject, bool isCommand, const std::string& helpText );

private:
	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >	m_registeredEvents;
};


//-----------------------------------------------------------------------------------------------
template< typename T_RegisteringObjectType, typename T_ObjectMethodType >
void EventSystem::RegisterCallbackForObject( const std::string& commandOrEventName, T_ObjectMethodType callbackFunc, T_RegisteringObjectType& callbackFuncObject, bool isCommand, const std::string& helpText )
{
	RegisteredEvent* registeredEvent = nullptr;
	CaseInsensitiveStringHashValue eventNameHash = ComputeCaseInsensitiveHashForString( commandOrEventName );
	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >::iterator found = m_registeredEvents.find( eventNameHash );
	if( found != m_registeredEvents.end() )
	{
		registeredEvent = found->second;
	}
	else
	{
		registeredEvent = new RegisteredEvent( commandOrEventName, isCommand, helpText );
		m_registeredEvents[ eventNameHash ] = registeredEvent;
	}

	RegisteredEventRecipient< T_RegisteringObjectType >* registeredObject = new RegisteredEventRecipient< T_RegisteringObjectType >( callbackFuncObject, callbackFunc );
	registeredEvent->m_eventRecipients.push_back( registeredObject );
}


//-----------------------------------------------------------------------------------------------
template< typename T_RegisteringObjectType, typename T_ObjectMethodType >
void EventSystem::RegisterCommandCallbackForObject( const std::string& commandEventName, T_ObjectMethodType callbackFunc, T_RegisteringObjectType& callbackFuncObject, const std::string& helpText )
{
	RegisterCallbackForObject( commandEventName, callbackFunc, callbackFuncObject, true, helpText );
}


//-----------------------------------------------------------------------------------------------
template< typename T_RegisteringObjectType, typename T_ObjectMethodType >
void EventSystem::RegisterEventCallbackForObject( const std::string& eventName, T_ObjectMethodType callbackFunc, T_RegisteringObjectType& callbackFuncObject )
{
	RegisterCallbackForObject( eventName, callbackFunc, callbackFuncObject, false, "" );
}


//-----------------------------------------------------------------------------------------------
template< typename T_RegisteredObjectType >
void EventSystem::UnregisterAllEventCallbacksForObject( const T_RegisteredObjectType& registeredObject )
{
	const void* registeredObjectAddress = static_cast< const void* >( &registeredObject );
	UnregisterEventCallbacksForObjectAddress( 0, registeredObjectAddress );
}


//-----------------------------------------------------------------------------------------------
template< typename T_RegisteredObjectType >
void EventSystem::UnregisterEventCallbackForObject( const std::string& eventName, const T_RegisteredObjectType& registeredObject )
{
	CaseInsensitiveStringHashValue eventNameHash = ComputeCaseInsensitiveHashForString( eventName );
	const void* registeredObjectAddress = static_cast< const void* >( &registeredObject );
	UnregisterEventCallbacksForObjectAddress( eventNameHash, registeredObjectAddress );
}


//-----------------------------------------------------------------------------------------------
// Standalone convenience functions
//
int FireEvent( const std::string& eventName, const NamedProperties& params );



//-----------------------------------------------------------------------------------------------
// Global singleton instance
//
extern EventSystem theEventSystem;


#endif // included_Events
