#include "EventManager.h"

#include <set>

#include "EventHandlerBase.h"

std::shared_ptr<EventManager> EventManager::getInstance()
{
	if (!s_instance)
	{
		s_instance = std::shared_ptr<EventManager>(new EventManager());
	}
	return s_instance;
}

void EventManager::destroyInstance()
{
	if (s_instance)
	{
		s_instance.reset();
	}
}

std::shared_ptr<EventManager> EventManager::s_instance = std::shared_ptr<EventManager>();

void EventManager::registerEventHandler(EventHandlerBase *eventHandler)
{
	std::scoped_lock<std::mutex> lock(m_eventHandlersMutex);
	m_eventHandlers[eventHandler->m_id] = eventHandler;
}

void EventManager::unregisterEventHandler(EventHandlerBase *eventHandler)
{
	std::scoped_lock<std::mutex> lock(m_eventHandlersMutex);
	m_eventHandlers.erase(eventHandler->m_id);
}

void EventManager::sendEventImmediately(std::shared_ptr<Event> event)
{
	if (event)
	{
		std::set<unsigned int> eventHandlerIds;
		{
			std::scoped_lock<std::mutex> lock(m_eventHandlersMutex);
			for (auto it : m_eventHandlers)
			{
				eventHandlerIds.insert(it.first);
			}
		}

		for (unsigned int id : eventHandlerIds)
		{
			// this is necessary to allow HandleBaseEvent() to remove event handlers without causing a crash
			EventHandlerBase *eventHandler = nullptr;
			{
				std::scoped_lock<std::mutex> lock(m_eventHandlersMutex);
				auto it = m_eventHandlers.find(id);
				if (it != m_eventHandlers.end())
				{
					eventHandler = it->second;
				}
			}
			if (eventHandler)
			{
				eventHandler->handleBaseEvent(event);
			}
		}
	}
}

void EventManager::addEvent(std::shared_ptr<Event> event)
{
	if (event)
	{
		std::scoped_lock<std::mutex> lock(m_eventsMutex);
		m_events.push_back(event);
	}
}

void EventManager::processEvents()
{
	std::vector<std::shared_ptr<Event>> events;
	{
		std::scoped_lock<std::mutex> lock(m_eventsMutex);
		events = m_events;
		m_events.clear();
	}

	std::set<unsigned int> eventHandlerIds;
	{
		std::scoped_lock<std::mutex> lock(m_eventHandlersMutex);
		for (auto it : m_eventHandlers)
		{
			eventHandlerIds.insert(it.first);
		}
	}

	for (std::shared_ptr<Event> event : events)
	{
		for (unsigned int id : eventHandlerIds)
		{
			// this is necessary to allow HandleBaseEvent() to remove event handlers without causing a crash
			EventHandlerBase *eventHandler = nullptr;
			{
				std::scoped_lock<std::mutex> lock(m_eventHandlersMutex);
				auto it = m_eventHandlers.find(id);
				if (it != m_eventHandlers.end())
				{
					eventHandler = it->second;
				}
			}
			if (eventHandler)
			{
				eventHandler->handleBaseEvent(event);
			}
		}
	}
}

void EventManager::clearEvents()
{
	std::scoped_lock<std::mutex> lock(m_eventsMutex);
	m_events.clear();
}

bool EventManager::hasEvents() const
{
	return !m_events.empty();
}
