/* 
 * 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 "SipStack.h"
#include "SipStackThread.h"
#include "SipDeleteQueue.h"
#include "SipTcpMessage.h"
#include "SipTlsMessage.h"
#include "SipQueue.h"
#include "TimeUtility.h"
#include "Log.h"
#include "MemoryDebug.h"

#include "SipStackCallBack.hpp"
#include "SipStackComm.hpp"

/**
 * @ingroup SipStack
 * @brief »ý¼ºÀÚ - ³»ºÎ º¯¼ö¸¦ ÃÊ±âÈ­½ÃÅ°°í transaction list ¿Í SIP stack À» ¿¬°á½ÃÅ²´Ù.
 */
CSipStack::CSipStack()
{
	m_bStopEvent = false;
	m_bStackThreadRun = false;
	m_hUdpSocket = INVALID_SOCKET;
	m_hTcpSocket = INVALID_SOCKET;

#ifdef USE_TLS
	m_hTlsSocket = INVALID_SOCKET;
#endif

	m_bStarted = false;
	m_iUdpThreadRunCount = 0;
	m_iTcpThreadRunCount = 0;

	m_clsICT.SetSipStack( this );
	m_clsNICT.SetSipStack( this );
	m_clsIST.SetSipStack( this );
	m_clsNIST.SetSipStack( this );

	m_pclsSecurityCallBack = NULL;
}

/**
 * @ingroup SipStack
 * @brief ¼Ò¸êÀÚ
 */
CSipStack::~CSipStack()
{
}

