#include "StdAfx.h"
#include "Network.h"
#include "../common/CommonNetworkMsgStruct.h"
#include "../Common/Protocol.h"
#include "../Common/Cheat_Common.h"


cNetwork::cNetwork(void) : 	mSendQueue(50)
{
	mSendMsg = new char[QUEUE_BUFFER_SIZE];
	memset( mSendMsg, 0, sizeof(mSendMsg) );
	mSendMsgTemp = new char[QUEUE_BUFFER_SIZE];
	memset( mSendMsgTemp, 0, sizeof(mSendMsgTemp) );

	mRecvMsg = new char[QUEUE_BUFFER_SIZE];
	memset( mRecvMsg, 0, sizeof(mRecvMsg) );
	mRecvMsgTemp = new char[QUEUE_BUFFER_SIZE];
	memset( mRecvMsgTemp, 0, sizeof(mRecvMsgTemp) );
	mRecvMsgPos = 0;

	mIsConnect = false;

	mPingDelayTime = 0;
	mUserCnt = 0;

	mPingCheckTime = GetTickCount() + 3000;
	mConnectCheckTime = GetTickCount() + 1000;

	mNumber = 0;
}



cNetwork::~cNetwork(void)
{
	delete []mRecvMsg;
	delete []mRecvMsgTemp;
	delete []mSendMsg;
	mRecvMsg = NULL;
	mRecvMsgTemp = NULL;
	mSendMsg = NULL;

	/// queue에 데이타가 있으면 모두 전송
	while( mSendQueue.IsEmpty() == false )
	{
		/// queue의 첫번째 데이타를 가져옴
		char* sendmsg = mSendQueue.Front();
		delete []sendmsg;
		sendmsg = NULL;
	}
	mSendQueue.Clear();

	closesocket(mSocket);
	mSocket = INVALID_SOCKET;
}


bool cNetwork::NetworkMsgParser( WPARAM wParam, char* pMsg )
{
	if( mSocket != (SOCKET)wParam )
		return false;

	return true;
}


bool cNetwork::Init( HWND hwnd, unsigned long number, char* ip, int port, CServerToolDlg* pDlg )
{
	mpDlg = pDlg;

	mHwnd = hwnd;
	memcpy( mIp, ip, sizeof(mIp) );
	mPort = port;

	mNumber = number;

	/// 기존에 접속되있던 접속을 끊음
	mIsConnect = false;

	mIsSubInfo = false;
	mSubInfoTime = 0;	

	mSocket = socket( AF_INET, SOCK_STREAM, 0 );
	if( mSocket == INVALID_SOCKET ) 
	{ 
		assert(NULL);
		MessageBox( NULL, _T("New Socket ERROR"), _T("ERROR"), NULL );
		return false; 
	}

	mSockaddr.sin_family = AF_INET;
	mSockaddr.sin_addr.s_addr = inet_addr(mIp);
	mSockaddr.sin_port = htons(mPort);
	
	Connect();

	return true;
}


bool cNetwork::Connect()
{
	if( mIsConnect == true )
		return false;

	int errorcode;

	if( WSAAsyncSelect( mSocket, mHwnd, WM_ASYNC_WRC, FD_CONNECT ) == SOCKET_ERROR ) 
	{
		assert(NULL);
		MessageBox( NULL, _T("Async Select ERROR"), _T("ERROR"), NULL );
		return false;
	}

	errorcode = connect( mSocket, (LPSOCKADDR)&mSockaddr, sizeof(mSockaddr));
	if( errorcode == SOCKET_ERROR ) 
	{
		if( WSAGetLastError() != WSAEWOULDBLOCK ) 
			return false; 
	}

	return true;
}

bool cNetwork::FDConnect( WPARAM wParam )
 {
	 if( mSocket != (SOCKET)wParam )
		 return false;

	if( WSAAsyncSelect( mSocket, mHwnd, WM_ASYNC_WRC, FD_READ|FD_WRITE|FD_CLOSE ) == SOCKET_ERROR ) 
	{
		assert(NULL);
		MessageBox( NULL, _T("Async Select ERROR"), _T("ERROR"), NULL );
		return false;
	}

	return true;
 }

bool cNetwork::DisConnect( WPARAM wParam )
{
	if( mSocket != (SOCKET)wParam )
		return false;

	mIsConnect = false;

	closesocket(mSocket);
	mSocket = INVALID_SOCKET;

	Init( mHwnd, mNumber, mIp, mPort, mpDlg );

	return true;
}


