#include "StdAfx.h"
#include ".\bufferdsocket.h"
#include "threadskeleton.h"
#include <ws2tcpip.h>

#define	BUF_SIZE			(32*1024)

CBufferdSocket::CBufferdSocket(void)
{
	//	イベント生成
	m_closeEvent.Create(TRUE,FALSE);
	m_exBreakEvent = NULL;

	//	メモリの確保
	m_recvBuf.InitBuf(BUF_SIZE);
	m_sendBuf.InitBuf(BUF_SIZE);
}


CBufferdSocket::~CBufferdSocket(void)
{
	Close();
	m_recvBuf.Close();
	m_sendBuf.Close();
}

//////////////////////////////////////////////////////////////////////////////////
//	初期化など
//////////////////////////////////////////////////////////////////////////////////
/*!
	使用開始
*/
int CBufferdSocket::AttachSocket(SOCKET sock)
{
	//	終了
	Close();

	//	ソケットを非ブロッキングモードへ
	if(::WSAEventSelect(sock, m_closeEvent, FD_CLOSE))
	{
		CloseError(CBS_ERROR_OTHER,"AttachSocket");
		return(-1);
	}

	//	バッファクリア
	m_recvBuf.Clear();
	m_sendBuf.Clear();

	//	変数設定
	m_socket.AttachSocket(sock);
	ClearError();

	//	イベントクリア
	::ResetEvent(m_closeEvent);

	return(0);
}


/*!
	閉じる
*/
void CBufferdSocket::Close()
{
	if(m_socket != INVALID_SOCKET)
	{
		//	ソケットからハンドルを関連付け解除
		::WSAEventSelect(m_socket, m_closeEvent, 0);

		//	閉じる
		m_socket.Close();
	}


	//	閉じた後も読み出す可能性があるので、バッファはオブジェクトが削除されるまで開放しない
}

//////////////////////////////////////////////////////////////////////////////////
//	中断インターフェース
//////////////////////////////////////////////////////////////////////////////////
/*!
	中断イベントを設定
*/
void CBufferdSocket::SetExBreakEvent(HANDLE exBreakEvent)
{
	m_exBreakEvent = exBreakEvent;
	m_socket.SetBreakEvent(m_exBreakEvent);
}


//////////////////////////////////////////////////////////////////////////////////
//	送受信(非同期)
//////////////////////////////////////////////////////////////////////////////////
/*!
	送信
*/
int CBufferdSocket::WriteBuf(char *data,int len)
{
	//	エラー情報クリア
	ClearError();

	return(m_sendBuf.WriteBuf(data,len));
}


/*!
	受信
*/
int CBufferdSocket::ReadBuf(char *data,int len)
{
	//	エラー情報クリア
	ClearError();

	return(m_recvBuf.ReadBuf(data,len));
}


/*!
	受信(バッファを消さない)
*/
int CBufferdSocket::PeekBuf(char *data,int len)
{
	//	エラー情報クリア
	ClearError();

	return(m_recvBuf.PeekBuf(data,len));
}



/*!
	ソケットが閉じてるかチェック

	戻り値: TRUE:閉じてる
*/
int CBufferdSocket::CheckCloseEvent()
{
	//	エラー情報クリア
	ClearError();

	//	ソケットは？
	if(m_socket == INVALID_SOCKET)
		return(TRUE);

	//	イベントを待つ
	switch(CThreadSkeleton::WaitForSomeObjects(0, m_closeEvent, m_exBreakEvent))
	{
	case 0:
		return(FALSE);

	case 1:
		CloseError(CBS_ERROR_CLOSE,"WaitNextEvent");
		break;

	case 2:
		CloseError(CBS_ERROR_BREAK,"WaitNextEvent - 中断");
		break;

	default:
		CloseError(CBS_ERROR_OTHER,"WaitNextEvent - WaitForMultipleObjects");
		break;
	}
	return(TRUE);
}

//////////////////////////////////////////////////////////////////////////////////
//	通信ステータス
//////////////////////////////////////////////////////////////////////////////////
/*!
	切断しているか取得
*/
int CBufferdSocket::IsDisconnect()
{
	//	イベントを待ってみる
	return CheckCloseEvent();
}

/*!
	バッファサイズ取得
*/
int CBufferdSocket::GetSendRecvBufSize()
{
	return(BUF_SIZE);
}

//////////////////////////////////////////////////////////////////////////////////
//	接続情報
//////////////////////////////////////////////////////////////////////////////////
/*!
	自分のアドレスを取得する
*/
CIPAddress CBufferdSocket::GetAddres()
{
	sockaddr_in	addr;
	memset(&addr, 0, sizeof(sockaddr_in));
	int nSockAddrLen = sizeof(sockaddr_in);

	getsockname(m_socket,(SOCKADDR*)&addr,&nSockAddrLen);
	return(addr.sin_addr);
}

/*!
	相手のアドレスを取得する
*/
CIPAddress CBufferdSocket::GetPeerAddres()
{
	sockaddr_in	addr;
	memset(&addr, 0, sizeof(sockaddr_in));
	int nSockAddrLen = sizeof(sockaddr_in);

	getpeername(m_socket,(SOCKADDR*)&addr,&nSockAddrLen);
	return(addr.sin_addr);
}