/**
 * @ingroup SipStack
 * @brief SIP stack À» ½ÃÀÛÇÑ´Ù. SIP stack ¾²·¹µå¿Í network ¼ö½Å ¾²·¹µå¸¦ ½ÃÀÛÇÑ´Ù.
 * @param clsSetup SIP stack ¼³Á¤ Ç×¸ñ ÀúÀå °´Ã¼
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipStack::Start( CSipStackSetup & clsSetup )
{
	if( m_bStarted || m_bStopEvent ) return false;
	if( clsSetup.Check() == false ) return false;

	m_clsSetup = clsSetup;
	m_clsICT.SetTimerD( m_clsSetup.m_iTimerD );
	m_clsNIST.SetTimerJ( m_clsSetup.m_iTimerJ );
	m_clsTcpConnectMap.SetStateful( m_clsSetup.m_bStateful );

#ifdef USE_TLS
	m_clsTlsConnectMap.SetStateful( m_clsSetup.m_bStateful );
#endif

	InitNetwork();

	if( m_clsSetup.m_iLocalUdpPort > 0 )
	{
		m_hUdpSocket = UdpListen( m_clsSetup.m_iLocalUdpPort, NULL, m_clsSetup.m_bIpv6 );
		if( m_hUdpSocket == INVALID_SOCKET ) return false;

		if( m_clsSetup.m_iUdpBufSize > 0 )
		{
			int iBufSize = 0;
			int iIntSize = sizeof(iBufSize);

			getsockopt( m_hUdpSocket, SOL_SOCKET, SO_SNDBUF, (char *)&iBufSize, (socklen_t*)&iIntSize );
			CLog::Print( LOG_DEBUG, "UDP SendBufferSize = %d", iBufSize );

			setsockopt( m_hUdpSocket, SOL_SOCKET, SO_SNDBUF, (char *)&m_clsSetup.m_iUdpBufSize, iIntSize );

			iBufSize = 0;
			getsockopt( m_hUdpSocket, SOL_SOCKET, SO_SNDBUF, (char *)&iBufSize, (socklen_t*)&iIntSize );
			CLog::Print( LOG_SYSTEM, "UDP SendBufferSize = %d", iBufSize );

			iBufSize = 0;
			getsockopt( m_hUdpSocket, SOL_SOCKET, SO_RCVBUF, (char *)&iBufSize, (socklen_t*)&iIntSize );
			CLog::Print( LOG_DEBUG, "UDP RecvBufferSize = %d", iBufSize );

			setsockopt( m_hUdpSocket, SOL_SOCKET, SO_RCVBUF, (char *)&m_clsSetup.m_iUdpBufSize, iIntSize );

			iBufSize = 0;
			getsockopt( m_hUdpSocket, SOL_SOCKET, SO_RCVBUF, (char *)&iBufSize, (socklen_t*)&iIntSize );
			CLog::Print( LOG_SYSTEM, "UDP RecvBufferSize = %d", iBufSize );
		}
	}

	if( m_clsSetup.m_iLocalTcpPort > 0 )
	{
		m_hTcpSocket = TcpListen( m_clsSetup.m_iLocalTcpPort, 255, NULL, m_clsSetup.m_bIpv6 );
		if( m_hTcpSocket == INVALID_SOCKET ) 
		{
			CLog::Print( LOG_ERROR, "TcpListen(%d) error", m_clsSetup.m_iLocalTcpPort );
			_Stop();
			return false;
		}

		m_clsTcpThreadList.SetMaxSocketPerThread( m_clsSetup.m_iTcpMaxSocketPerThread );
		if( m_clsTcpThreadList.Init( m_clsSetup.m_iTcpThreadCount, m_clsSetup.m_iTcpThreadCount, SipTcpThread, this ) == false )
		{
			_Stop();
			return false;
		}

		if( StartSipTcpListenThread( this ) == false )
		{
			_Stop();
			return false;
		}
	}

#ifdef USE_TLS
	if( m_clsSetup.m_iLocalTlsPort > 0 )
	{
		if( SSLServerStart( m_clsSetup.m_strCertFile.c_str(), m_clsSetup.m_strCaCertFile.c_str() ) == false )
		{
			_Stop();
			return false;
		}

		m_hTlsSocket = TcpListen( m_clsSetup.m_iLocalTlsPort, 255, NULL, m_clsSetup.m_bIpv6 );
		if( m_hTlsSocket == INVALID_SOCKET ) 
		{
				CLog::Print( LOG_ERROR, "TcpListen(%d) error", m_clsSetup.m_iLocalTlsPort );
			_Stop();
			return false;
		}

		m_clsTlsThreadList.SetMaxSocketPerThread( m_clsSetup.m_iTcpMaxSocketPerThread );
		if( m_clsTlsThreadList.Init( m_clsSetup.m_iTcpThreadCount, m_clsSetup.m_iTcpThreadCount, SipTlsThread, this ) == false )
		{
			_Stop();
			return false;
		}

		if( StartSipTlsListenThread( this ) == false )
		{
			_Stop();
			return false;
		}
	}
	else if( m_clsSetup.m_bTlsClient )
	{
		if( SSLClientStart( ) == false )
		{
			_Stop();
			return false;
		}

		m_clsTlsThreadList.SetMaxSocketPerThread( m_clsSetup.m_iTcpMaxSocketPerThread );
		if( m_clsTlsThreadList.Init( m_clsSetup.m_iTcpThreadCount, m_clsSetup.m_iTcpThreadCount, SipTlsThread, this ) == false )
		{
			_Stop();
			return false;
		}
	}
#endif

	if( m_clsSetup.m_iLocalUdpPort > 0 )
	{
		if( StartSipUdpThread( this ) == false )
		{
			_Stop();
			return false;
		}
	}

	if( m_clsSetup.m_bStateful )
	{
		if( StartSipStackThread( this ) == false )
		{
			_Stop();
			return false;
		}
	}

	if( m_clsSetup.m_iTcpCallBackThreadCount > 0 )
	{
		for( int i = 0; i < m_clsSetup.m_iTcpCallBackThreadCount; ++i )
		{
			if( StartSipQueueThread( this ) == false )
			{
				_Stop();
				return false;
			}
		}
	}

	m_bStarted = true;

	return true;
}

/**
 * @ingroup SipStack
 * @brief SIP stack À» ÁßÁö½ÃÅ²´Ù.
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í SIP stack ÀÌ ½ÇÇàµÇÁö ¾Ê¾Ò°Å³ª Á¾·á ÀÌº¥Æ® Ã³¸® ÁßÀÌ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipStack::Stop( )
{
	if( m_bStarted == false || m_bStopEvent ) return false;

	_Stop();

	m_clsCallBackList.clear();

	m_bStarted = false;

	return true;
}

/**
 * @ingroup SipStack
 * @brief SIP stack À» ½ÇÇàÇÑ´Ù.
 *				SIP stack ÀÌ °ü¸®ÇÏ´Â Transaction List ¸¦ ÁÖ±âÀûÀ¸·Î Á¡°ËÇÏ¿©¼­ Re-Transmit ¶Ç´Â Timeout µîÀ» Ã³¸®ÇÏ±â À§ÇØ¼­ º» ÇÔ¼ö¸¦ 20ms °£°ÝÀ¸·Î È£ÃâÇØ ÁÖ¾î¾ß ÇÑ´Ù.
 * @param psttTime ÇöÀç ½Ã°£
 * @returns true ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipStack::Execute( struct timeval * psttTime )
{
	m_clsICT.Execute( psttTime );
	m_clsIST.Execute( psttTime );
	m_clsNICT.Execute( psttTime );
	m_clsNIST.Execute( psttTime );

	return true;
}

/**
 * @ingroup SipStack
 * @brief UDP SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå °³¼ö¸¦ Áõ°¡½ÃÅ²´Ù.
 * @param iThreadId UDP SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå °³¼ö¸¦ Áõ°¡½ÃÅ°±â Àü¿¡ UDP SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå °³¼ö¸¦ ÀúÀåÇÒ º¯¼ö
 */