bool cNetwork::Send( char* msg, unsigned long length )
{

	/// queue에 저장되있던 데이타를 먼저 보냄
	QSend();

	/// 2차send버퍼 초기화및 형변환
	memset( mSendMsg, 0, sizeof(mSendMsg) );
	Packet* pMsg = (Packet*)mSendMsg;

	static int i = 0;

	/// 보낼 메세지 설정
	pMsg->ver  = 1;
	pMsg->hlen = sizeof(Packet);
	pMsg->tos = TOS_SERVERTOOL;
	pMsg->tlen = static_cast<u_short>( sizeof(Packet) + length );

	memcpy( mSendMsg + sizeof( Packet ) , msg, length );

	/// 보낼 메세지를 queue에 등록
	char* sendmsg;
	sendmsg = new char[QUEUE_BUFFER_SIZE];
	memcpy( sendmsg, mSendMsg, pMsg->tlen );
	if( mSendQueue.IsFull() == true )
	{
		assert(NULL);
		MessageBox( NULL, _T("Queue is full"), _T("ERROR"), NULL );
		return false;
	}
	mSendQueue.PushBack( sendmsg );

	/// queue에 저장된 내용을 모두 전송
	return QSend();
}


void cNetwork::RetrySend( WPARAM wParam )
{
	if( mSocket != (SOCKET)wParam )
		return;

	QSend();
}


bool cNetwork::QSend()
{
	/// queue에 데이타가 있으면 모두 전송
	while( mSendQueue.IsEmpty() == false )
	{

		/// queue의 첫번째 데이타를 가져옴
		char* sendmsg = mSendQueue.Front();
		Packet* pMsg = (Packet*)sendmsg;

		/// 데이타 전송
		int errorcode;
		errorcode = send( mSocket, sendmsg, pMsg->tlen, 0 );
		if( errorcode == SOCKET_ERROR )
		{
			if( WSAGetLastError() != WSAEWOULDBLOCK ) 
			{ 
				return false; 
			}
			else
			{
				/// WSAEWOULDBLOCK 인경우 메세지 재전송
				continue;
			}
		}

		/// queue에서 데이타 제거
		mSendQueue.PopFront();
		pMsg = NULL;
		delete []sendmsg;
		sendmsg = NULL;
	}

	return true;
}


void cNetwork::PingSend()
{
	if( mIsConnect == false )
		return;

	/// 버퍼 초기화
	memset( mSendMsgTemp, 0, sizeof(mSendMsgTemp) );
	Packet* pMsg = (Packet*)mSendMsgTemp;

	/// 상세 메세지
	MSG_REQ_SRVTOOL_PING sendMsg;

	/// 보낼 메세지 헤더 설정
	pMsg->ver  = 1;
	pMsg->hlen = sizeof(Packet);
	pMsg->tos = TOS_SERVERTOOL;
	pMsg->tlen = static_cast<u_short>( sizeof(Packet) + sizeof(sendMsg) );

	/// 헤더 기록
	memcpy( mSendMsgTemp, pMsg, pMsg->hlen );

	sendMsg.Category = NM_SERVERTOOL;
	sendMsg.Protocol = NM_SERVERTOOL_PING_REQ;
	sendMsg.mToolSendTime = GetTickCount();

	/// 헤더뒤에 상세 메세지 기록
	memcpy( mSendMsgTemp + sizeof( Packet ), &sendMsg, pMsg->tlen );

	/// 데이타 전송
	int errorcode;
	errorcode = send( mSocket, mSendMsgTemp, pMsg->tlen, 0 );

	return;
}


