#include "StdAfx.h"
#include "ProtocolSocket.h"

#define	DEFAULT_TIMEOUT	(3 * 60 * 1000)

CProtocolSocket::CProtocolSocket(void)
{
	SetTimeout(DEFAULT_TIMEOUT);
}

CProtocolSocket::~CProtocolSocket(void)
{
}


//////////////////////////////////////////////////////////////////////////////////
//	入出力(同期)
//////////////////////////////////////////////////////////////////////////////////
/*!
	同期送信

	全てのデータを送信するまでブロック
*/
int CProtocolSocket::Send(const char *data, int len)
{
	//	エラー情報クリア
	ClearError();

	//	送信ループ
	while(1)
	{
		//	送信バッファに入れる
		if(len > 0)
		{
			int ret = WriteBuf(data, len);
			data += ret;
			len -= ret;
		}
		else if(GetSendInBuf() == 0)
			return(0);

		//	送信処理
		if(BlockingSend(m_timeoutMS) < 0)
			return(-1);
	}
}

/*!
	同期送信（文字列）

	全てのデータを送信するまでブロック
*/
int CProtocolSocket::SendString(const CUTF8String str)
{
	return(Send((const char *)str.GetString(), str.GetLength()));
};

/*!
	同期送信（CBinaryData）
*/
int CProtocolSocket::SendBinary(CBinaryData &data)
{
	return(Send(data.GetPtr(), data.GetSize()));
}


/*!
	同期受信

	何かデータを受信するまでブロック	
*/
int CProtocolSocket::Recv(char *data, int len)
{
	//	エラー情報クリア
	ClearError();

	//	受信の必要はあるか？
	if(GetRecvInBuf() == 0)
	{
		//	受信
		if(BlockingRecv(m_timeoutMS) < 0)
			return(-1);
	}

	//	バッファから読み出し
	return(ReadBuf(data, len));
}


/*!
	同期受信（CBinaryData）
*/
int CProtocolSocket::RecvBinary(CBinaryData &data, int maxSize)
{
	data.ReSize(maxSize);

	int ret = Recv(data.GetPtr(), maxSize);
	if(ret < 0)
		data.ReSize(0);
	else
		data.ReSize(ret);

	return(ret);
}

/*!
	受信を試みる
*/
int CProtocolSocket::Peek(char *data, int len)
{
	return PeekBuf(data, len);
}

/*!
	受信を試みる（CBinaryData）
*/
int CProtocolSocket::PeekBinary(CBinaryData &data, int maxSize)
{
	data.ReSize(maxSize);

	int ret = PeekBuf(data.GetPtr(), maxSize);
	if(ret < 0)
		data.ReSize(0);
	else
		data.ReSize(ret);

	return(ret);
}

//////////////////////////////////////////////////////////////////////////////////
//	プロトコル処理
//////////////////////////////////////////////////////////////////////////////////
/*!
	リクエスト受信を待つ
	リクエストを検出するまでブロック

	\return TRUE->検出、FALSE->タイムアウト
*/
int CProtocolSocket::WaitNextRequest(int maxWaitTime)
{
	//	エラー情報クリア
	ClearError();

	//	受信バッファチェック
	if(GetRecvInBuf() > 0)
		return(TRUE);

	//	開始時刻
	DWORD	start = GetTickCount();

	//	受信までループ
	while(1)
	{
		//	受信バッファチェック
		if(GetRecvInBuf() > 0)
			return(TRUE);

		//	切断？
		if(IsDisconnect())
		{
			CloseError(CBS_ERROR_CLOSE, _U16("WaitNextRequest"));
			return(FALSE);
		}

		//	タイムアウト？
		DWORD	now = GetTickCount();
		if(now < start || start + maxWaitTime <= now)
			return(FALSE);

		//	受信を試みる
		if(BlockingRecv(start + maxWaitTime - now) < 0)
			return(FALSE);
	}
}

/*!
	ヘッダ受信

	ヘッダを受信するまでブロック	
*/
int CProtocolSocket::RecvProtocolHeader(CUTF8String &head, CUTF8String endToken, int maxHeaderSize, int isPeek)
{
	//	チェック
	ASSERT(!endToken.IsEmpty());

	//	エラー情報クリア
	ClearError();

	//	最大サイズは
	if(maxHeaderSize >= GetSendRecvBufSize())
		maxHeaderSize = GetSendRecvBufSize()-1;

	//	受信ループ
	CBinaryData	bHead;
	while(1)
	{
		//	受信?
		int ret = GetHeaderFromBuf(bHead, endToken, maxHeaderSize, isPeek);
		if(ret < 0)
		{
			//	エラー
			return(-1);
		}
		else if(ret > 0)
		{
			//	文字列へ
			head = bHead.GetString();
			return(0);	//	完了
		}

		//	受信を試みる
		if(BlockingRecv(m_timeoutMS) < 0)
			return(-1);

		//	ヘッダが見つからない上に切断？
		if(IsDisconnect())
		{
			CloseError(CBS_ERROR_CLOSE, _U16("RecvProtocolHeader"));
			return(-1);
		}
	}
}

/*!
	ヘッダチェック
*/
int CProtocolSocket::GetHeaderFromBuf(CBinaryData &head, CUTF8String endToken, int maxHeaderSize, int isPeek)
{
	//	受信データを取得
	if(PeekBinary(head, maxHeaderSize) < 0)
		return(-1);

	//	ヘッダサイズチェック
	if(head.GetSize() > maxHeaderSize)
	{
		CloseError(CPS_ERROR_RECV_HEADER_OVER_SIZE, _U16("RecvProtocolHeader"));
		return(-1);
	}

	//	検索してみる
	int index = head.SerachString(endToken);
	if(index != -1 && index <= maxHeaderSize - endToken.GetLength())
	{
		//	tokenも含む
		index += endToken.GetLength();

		//	PeekMode?
		if(isPeek)
		{
			head.ReSize(index);
		}
		else
		{
			//	読み出し
			if(RecvBinary(head, index) < 0)
				return(-1);
		}

		//	ASCIIチェック
		if(CheckAllAscii(head))
			return(-1);
		return(1);	//	完了
	}

	return(0);	//	無し
}

/*!
	すべてASCIIか確認
*/
int CProtocolSocket::CheckAllAscii(CBinaryData &head)
{
	int	size = head.GetSize();
	unsigned char *ptr = (unsigned char *)head.GetPtr();

	//	チェック
	for(int i=0;i<size;i++)
	{
		if(*ptr == 0 || *ptr >= 0x80)
			return(-1);
		*ptr++;
	}
	return(0);
}

//////////////////////////////////////////////////////////////////////////////////
//	エラー
//////////////////////////////////////////////////////////////////////////////////
/*!
	エラーコードから文字列を取得
*/
CUTF8String CProtocolSocket::GetErrorString(int errorCode)
{
	switch(errorCode)
	{
	case CPS_ERROR_RECV_HEADER_OVER_SIZE:
		return(_U16("不正なヘッダを受信しました"));

	default:
		return(CBufferdSocket::GetErrorString(errorCode));
	}
}