void CSipStack::IncreateUdpThreadCount( int & iThreadId )
{
	m_clsMutex.acquire();
	iThreadId = m_iUdpThreadRunCount;
	++m_iUdpThreadRunCount;
	m_clsMutex.release();
}

/**
 * @ingroup SipStack
 * @brief UDP SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå °³¼ö¸¦ °¨¼Ò½ÃÅ²´Ù.
 */
void CSipStack::DecreateUdpThreadCount()
{
	m_clsMutex.acquire();
	--m_iUdpThreadRunCount;
	m_clsMutex.release();
}

/**
 * @ingroup SipStack
 * @brief TCP SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå °³¼ö¸¦ Áõ°¡½ÃÅ²´Ù.
 * @param iThreadId UDP SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå °³¼ö¸¦ Áõ°¡½ÃÅ°±â Àü¿¡ UDP SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå °³¼ö¸¦ ÀúÀåÇÒ º¯¼ö
 */
void CSipStack::IncreateTcpThreadCount( int & iThreadId )
{
	m_clsMutex.acquire();
	iThreadId = m_iTcpThreadRunCount;
	++m_iTcpThreadRunCount;
	m_clsMutex.release();
}

/**
 * @ingroup SipStack
 * @brief TCP SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå °³¼ö¸¦ °¨¼Ò½ÃÅ²´Ù.
 */
void CSipStack::DecreateTcpThreadCount()
{
	m_clsMutex.acquire();
	--m_iTcpThreadRunCount;
	m_clsMutex.release();
}

/**
 * @ingroup SipStack
 * @brief Transaction List ÀÇ Á¤º¸¸¦ ¹®ÀÚ¿­¿¡ ÀúÀåÇÑ´Ù.
 * @param strBuf		¹®ÀÚ¿­ º¯¼ö
 */
void CSipStack::GetString( CMonitorString & strBuf )
{
	strBuf.Clear();

	strBuf.AddCol( m_clsICT.GetSize() );
	strBuf.AddCol( m_clsNICT.GetSize() );
	strBuf.AddCol( m_clsIST.GetSize() );
	strBuf.AddCol( m_clsNIST.GetSize() );
	strBuf.AddCol( gclsSipDeleteQueue.GetSize() );
	strBuf.AddCol( gclsSipDeleteQueue.GetCacheSize() );
	strBuf.AddCol( m_clsICT.GetCallIdCount() );
	strBuf.AddRow( m_clsIST.GetCallIdCount() );
}

/**
 * @ingroup SipStack
 * @brief Invite Client Transaction Á¤º¸¸¦ ¹®ÀÚ¿­¿¡ ÀúÀåÇÑ´Ù.
 * @param strBuf ¹®ÀÚ¿­ º¯¼ö
 */
void CSipStack::GetICTString( CMonitorString & strBuf )
{
	m_clsICT.GetString( strBuf );
}

/**
 * @ingroup SipStack
 * @brief ÇÁ·Î¼¼½º°¡ Á¾·áµÉ ¶§¿¡ ÃÖÁ¾ÀûÀ¸·Î ½ÇÇàÇÏ¿©¼­ openssl ¸Þ¸ð¸® ´©¼ö¸¦ Ãâ·ÂÇÏÁö ¾Ê´Â´Ù. 
 */
void CSipStack::Final()
{
#ifdef USE_TLS
	SSLFinal();
#endif
}

/**
 * @ingroup SipStack
 * @brief ¸ðµç SIP transaction À» »èÁ¦ÇÑ´Ù.
 */