eRECV_RETURN cNetwork::Recv( char* msg, int* size )
{
	int recvsize;

	do
	{
		if( QUEUE_BUFFER_SIZE - mRecvMsgPos < BUFFER_SIZE )
		{
			assert(NULL);
			return eERROR; 
		}

		/// 데이타를 담아두는 버퍼에 끝부분에 데이타 추가
		recvsize = recv( mSocket, &mRecvMsg[mRecvMsgPos], QUEUE_BUFFER_SIZE - mRecvMsgPos, 0 );
		/// 데이타 에러처리
		if( recvsize == SOCKET_ERROR ) 
		{ 
			/// recv WOULDBLOCK 처리
			if( WSAGetLastError() != WSAEWOULDBLOCK ) 
			{
				assert(NULL);
				MessageBox( NULL, _T("Recv SOCKET ERROR"), _T("ERROR"), NULL );
				return eERROR; 
			}
		}

		/// 정상 데이타입력인 경우 데이타를 담아두는 버퍼끝위치 재지정
		if( recvsize > 0 )
			mRecvMsgPos += recvsize;

	}while( recvsize > 0 ); /// 더 받을 데이타가 있으면 루프지속

	/// 버퍼에 저장된 데이타가 완전한 1개가 못되는경우 종료
	if( mRecvMsgPos < sizeof(Packet) ) 
		return eINCOMPLETION;

	/// 메세지 헤더 판독
	Packet* pPhv1 = (Packet*)mRecvMsg;
	/// 데이타의 전체사이즈를 알아옴
	int msgSize = pPhv1->tlen;

	if( !( pPhv1->tos == TOS_LOGIN || pPhv1->tos == TOS_GAME || 
		pPhv1->tos == TOS_LOGIN_ACCEPT || pPhv1->tos == TOS_GAME_ACCEPT) )
	{
		assert(NULL);
		memset( mRecvMsg, 0, sizeof(char) * QUEUE_BUFFER_SIZE );
		mRecvMsgPos = 0;
	}

	/// 받은 메세지 전체길이가 완전한지 체크
	if( mRecvMsgPos < pPhv1->tlen )
		return eINCOMPLETION;
	else if( sizeof(Packet) == pPhv1->tlen )
	{
		/// 복사한 메세지 제거 & 공간 정렬
		memset( mRecvMsgTemp, 0, sizeof(mRecvMsgTemp) );					/// 임시공간 clear
		memcpy( mRecvMsgTemp, &mRecvMsg[msgSize], mRecvMsgPos - msgSize);	/// 남은메세지 임시공간에 저장
		memset( mRecvMsg, 0, sizeof(mRecvMsg) );							/// 주메모리공간 clear
		memcpy( mRecvMsg, mRecvMsgTemp, mRecvMsgPos - msgSize );			/// 임시공간에있는 잔여메세지 주메모리공간에 저장

		mRecvMsgPos = mRecvMsgPos - msgSize;
		*size = 0;
		if( mRecvMsgPos <= 0 )
            return eINCOMPLETION;
		else
			return eCONTINUE;
	}

    /// 헤더 이후의 실제 리턴 할 메세지를 복사
	memcpy( msg, &mRecvMsg[sizeof(Packet)], msgSize - sizeof(Packet) );
	/// 처리할 패킷의 사이즈
	*size = msgSize;

	/// ping 메세지 별도처리
	CalcPing( (MSGROOT*)msg );

	/// 복사한 메세지 제거 & 공간 정렬
	memset( mRecvMsgTemp, 0, sizeof(mRecvMsgTemp) );					/// 임시공간 clear
	memcpy( mRecvMsgTemp, &mRecvMsg[msgSize], mRecvMsgPos - msgSize);	/// 남은메세지 임시공간에 저장
	memset( mRecvMsg, 0, sizeof(mRecvMsg) );							/// 주메모리공간 clear
	memcpy( mRecvMsg, mRecvMsgTemp, mRecvMsgPos - msgSize );			/// 임시공간에있는 잔여메세지 주메모리공간에 저장

	mRecvMsgPos = mRecvMsgPos - msgSize;

	/// 잔여 데이타가 완전한 1개가 못되는경우 종료
	if( mRecvMsgPos < sizeof(Packet) ) 
		return eCOMPLETION;

	/// 잔여 메세지 헤더 판독
	pPhv1 = (Packet*)mRecvMsg;
	/// 잔여 데이타의 전체사이즈를 알아옴
	msgSize = pPhv1->tlen;

	/// 잔여 메세지 전체길이가 완전한지 체크
	if( mRecvMsgPos < pPhv1->tlen )
		return eCOMPLETION;
	else
		return eCONTINUE; 
}


void cNetwork::NoticeSend( LPCTSTR chatmsg )
{
	MSG_REQ_CHEAT_NOTICE_CHANNEL sendMsgCh;
	::memset( &sendMsgCh, 0, sizeof( sendMsgCh ) );

	sendMsgCh.Category = NM_SERVERTOOL;
	sendMsgCh.Protocol = NM_SERVERTOOL_CHAT_NOTICE_REQ;

	Sstrncpy( sendMsgCh.mMessage, MAX_CHAT_BUFFER_SIZE, chatmsg, MAX_CHAT_SIZE );

	Send( (char*)&sendMsgCh, sendMsgCh.GetMsgLength() );
}


void cNetwork::CalcPing( MSGROOT* pMsg )
{
	/// ping 메세지 인지 확인
	MSGROOT* pRootMsg = (MSGROOT*)pMsg;
	if( pRootMsg->Category != NM_SERVERTOOL && pRootMsg->Protocol != NM_SERVERTOOL_PING_RES )
		return;

	/// 현재 시간과 보냈을당시의 시간 비교
	MSG_RES_SRVTOOL_PING* pPingMsg = (MSG_RES_SRVTOOL_PING*)pRootMsg;

	unsigned long time = GetTickCount();
	mPingDelayTime = time - pPingMsg->mToolSendTime;

	mUserCnt = pPingMsg->mUserCnt;
}