#include "StdAfx.h"
#include "Listener.h"
#include "TextTools.h"
#include "ServerStartException.h"


CListener::CListener(void)
{
}

CListener::~CListener(void)
{
	StopListener();
}



/////////////////////////////////////////////////////////////////////////////////////
//	操作
/////////////////////////////////////////////////////////////////////////////////////
/*!
	初期化
*/
void CListener::InitListener(CAcceptedSocketQueue *queue, int socketType, int errorRetryTime)
{
	m_queue = queue;
	m_socketType = socketType;
	m_errorRetryTime = errorRetryTime;
}

/*!
	開始
*/
void CListener::StartListener()
{
	if(OpenPorts())
		throw CServerStartException(_T("通信ポートのオープンに失敗しました、ポート番号を確認してください。"));

	if(StartThread())
		throw CServerStartException(_T("スレッドの起動に失敗しました。"));
}

/*!
	停止
*/
void CListener::StopListener()
{
	//	スレッド停止
	EndThread();

	//	掃除
	CloseAllPorts();
}

/*!
	クリーンアップ
*/
void CListener::CloseAllPorts()
{
	//	掃除
	for(int i=0;i<m_listeners.GetSize();i++)
		delete m_listeners[i];
	m_listeners.RemoveAll();
}

/*!
	設定からポート追加
*/
void CListener::AddPortsFromSetting(CString strPorts)
{
	//	ポート列取得
	CStringArray	ports;
	CTextTools::GetAllToken(strPorts, ports, ",", TRUE);

	//	登録
	for(int i=0;i<ports.GetSize();i++)
	{
		int port = atoi(ports[i]);

		if(port > 0 && port < 65536)
			AddPort(port);
	}
}

/*!
	ポートの追加
*/
void CListener::AddPort(int port)
{
	//	禁止
	if(IsRunning())
		return;

	//	多い？
	if(m_listeners.GetSize() >= MAXIMUM_WAIT_OBJECTS)
		throw CServerStartException(_T("通信ポート数の最大値を超えました。"));

	//	追加
	m_listeners.Add(new CListenSocket(port));
}


/////////////////////////////////////////////////////////////////////////////////////
//	スレッド関数
/////////////////////////////////////////////////////////////////////////////////////
/*!
	スレッド
*/
void CListener::ThreadMain()
{
	//	スレッドを優先に
	::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);

	//	エラーループ
	while(!GetBreak())
	{
		//	許可ループ
		AcceptLoop();

		//	ポートの待ち受けをやり直し
		while(!GetBreak() && OpenPorts())
			WaitForSomeObjects(m_errorRetryTime, GetBreakEvent());	//	エラーなので、しばらく待機
	}

	//	閉じる
	ClosePorts();
}

/*!
	受付ループ
*/
int CListener::AcceptLoop()
{
	//	待ち受けループ
	while(!GetBreak())
	{
		//	許可を試みる
		if(AcceptConnections())
			return(-1);		//	エラー

		//	イベントを待つ
		WaitNextRequest();
	}

	return(0);
}


/*!
	ポートを開く
*/
int CListener::OpenPorts()
{
	//	まずは、すべて閉じる
	ClosePorts();

	//	全てのポートを開く
	for(int i=0;i<m_listeners.GetSize();i++)
	{
		//	待ち受け開始
		if(m_listeners[i]->StartListen())
			return(-1);
	}

	return(0);
}

/*!
	ポートを閉じる
*/
void CListener::ClosePorts()
{
	//	全てのポートを閉じる
	for(int i=0;i<m_listeners.GetSize();i++)
	{
		//	待ち受け停止
		m_listeners[i]->EndListen();
	}
}


/*!
	接続を受け付け
*/
int CListener::AcceptConnections()
{
	//	テンポラリ
	SOCKET		newSock;
	CIPAddress	ip;

	while(1)
	{
		//	全てのポートをチェック
		int	acceptedCount = 0;
		for(int i=0;i<m_listeners.GetSize();i++)
		{
			//	受付を試みる
			switch(m_listeners[i]->TryAccept(newSock, ip))
			{
			//	ソケットなし
			case LISTEN_SOCKET_NOCLIENT:
				break;

			//	エラー
			case LISTEN_SOCKET_ERROR:
				return(-1);

			//	新規接続
			case LISTEN_SOCKET_ACCEPTED:
				acceptedCount++;

				OnNewConnection(newSock);
				break;
			}
		}

		//	当面無い
		if(acceptedCount == 0)
			return(0);
	}
}



/*!
	接続が来るのを待つ
*/
void CListener::WaitNextRequest()
{
	//	イベント数
	int	eventCount = m_listeners.GetSize();
	if(eventCount > MAXIMUM_WAIT_OBJECTS - 1)
		eventCount = MAXIMUM_WAIT_OBJECTS - 1;

	//	すべてのイベントを取得
	HANDLE	waits[MAXIMUM_WAIT_OBJECTS];
	for(int i=0;i<eventCount;i++)
		waits[i+1] = m_listeners[i]->GetEventHandle();

	//	待つ
	waits[0] = GetBreakEvent();
	::WaitForMultipleObjects(eventCount + 1, waits, FALSE, INFINITE);
}


/*!
	新規接続
*/
void CListener::OnNewConnection(SOCKET socket)
{
	CAcceptedSocket	addSocket(socket, m_socketType);
	if(m_queue->AddNewConnection(addSocket))
		addSocket.Abort();
}

