/* 
 * Copyright (C) 2012 Yee Young Han <websearch@naver.com> (http://blog.naver.com/websearch)
 *
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

#include "SipPlatformDefine.h"
#include "TcpSessionMap.h"
#include "TcpStackCallBack.h"
#include "MemoryDebug.h"

CTcpSessionMap::CTcpSessionMap() : m_iThreadIndex(0)
{
}

CTcpSessionMap::~CTcpSessionMap()
{
}

/**
 * @ingroup TcpStack
 * @brief TCP ¼¼¼Ç Á¤º¸¸¦ ÀúÀåÇÑ´Ù.
 * @param pszIp		Å¬¶óÀÌ¾ðÆ® IP ÁÖ¼Ò
 * @param iPort		Å¬¶óÀÌ¾ðÆ® Æ÷Æ® ¹øÈ£
 * @param pclsSessionInfo TCP ¼¼¼Ç Á¤º¸
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CTcpSessionMap::Insert( const char * pszIp, int iPort, CTcpSessionInfo * pclsSessionInfo )
{
	TCP_SESSION_MAP::iterator itTSM;
	std::string strKey;
	bool bRes = false;

	GetKey( pszIp, iPort, strKey );

	m_clsMutex.acquire();
	itTSM = m_clsMap.find( strKey );
	if( itTSM == m_clsMap.end() )
	{
		pclsSessionInfo->m_iThreadIndex = GetThreadIndex();
		m_clsMap.insert( TCP_SESSION_MAP::value_type( strKey, pclsSessionInfo ) );
		bRes = true;
	}
	m_clsMutex.release();

	return bRes;
}

/**
 * @ingroup TcpStack
 * @brief TCP ¼¼¼Ç Á¤º¸¸¦ »èÁ¦ÇÑ´Ù.
 * @param pszIp		Å¬¶óÀÌ¾ðÆ® IP ÁÖ¼Ò
 * @param iPort		Å¬¶óÀÌ¾ðÆ® Æ÷Æ® ¹øÈ£
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CTcpSessionMap::Delete( const char * pszIp, int iPort )
{
	TCP_SESSION_MAP::iterator itTSM;
	std::string strKey;
	bool bRes = false;

	GetKey( pszIp, iPort, strKey );

	m_clsMutex.acquire();
	itTSM = m_clsMap.find( strKey );
	if( itTSM != m_clsMap.end() )
	{
		// // thread ÀÇ ½ºÅÃ º¯¼öÀÇ Æ÷ÀÎÅÍ°¡ second ÀÌ±â ¶§¹®¿¡ delete ÇÏ¸é ¾È µÈ´Ù.
		// delete itTSM->second;
		m_clsMap.erase( itTSM );

		int iMaxIndex = 0;

		for( itTSM = m_clsMap.begin(); itTSM != m_clsMap.end(); ++itTSM )
		{
			if( itTSM->second->m_iThreadIndex > iMaxIndex )
			{
				iMaxIndex = itTSM->second->m_iThreadIndex;
			}
		}

		m_iThreadIndex = iMaxIndex;
		bRes = true;
	}
	m_clsMutex.release();

	return bRes;
}

/**
 * @ingroup TcpStack
 * @brief ¼¼¼Ç °³¼ö¸¦ ¸®ÅÏÇÑ´Ù.
 * @returns ¼¼¼Ç °³¼ö¸¦ ¸®ÅÏÇÑ´Ù.
 */
int CTcpSessionMap::GetCount( )
{
	int iCount;

	m_clsMutex.acquire();
	iCount = m_clsMap.size();
	m_clsMutex.release();

	return iCount;
}

