// Include
#include "gamesrv.h"

// Local definitions
#pragma warning( disable: 4127 )

// Global data

// cRecver Constructor
cRecver::cRecver(void)
{}

// ~cRecver Destructor
cRecver::~cRecver(void)
{}

// Initialize Method
bool cRecver::Initialize(char* ipAddr, unsigned short port, unsigned short numWorkerThreads)
{
	return IocpUdpRecv::Initialize( ipAddr, port, numWorkerThreads );
}

// Shutdown Method
void cRecver::Shutdown(DWORD maxWait)
{
	Sleep( 50 );
	IocpUdpRecv::Shutdown( maxWait );
}

// PostServerEvent Method
bool cRecver::PostServerEvent(LPCTSTR format, ...)
{
	bool retvalue = false;
	if ( g_gameSrv != NULL )
	{
		LPVOID  msgBuf = NULL;
		DWORD   bufferLength;

		va_list args;

		va_start( args, format );

		bufferLength = _vscprintf( format, args ) + 1;
		msgBuf       = malloc( bufferLength );

		vsprintf( (char*)msgBuf, format, args );

		va_end( args );

		if ( msgBuf != NULL )
		{
			retvalue = g_gameSrv->PostServerEvent( (char*)msgBuf );
			free( msgBuf );
		}
	}
	return retvalue;
}

// BatchComplete Method
bool cRecver::BatchComplete(char* ptr)
{
	cCSLock lock( &m_cs );
	Packet* packet = (Packet*)ptr;

	if ( g_packet == true )
	{
		// 패킷해더(Packet Header) 정보 출력 - 버전/헤더길이/서비스 종류/전체길이.
		printf( "RECVER::RECV COMPLETE::VER:%d/HLEN:%d/TOS:%d/TLEN:%d\n",
				packet->ver,
				packet->hlen,
				packet->tos,
				packet->tlen );
	}

	// 서비스 타입이 [TOS_LOGIN] 또는 [TOS_GAME] 아닐경우 오류 처리.
	// 데이터의 길이가 최소 단위보다 작을 경우 오류 처리.
	if ( packet->tos != TOS_GAME || packet->hlen != PHLen )
		return false;

	MSGBUF*       msgBuf      = (MSGBUF*)(ptr + packet->hlen);
	int           msgLen      = packet->tlen - packet->hlen;
	cGameProcess* gameProcess = g_gameSrv->GetGameProcess( );

	if ( gameProcess == NULL )
		return false;
	else if ( gameProcess->IsChannelCheck( ) == false )
		return false;

	switch ( msgBuf->protocol )
	{
	case MB_CHAT_SHOUT_SYN:
		{
			MB_SYN_CHAT_SHOUT* recvMsg = (MB_SYN_CHAT_SHOUT*)msgBuf;
			gameProcess->ChatShout( recvMsg->cid, recvMsg->numOfMap, recvMsg->characterIdx, recvMsg->name, recvMsg->message );
		}
		break;
	case MB_CHAT_SYN:
		{
			MB_SYN_CHAT_SYNC* recvMsg = (MB_SYN_CHAT_SYNC*)msgBuf;
			int				  len	  = msgLen - (sizeof(MB_CHAT_SYN) - sizeof(recvMsg->msg) );
			gameProcess->ChatSync( recvMsg->cid, recvMsg->msg, len );
		}
		break;
	case MB_SERVER_NOTICE_SYN:
		{
			MB_SYN_SERVER_NOTICE* recvMsg = (MB_SYN_SERVER_NOTICE*)msgBuf;
			gameProcess->ServerNotice( recvMsg->cid, recvMsg->message );
		}
		break;
	case MB_GUILD_SYNC_SYN:
		{
			MB_SYN_GUILD_SYNC* recvMsg = (MB_SYN_GUILD_SYNC*)msgBuf;
			int                len     = msgLen - (sizeof(MB_SYN_GUILD_SYNC) - sizeof(recvMsg->msg));
			gameProcess->GuildSync( recvMsg->cid, recvMsg->msg, len );
		}
		break;
	case MB_GUILD_CONNECTSYNC_SYN:
		{
			MB_SYN_GUILD_CONNECTSYNC* recvMsg = (MB_SYN_GUILD_CONNECTSYNC*)msgBuf;
			gameProcess->GuildConnectSync( recvMsg->cid );
		}
		break;
	case MB_PVP_STATUS_SYN:
		{
			MB_SYN_PVP_STATUS* recvMsg = (MB_SYN_PVP_STATUS*)msgBuf;
			gameProcess->PVPStatus( recvMsg->cid, recvMsg->status );
		}
		break;
	case MB_PVP_NOTICE_SYN:
		{
			MB_SYN_PVP_NOTICE* recvMsg = (MB_SYN_PVP_NOTICE*)msgBuf;
			gameProcess->PVPNotice( recvMsg->cid, recvMsg->leftMinute );
		}
		break;
	case MB_CH_STATUS_SYN:
		{
			MB_SYN_CH_STATUS* recvMsg = (MB_SYN_CH_STATUS*)msgBuf;
			gameProcess->ChannelStatus( recvMsg->cid, recvMsg->status );
		}
		break;
	case MB_CH_LIST_SYN:
		{
			MB_SYN_CH_LIST* recvMsg = (MB_SYN_CH_LIST*)msgBuf;
			for ( BYTE i = 0; i < recvMsg->rowCount; i++ )
				gameProcess->ChannelStatus( recvMsg->channels[ i ].cid, recvMsg->channels[ i ].status );
		}
		break;
	case MB_MONSTER_SYN_SYN:
		{
			MB_SYN_MONSTER_SYNC* pRecMsg = (MB_SYN_MONSTER_SYNC*)msgBuf;
			int len = msgLen - (sizeof(MB_SYN_MONSTER_SYNC) - sizeof(pRecMsg->msg));
			gameProcess->MonsterSync( pRecMsg->cid, pRecMsg->msg, len );
		}
		break;
	case MB_CH_SYNC_SYN:
		gameProcess->ChannelSync( );
		break;
	default:
		return false;
	}
	return true;
}

