//-----------------------------------------------------------------------------------------------
// Events.cpp
//
#include "GypsyPrecompiledHeader.hpp"

//-----------------------------------------------------------------------------------------------
#include "Engine/Core/Events.hpp"
#include "Engine/Core/NamedProperties.hpp"


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


/////////////////////////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------------------------
VIRTUAL EventRecipient::~EventRecipient()
{
	theEventSystem.UnregisterAllEventCallbacksForObject( *this );
}


/////////////////////////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------------------------
int EventSystem::FireEvent( const std::string& eventName, const NamedProperties& params = NamedProperties::s_empty )
{
	CaseInsensitiveStringHashValue eventNameHash = ComputeCaseInsensitiveHashForString( eventName );
	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >::const_iterator found = m_registeredEvents.find( eventNameHash );
	if( found != m_registeredEvents.end() )
	{
		const RegisteredEvent* registeredEvent = found->second;
		const std::vector< RegisteredEventRecipientBase* > recipientsAtTimeEventWasFired = registeredEvent->m_eventRecipients;
		const int numRecipients = static_cast< int >( recipientsAtTimeEventWasFired.size() );
		int numActualRecipients = 0;
		for( int recipientIndex = 0; recipientIndex < numRecipients; ++ recipientIndex )
		{
			RegisteredEventRecipientBase* recipient = recipientsAtTimeEventWasFired[ recipientIndex ];
			if( recipient )
			{
				++ numActualRecipients;
				recipient->CallRegisteredFunction( params );
			}
		}

		return numActualRecipients;
	}
	else
	{
		return 0;
	}
}


//-----------------------------------------------------------------------------------------------
void EventSystem::UnregisterEventCallbacksForObjectAddress( CaseInsensitiveStringHashValue eventNameHash, const void* objectAddress )
{
// 	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >::iterator beginIter = m_registeredEvents.begin();
// 	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >::iterator endIter = m_registeredEvents.end();
// 	eventIter = beginIter;
// 	eventIter = endIter;
//	for( eventIter = m_registeredEvents.begin(); eventIter != m_registeredEvents.end(); ++ eventIter )
//	for( eventIter = beginIter; eventIter != endIter; ++ eventIter )
	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >::iterator eventIter;
	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >::iterator endOfEvents = m_registeredEvents.end();
	for( eventIter = m_registeredEvents.begin(); eventIter != endOfEvents; ++ eventIter )
	{
		CaseInsensitiveStringHashValue thisEventNameHash = eventIter->first;
		if( eventNameHash && eventNameHash != thisEventNameHash )
			continue;

		RegisteredEvent& registeredEvent = *eventIter->second;
		unsigned int numRecipients = registeredEvent.m_eventRecipients.size();
		for( unsigned int recipientIndex = 0; recipientIndex < numRecipients; ++ recipientIndex )
		{
			RegisteredEventRecipientBase* eventRecipient = registeredEvent.m_eventRecipients[ recipientIndex ];
			if( eventRecipient && eventRecipient->GetObjectAddress() == objectAddress )
			{
				delete registeredEvent.m_eventRecipients[ recipientIndex ];
				registeredEvent.m_eventRecipients[ recipientIndex ] = nullptr;
			}
		}
	}
}