/*!
	自分のポートを取得する	
*/
int CBufferdSocket::GetPort()
{
	sockaddr_in	addr;
	memset(&addr, 0, sizeof(sockaddr_in));
	int nSockAddrLen = sizeof(sockaddr_in);

	getsockname(m_socket,(SOCKADDR*)&addr,&nSockAddrLen);
	return(ntohs(addr.sin_port));
}

/*!
	相手のポートを取得する
*/
int CBufferdSocket::GetPeerPort()
{
	sockaddr_in	addr;
	memset(&addr, 0, sizeof(sockaddr_in));
	int nSockAddrLen = sizeof(sockaddr_in);

	getpeername(m_socket,(SOCKADDR*)&addr,&nSockAddrLen);
	return(ntohs(addr.sin_port));
}


//////////////////////////////////////////////////////////////////////////////////
//	通信処理
//////////////////////////////////////////////////////////////////////////////////
/*!
	送信を試す

	\return	-1:エラー、0:送信完了、>0:残りデータ量
*/
int CBufferdSocket::BlockingSend(int timeOut)
{
	//	データがある?
	if(m_sendBuf.GetInBuf() == 0)
		return(0);

	//	送信用バッファ取得
	int		bufLen;
	char	*sendBuf = m_sendBuf.GetReadBuffer(&bufLen);

	//	送信
	int sended = m_socket.BlockingSend(sendBuf, bufLen, timeOut);
	if(sended <= 0)
	{
		switch(m_socket.GetLastError())
		{
		case COverlappedSocket::OVRS_ERROR_TIMEOUT:
			CloseError(CBS_ERROR_TIMEOUT,"BlockingSend");
			break;

		case COverlappedSocket::OVRS_ERROR_CLOSE:
			CloseError(CBS_ERROR_CLOSE,"BlockingSend");
			break;

		case COverlappedSocket::OVRS_ERROR_BREAK:
			CloseError(CBS_ERROR_BREAK,"BlockingSend");
			break;

		case COverlappedSocket::OVRS_ERROR_OTHER:
		default:
			CloseError(CBS_ERROR_OTHER,"BlockingSend");
			break;
		}
		return(-1);
	}

	//	送信したデータ量を設定
	m_sendBuf.SetReadedLen(sended);
	return(m_sendBuf.GetInBuf());
}


/*!
	受信を試す

	\return	-1:エラー、0:受信しなかった、>0:受信データ量
*/
int CBufferdSocket::BlockingRecv(int timeOut)
{
	//	バッファがあるか？
	if(m_recvBuf.GetFreeBuf() == 0)
		return(m_recvBuf.GetInBuf());	//	データ受信済みなら終了

	//	受信用バッファ取得
	int		bufLen;
	char	*recvBuf = m_recvBuf.GetWriteBuffer(&bufLen);

	//	受信
	int recved = m_socket.BlockingRecv(recvBuf, bufLen, timeOut);
	if(recved <= 0)
	{
		switch(m_socket.GetLastError())
		{
		case COverlappedSocket::OVRS_ERROR_TIMEOUT:
			CloseError(CBS_ERROR_TIMEOUT,"BlockingRecv");
			break;

		case COverlappedSocket::OVRS_ERROR_CLOSE:
			CloseError(CBS_ERROR_CLOSE,"BlockingSend");
			break;

		case COverlappedSocket::OVRS_ERROR_BREAK:
			CloseError(CBS_ERROR_BREAK,"BlockingRecv");
			break;

		case COverlappedSocket::OVRS_ERROR_OTHER:
		default:
			CloseError(CBS_ERROR_OTHER,"BlockingRecv");
			break;
		}
		return(-1);
	}

	//	受信したデータ量を設定
	m_recvBuf.SetWritedLen(recved);
	return(m_recvBuf.GetInBuf());
}


//////////////////////////////////////////////////////////////////////////////////
//	エラー
//////////////////////////////////////////////////////////////////////////////////
/*!
	エラーで閉じる
*/
void CBufferdSocket::CloseError(int code, CString debugInfo)
{
	TRACE("SocketError : %d [%s] (SocketError : %d)\n", code, debugInfo, WSAGetLastError());
	m_lastError = code;

	//	ソケットを閉じる
	if(m_socket != INVALID_SOCKET)
		Close();
}

/*!
	エラーを文字列で取得
*/
CString CBufferdSocket::GetLastErrorString()
{
	return(GetErrorString(GetLastError()));
}

/*!
	エラーコードから文字列を取得
*/
CString CBufferdSocket::GetErrorString(int errorCode)
{
	switch(errorCode)
	{
	case CBS_ERROR_TIMEOUT:
		return(_T("タイムアウトしました"));

	case CBS_ERROR_CLOSE:
		return(_T("接続が終了しました"));

	case CBS_ERROR_BREAK:
		return(_T("中断しました"));

	case CBS_ERROR_OTHER:
		return(_T("ネットワークエラーです"));


	case CBS_NO_ERROR:
	default:
		return(_T("不明なエラーです"));
	}
}