void CSipStack::DeleteAllTransaction()
{
	m_clsICT.DeleteAll();
	m_clsNICT.DeleteAll();
	m_clsIST.DeleteAll();
	m_clsNIST.DeleteAll();

	gclsSipDeleteQueue.DeleteAll();
}

/**
 * @ingroup SipStack
 * @brief ICT transcation map À» °¡Á®¿Â´Ù.
 * @param clsMap [out] transcation map ÀúÀå º¯¼ö
 */
void CSipStack::GetICTMap( INVITE_TRANSACTION_MAP & clsMap )
{
	m_clsICT.GetTransactionMap( clsMap );
}

void CSipStack::PrintReSendCount()
{
	printf( "ReSendCount ICT(%u) IST(%u) NICT(%u) NIST(%u)\n", m_clsICT.GetReSendCount(), m_clsIST.GetReSendCount(), m_clsNICT.GetReSendCount(), m_clsNIST.GetReSendCount() ); 
}

/**
 * @ingroup SipStack
 * @brief UDP SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå¿¡ Á¾·á SIP ¸Þ½ÃÁö¸¦ Àü¼ÛÇÏ°í SIP stack ¾²·¹µå¿¡ Á¾·á ÀÌº¥Æ®¸¦ ¼³Á¤ÇÑ ÈÄ, ¸ðµç ¾²·¹µå°¡ Á¾·áÇÒ ¶§±îÁö ´ë±âÇÑ ÈÄ,
 *				¼ÒÄÏ ÇÚµéÀ» Á¾·á½ÃÅ²´Ù.
 * @returns true ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipStack::_Stop( )
{
	m_bStopEvent = true;

	if( m_clsSetup.m_iLocalUdpPort > 0 )
	{
		// SIP ¸Þ½ÃÁö ¼ö½Å ¾²·¹µå°¡ N °³ ½ÇÇàµÇ¹Ç·Î N ÃÊ ´ë±âÇÏ´Â °ÍÀ» ¹æÁöÇÏ±â À§ÇÑ ÄÚµåÀÌ´Ù.
		Socket hSocket = UdpSocket();

		if( hSocket != INVALID_SOCKET )
		{
			for( int i = 0; i < m_clsSetup.m_iUdpThreadCount; ++i )
			{
				UdpSend( hSocket, "\r\n", 2, "127.0.0.1", m_clsSetup.m_iLocalUdpPort );
			}

			closesocket( hSocket );
		}
	}

	gclsSipQueue.BroadCast();

	// ¸ðµç ¾²·¹µå°¡ Á¾·áÇÒ ¶§±îÁö ´ë±âÇÑ´Ù.
	while( m_iUdpThreadRunCount > 0 || m_iTcpThreadRunCount > 0 || m_bStackThreadRun || GetTcpConnectingCount() > 0 )
	{
		MiliSleep( 20 );
	}

	if( m_hUdpSocket != INVALID_SOCKET )
	{
		closesocket( m_hUdpSocket );
		m_hUdpSocket = INVALID_SOCKET;
	}

	if( m_hTcpSocket != INVALID_SOCKET )
	{
		closesocket( m_hTcpSocket );
		m_hTcpSocket = INVALID_SOCKET;
	}

	m_clsTcpThreadList.Final();
	m_clsTcpSocketMap.DeleteAll();

#ifdef USE_TLS
	if( m_hTlsSocket != INVALID_SOCKET )
	{
		closesocket( m_hTlsSocket );
		m_hTlsSocket = INVALID_SOCKET;
	}

	m_clsTlsThreadList.Final();
	m_clsTlsSocketMap.DeleteAll();
	SSLServerStop();
#endif

	DeleteAllTransaction();

	m_bStopEvent = false;

	return true;
}

/**
 * @ingroup SipStack
 * @brief TCP/TLS ¿¬°á ÁøÇàÁßÀÎ ¾²·¹µå °³¼ö¸¦ ¸®ÅÏÇÑ´Ù.
 * @returns TCP/TLS ¿¬°á ÁøÇàÁßÀÎ ¾²·¹µå °³¼ö¸¦ ¸®ÅÏÇÑ´Ù.
 */
int CSipStack::GetTcpConnectingCount( )
{
	int iCount = m_clsTcpConnectMap.GetSize();

#ifdef USE_TLS
	iCount += m_clsTlsConnectMap.GetSize();
#endif

	return iCount;
}
