#include "precompiled.h"

#include "Session.h"
#include "Network.h"
#include "ps/CLogger.h"

using namespace std;

void CSessionManager::Register(CNetSession *pSession)
{
	CScopeLock scopeLock(m_Mutex);
	SessionMap::iterator it;
	
	// The total number of references to this session should be 1 and only 1
	// Either there's one in m_Sessions or there's one in m_AddQueue. There
	// shall be no references in the remove queue.
	// m_Sessions is Read-Only
	
	if ((it = m_RemoveQueue.find(pSession)) != m_RemoveQueue.end())
		m_RemoveQueue.erase(it);
	if (m_Sessions.find(pSession) == m_Sessions.end())
		m_AddQueue[pSession]=pSession;
}

void CSessionManager::Deregister(CNetSession *pSession)
{
	CScopeLock scopeLock(m_Mutex);
	SessionMap::iterator it;
	
	// The total number of references to this session should be 2 or 0:
	// One in m_Sessions and one in the remove queue or 0 in both. None in
	// m_AddQueue.
	// m_Sessions is Read-Only
	
	if ((it = m_AddQueue.find(pSession)) != m_AddQueue.end())
		m_AddQueue.erase(it);
	if (m_Sessions.find(pSession) != m_Sessions.end())
		m_RemoveQueue.insert(make_pair(pSession, pSession));
}

void CSessionManager::Poll()
{
	m_Mutex.Lock();
	SessionMap::iterator it;
	
	// De/Register should've made sure that it doesn't matter which order we
	// process the two queues.
	for (it=m_AddQueue.begin();it != m_AddQueue.end();++it)
	{
		m_Sessions.insert(*it);
	}
	m_AddQueue.clear();
	for (it=m_RemoveQueue.begin();it != m_RemoveQueue.end();++it)
	{
		m_Sessions.erase(it->second);
	}
	m_RemoveQueue.clear();
	
	it=m_Sessions.begin();
	while (it != m_Sessions.end())
	{
		CNetMessage *pMsg;
		
		// Extraneous? well, sessions are perfectly able to delete other
		// sessions (or cause them to be deleted) in their message handler
		if (m_RemoveQueue.find(it->second) != m_RemoveQueue.end())
			continue;
		
		while ((pMsg = it->second->TryPop()) != NULL)
		{
			CNetSession *pSess=it->second;
			m_Mutex.Unlock();
			if (!pSess->HandleMessage(pMsg))
			{
				LOG(WARNING, LOG_CAT_NET, "CSessionManager::Poll(): Unhandled message %s.", pMsg->GetString().c_str());
				delete pMsg;
			}
			m_Mutex.Lock();
			
			if (m_RemoveQueue.find(it->second) != m_RemoveQueue.end())
				break;
		}
		++it;
	}
	m_Mutex.Unlock();
}