//-----------------------------------------------------------------------------------------------
int EventSystem::GetFilteredEventNameList( bool includeCommands, bool includeNonCommands, bool requireSubstringAtBeginning, const std::string& substringFilter, std::vector< std::string >& out_eventNames, std::vector< std::string >* out_eventDescriptions )
{
	const bool isFiltering = !substringFilter.empty();
	std::string substringFilterLowered = GetAsLowerCase( substringFilter );
	std::map< std::string, RegisteredEvent* > sortedFilteredEvents;
	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >::iterator eventIter;
	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >::iterator endOfEvents = m_registeredEvents.end();
	for( eventIter = m_registeredEvents.begin(); eventIter != endOfEvents; ++ eventIter )
	{
		RegisteredEvent& registeredEvent = *eventIter->second;
		if( registeredEvent.m_isCommand && !includeCommands )
			continue;

		if( !registeredEvent.m_isCommand && !includeNonCommands )
			continue;

		if( isFiltering )
		{
			std::string eventNameLowered = GetAsLowerCase( registeredEvent.m_eventName );
			if( requireSubstringAtBeginning )
			{

				if( !DoesShorterStringMatch( substringFilterLowered, eventNameLowered ) )
					continue;
			}
			else
			{
				if( eventNameLowered.find( substringFilterLowered ) == std::string::npos )
					continue;
			}
		}

		std::string lowerCaseEventName = GetAsLowerCase( registeredEvent.m_eventName );
		sortedFilteredEvents[ lowerCaseEventName ] = &registeredEvent;
	}

	int numMatchesFound = static_cast< int >( sortedFilteredEvents.size() );
	out_eventNames.reserve( numMatchesFound );
	if( out_eventDescriptions )
		out_eventDescriptions->reserve( numMatchesFound );

	std::map< std::string, RegisteredEvent* >::iterator sortedIter;
	std::map< std::string, RegisteredEvent* >::iterator endOfSortedEvents = sortedFilteredEvents.end();
	for( sortedIter = sortedFilteredEvents.begin(); sortedIter != endOfSortedEvents; ++ sortedIter )
	{
		RegisteredEvent& sortedEvent = *sortedIter->second;
		out_eventNames.push_back( sortedEvent.m_eventName );
		if( out_eventDescriptions )
			out_eventDescriptions->push_back( sortedEvent.m_helpText );
	}

	return numMatchesFound;
}


//-----------------------------------------------------------------------------------------------
void EventSystem::RegisterCommands()
{
	RegisterCommandCallbackForObject( "Help", &EventSystem::Command_Help, *this, "Display a list of registered commands.  Use Help <substring> to filter search." );
}


//-----------------------------------------------------------------------------------------------
void EventSystem::RemoveAllEventRegistrations()
{
	std::map< CaseInsensitiveStringHashValue, RegisteredEvent* >::iterator eventIter;
	for( eventIter = m_registeredEvents.begin(); eventIter != m_registeredEvents.end(); ++ eventIter )
	{
		RegisteredEvent& registeredEvent = *eventIter->second;
		std::vector< RegisteredEventRecipientBase* >::iterator recipientIter;
		for( recipientIter = registeredEvent.m_eventRecipients.begin(); recipientIter != registeredEvent.m_eventRecipients.end(); ++ recipientIter )
		{
			RegisteredEventRecipientBase* recipient = *recipientIter;
			delete recipient;
		}
	}

	m_registeredEvents.clear();
}


//-----------------------------------------------------------------------------------------------
void EventSystem::Command_Help( const NamedProperties& params )
{
	const Rgba CONSOLE_HELP_COLOR_HEADER( Rgba::GREY );
	const Rgba CONSOLE_HELP_COLOR_BODY( Rgba::WHITE );
	std::string substringFilter;
	params.Get( "Args", substringFilter );
	std::vector< std::string > eventNames;
	std::vector< std::string > eventDescriptions;
	int numResults = GetFilteredEventNameList( true, false, false, substringFilter, eventNames, &eventDescriptions );

	if( substringFilter.empty() )
	{
		ConsolePrintf( CONSOLE_HELP_COLOR_HEADER, "There are currently %i registered command(s):\n", numResults );
	}
	else
	{
		ConsolePrintf( CONSOLE_HELP_COLOR_HEADER, "There are currently %i registered command(s) whose names contain \"%s\":\n", numResults, substringFilter.c_str() );
	}
	for( int resultIndex = 0; resultIndex < numResults; ++ resultIndex )
	{
		if( eventDescriptions[ resultIndex ].empty() )
			ConsolePrintf( CONSOLE_HELP_COLOR_BODY, "  %s\n", eventNames[ resultIndex ].c_str() );
		else
			ConsolePrintf( CONSOLE_HELP_COLOR_BODY, "  %s - %s\n", eventNames[ resultIndex ].c_str(), eventDescriptions[ resultIndex ].c_str() );
	}
	ConsolePrintf( "\n" );
}


//-----------------------------------------------------------------------------------------------
void EventSystem::StartUp()
{
	RegisterCommands();
}


//-----------------------------------------------------------------------------------------------
void EventSystem::ShutDown()
{
	RemoveAllEventRegistrations();
}


//-----------------------------------------------------------------------------------------------
// Standalone convenience functions
//
int FireEvent( const std::string& eventName, const NamedProperties& params = NamedProperties::s_empty )
{
	return theEventSystem.FireEvent( eventName, params );
}