// RecvComplete Method
//
// UDP:.......User Datagram Protocol.
// Datagram:..패킷 교환에서, 데이터 단말 장치와 망과의 사전 접속 절차에 의하지 않고,
//            하나하나의 패킷이 발신 데이터 단말 장치와 수신처 데이터 단말 장치 간의
//            경로 지정을 위한 충분한 정보를 가지고 있는 패킷.
bool cRecver::RecvComplete(ULONG_PTR completionKey, PerIoContext* perIoContext, DWORD bytesTransferred)
{
	cCSLock lock( &m_cs );

	// 수신된 데이터 처리.
	perIoContext->offset = bytesTransferred;

	// 더블버퍼링 처리.
	PerIoContext** buffer = m_ioContextBackBuffer->buffer;
	long&          offset = m_ioContextBackBuffer->offset;

	if ( offset < MAX_IO_CONTEXT_BUFFER_LEN )
	{
		buffer[ offset ] = perIoContext;
		offset++;
	}
	else
	{
		m_ioContextPool->ReleaseIoContext( perIoContext );
		PostServerEvent( "Warning - cRecver::RecvComplete Method - Overflow I/O Context Buffer." );
	}

	// 수신을 위해 I/O Context를 준비한다.
	perIoContext = m_ioContextPool->GetIoContext( m_socket, IOCP_REQUEST_READ );
	if ( RecvPost( completionKey, perIoContext ) == false )
	{
		PostServerEvent( "Error - cRecver::RecvPost Method - Return value is false." );
		return false;
	}
	return true;
}

// BackendThread Method
DWORD cRecver::BackendThread( )
{
	DWORD currentTick;
	DWORD elapsedTick;

	try {
		while ( true )
		{
			currentTick = GetTickCount( );

			// 서버종료, Thread를 나간다.
			if ( m_endServer == true )
				break;

			// Double Buffering 처리.
			EnterCriticalSection( &m_cs );

				IoContextBuffer* temp  = m_ioContextBackBuffer;
				m_ioContextBackBuffer  = m_ioContextFrontBuffer;
				m_ioContextFrontBuffer = temp;

			LeaveCriticalSection( &m_cs );

			// 수신된 데이터 처리.
			PerIoContext** buffer = m_ioContextFrontBuffer->buffer;
			long&          offset = m_ioContextFrontBuffer->offset;

			while ( offset > 0 )
			{
				try {
					BatchComplete( (*buffer)->buffer );
				} catch ( char* str ) {
					PostServerEvent( "Caught 'BatchComplete' exception type: '%s'. Throwing 'cRecver::BackendThread' exception", str );
					Sleep( 100 );
					throw;
				} catch ( ... ) {
					PostServerEvent( "In BatchComplete Method. Throwing 'cRecver::BackendThread' exception." );
					Sleep( 100 );
					throw;
				}
				m_ioContextPool->ReleaseIoContext( (*buffer) );

				buffer++;
				offset--;
			}

			// 지연시간 약16(ms) - CPU 과부화 방지 & 내부 동기화.
			elapsedTick = GetTickCount( ) - currentTick;
			if ( elapsedTick < 16 )
			{
				Sleep( (16 - elapsedTick) );
			}
		}
	} catch ( char* str ) {
		PostServerEvent( "Caught 'while ( true ) { ... }' exception type: '%s'. Throwing 'cRecver::BackendThread' exception", str );
		Sleep( 100 );
		throw;
	} catch ( ... ) {
		PostServerEvent( "In while ( true ) { ... }. Throwing 'cRecver::BackendThread' exception." );
		Sleep( 100 );
		throw;
	}
	return 0;
}
