#include "StdAfx.h"
#include "ListenerBase.h"
#include "ServerStartErrorException.h"
#include "TextTools.h"


CListenerBase::CListenerBase(void)
{
}

CListenerBase::~CListenerBase(void)
{
	StopListenerBase();
}



/////////////////////////////////////////////////////////////////////////////////////
//	操作
/////////////////////////////////////////////////////////////////////////////////////
/*!
	初期化
*/
void CListenerBase::InitListenerBase(IServerToolIIPtr serverTool, int errorRetryTime)
{
	m_serverTool = serverTool;
	m_errorRetryTime = errorRetryTime;
	m_disableNewConnectionEndTime = 0;
}

/*!
	開始
*/
void CListenerBase::StartListenerBase()
{
	if(OpenPorts())
		throw CServerStartErrorException(GetListenerClassName(), "接続の待ち受け開始に失敗しました");

	if(StartThread())
		throw CServerStartErrorException(GetListenerClassName(), "スレッドの起動に失敗しました");
}

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

	//	掃除
	CloseAllPorts();
}

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

/*!
	設定からポート追加
*/
void CListenerBase::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 CListenerBase::AddPort(int port)
{
	//	禁止
	if(IsRunning())
		return;

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

	//	ログへ
	m_serverTool->WriteSystemLog(GetListenerClassName(), SERVER_STATUS_NORMAL_LOG, CTextTools::GetFormat("ポート %d を追加しました", port));

}


/*!
	一時的に受け付け停止
*/
void CListenerBase::ForceDisableNewConnection(int time)
{
	m_disableNewConnectionEndTime = GetTickCount() + time;
}

/////////////////////////////////////////////////////////////////////////////////////
//	スレッド関数
/////////////////////////////////////////////////////////////////////////////////////
/*!
	スレッド
*/
void CListenerBase::ThreadMain()
{
	//	エラーループ
	while(!GetBreak())
	{
		//	許可ループ
		AcceptLoop();

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

	//	閉じる
	ClosePorts();

	//	最終ステータス
	m_serverTool->WriteSystemLog(GetListenerClassName(), SERVER_STATUS_NORMAL, "ポート待ち受けを停止しました");
}

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

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

	return(0);
}


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

	//	全てのポートを開く
	for(int i=0;i<m_listeners.GetSize();i++)
	{
		//	待ち受け開始
		if(m_listeners[i]->StartListen())
		{
			//	エラー報告
			m_serverTool->WriteSystemLog(GetListenerClassName(), SERVER_STATUS_ERROR,
				CTextTools::GetFormat("ポート %d の待ち受け開始に失敗しました", m_listeners[i]->GetPort()));
			return(-1);
		}
	}

	//	成功報告
	m_serverTool->WriteSystemLog(GetListenerClassName(), SERVER_STATUS_NORMAL, "全ポートの待ち受けを開始しました");
	return(0);
}

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


/*!
	接続を受け付け
*/
int CListenerBase::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:
				m_serverTool->WriteSystemLog(GetListenerClassName(), SERVER_STATUS_ERROR,
					CTextTools::GetFormat("ポート %s の接続受付処理でエラーが発生しました", m_listeners[i]->GetPort()));
				return(-1);

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

				//	無効？
				if(InDisableMode())
					closesocket(newSock);		//	削除
				else
					OnNewConnection(newSock);

				TRACE("New connection\n");
				break;
			}
		}

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



/*!
	接続が来るのを待つ
*/
void CListenerBase::WaitNextConnection()
{
	//	イベント数
	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);
}


/*!
	停止中？
*/
int CListenerBase::InDisableMode()
{
	if(m_disableNewConnectionEndTime != 0)
	{
		DWORD	tick = GetTickCount();
		if(m_disableNewConnectionEndTime < tick || m_disableNewConnectionEndTime >= tick)
		{
			m_disableNewConnectionEndTime = 0;
			return(0);
		}
		return(1);
	}
	return(0);
}