/**
 * @ingroup TcpStack
 * @brief Æ¯Á¤ ¼¼¼Ç¿¡ TCP ÆÐÅ¶À» Àü¼ÛÇÑ´Ù.
 * @param pszIp				IP ÁÖ¼Ò
 * @param iPort				Æ÷Æ® ¹øÈ£
 * @param pszPacket		ÆÐÅ¶
 * @param iPacketLen	ÆÐÅ¶ ±æÀÌ
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CTcpSessionMap::Send( const char * pszIp, int iPort, const char * pszPacket, int iPacketLen )
{
	TCP_SESSION_MAP::iterator itTSM;
	std::string strKey;
	bool bRes = false;

	GetKey( pszIp, iPort, strKey );

	m_clsMutex.acquire();
	itTSM = m_clsMap.find( strKey );
	if( itTSM != m_clsMap.end() )
	{
		bRes = itTSM->second->Send( pszPacket, iPacketLen );
	}
	m_clsMutex.release();

	return bRes;
}

/**
 * @ingroup TcpStack
 * @brief Æ¯Á¤ ¼¼¼Ç¿¡ TCP ÆÐÅ¶À» Àü¼ÛÇÑ´Ù.
 * @param iThreadIndex	TCP ¾²·¹µå ¹øÈ£
 * @param pszPacket			ÆÐÅ¶
 * @param iPacketLen		ÆÐÅ¶ ±æÀÌ
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CTcpSessionMap::Send( int iThreadIndex, const char * pszPacket, int iPacketLen )
{
	TCP_SESSION_MAP::iterator itTSM;
	bool bRes = false;

	m_clsMutex.acquire();
	for( itTSM = m_clsMap.begin(); itTSM != m_clsMap.end(); ++itTSM )
	{
		if( itTSM->second->m_iThreadIndex == iThreadIndex )
		{
			bRes = itTSM->second->Send( pszPacket, iPacketLen );
			break;
		}
	}
	m_clsMutex.release();

	return bRes;
}

/**
 * @ingroup TcpStack
 * @brief ¸ðµç ¼¼¼Ç¿¡ TCP ÆÐÅ¶À» Àü¼ÛÇÑ´Ù.
 * @param pszPacket			ÆÐÅ¶
 * @param iPacketLen		ÆÐÅ¶ ±æÀÌ
 * @param pclsCallBack	¼¼¼Çº°·Î Àü¼Û À¯¹«¸¦ °áÁ¤ÇÏ´Â callback °´Ã¼
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CTcpSessionMap::SendAll( const char * pszPacket, int iPacketLen, ITcpStackCallBack * pclsCallBack )
{
	TCP_SESSION_MAP::iterator itTSM;

	m_clsMutex.acquire();
	for( itTSM = m_clsMap.begin(); itTSM != m_clsMap.end(); ++itTSM )
	{
		if( pclsCallBack->IsSendAll( itTSM->second ) )
		{
			itTSM->second->Send( pszPacket, iPacketLen );
			pclsCallBack->AfterSendAllPerSession( itTSM->second, pszPacket, iPacketLen );
		}
	}
	m_clsMutex.release();

	return true;
}

/**
 * @ingroup TcpStack
 * @brief Æ¯Á¤ ¼¼¼ÇÀ» Á¦¿ÜÇÑ ¸ðµç ¼¼¼Ç¿¡ TCP ÆÐÅ¶À» Àü¼ÛÇÑ´Ù.
 * @param pszPacket			ÆÐÅ¶
 * @param iPacketLen		ÆÐÅ¶ ±æÀÌ
 * @param pclsCallBack	¼¼¼Çº°·Î Àü¼Û À¯¹«¸¦ °áÁ¤ÇÏ´Â callback °´Ã¼
 * @param iThreadIndex	Àü¼ÛÇÏÁö ¾ÊÀ» ¼¼¼ÇÀÇ ¾²·¹µå ÀÎµ¦½º
 * @param iSessionIndex Àü¼ÛÇÏÁö ¾ÊÀ» ¼¼¼Ç ÀÎµ¦½º
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CTcpSessionMap::SendAllExcept( const char * pszPacket, int iPacketLen, ITcpStackCallBack * pclsCallBack, int iThreadIndex, int iSessionIndex )
{
	TCP_SESSION_MAP::iterator itTSM;

	m_clsMutex.acquire();
	for( itTSM = m_clsMap.begin(); itTSM != m_clsMap.end(); ++itTSM )
	{
		if( pclsCallBack->IsSendAll( itTSM->second ) )
		{
			if( itTSM->second->m_iThreadIndex == iThreadIndex && itTSM->second->m_iSessionIndex == iSessionIndex ) continue;

			itTSM->second->Send( pszPacket, iPacketLen );
			pclsCallBack->AfterSendAllPerSession( itTSM->second, pszPacket, iPacketLen );
		}
	}
	m_clsMutex.release();

	return true;
}

/**
 * @ingroup TcpStack
 * @brief ÀÚ·á±¸Á¶ÀÇ Å°¸¦ »ý¼ºÇÑ´Ù.
 * @param pszIp TCP ¼­¹ö IP ÁÖ¼Ò
 * @param iPort TCP ¼­¹ö Æ÷Æ® ¹øÈ£
 * @param strKey ÀÚ·á±¸Á¶ÀÇ Å°¸¦ ÀúÀåÇÒ º¯¼ö
 */
void CTcpSessionMap::GetKey( const char * pszIp, int iPort, std::string & strKey )
{
	char szPort[6];

	snprintf( szPort, sizeof(szPort), "%d", iPort );

	strKey = pszIp;
	strKey.append( ":" );
	strKey.append( szPort );
}

/**
 * @ingroup TcpStack
 * @brief »õ·Î »ç¿ëÇÒ ¾²·¹µå ¹øÈ£¸¦ °¡Á®¿Â´Ù.
 * @returns »õ·Î »ç¿ëÇÒ ¾²·¹µå ¹øÈ£¸¦ ¸®ÅÏÇÑ´Ù.
 */
int CTcpSessionMap::GetThreadIndex()
{
	++m_iThreadIndex;
	if( m_iThreadIndex > 2000000000 )
	{
		m_iThreadIndex = 1;
	}

	while( SelectThreadIndex( m_iThreadIndex ) )
	{
		++m_iThreadIndex;
		if( m_iThreadIndex > 2000000000 )
		{
			m_iThreadIndex = 1;
		}
	}

	return m_iThreadIndex;
}

/**
 * @ingroup TcpStack
 * @brief ¾²·¹µå ¹øÈ£°¡ »ç¿ëÁßÀÎÁö °Ë»çÇÑ´Ù.
 * @param iThreadIndex ¾²·¹µå ¹øÈ£
 * @returns ¾²·¹µå ¹øÈ£°¡ »ç¿ëÁßÀÌ¸é true ¸¦ ¸®ÅÏÇÏ°í ±×·¸Áö ¾ÊÀ¸¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CTcpSessionMap::SelectThreadIndex( int iThreadIndex )
{
	TCP_SESSION_MAP::iterator itTSM;

	for( itTSM = m_clsMap.begin(); itTSM != m_clsMap.end(); ++itTSM )
	{
		if( itTSM->second->m_iThreadIndex == iThreadIndex )
		{
			return true;
		}
	}

	return false;
}
