#include "StdAfx.h"
#include "IPv4ListenerBase.h"
#include "ServerException.h"
#include "TextTools.h"


CIPv4ListenerBase::CIPv4ListenerBase(void)
{
}

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



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

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

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

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

	//	掃除
	CloseAllPorts();
}

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

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

	//	登録
	for(int i=0;i<ports.GetCount();i++)
	{
		//	ポート番号取得
		int port = CTextTools::StrTol(ports[i]);
		if(port > 0 && port < 65536)
			AddPort(port);
	}
}

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

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

	//	ログへ
	m_serverTool.WriteSystemLog(GetListenerClassName(), SYSTEM_LOG_INFO, CUTF8String::GetFormat(_U16("ポート %d を追加しました"), port));

}


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

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

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

	//	閉じる
	ClosePorts();

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

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

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

	return(0);
}


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

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

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

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


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

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

			//	エラー
			case LISTEN_SOCKET_ERROR:
				m_serverTool.WriteSystemLog(GetListenerClassName(), SYSTEM_LOG_ERROR,
					CUTF8String::GetFormat(_U16("ポート %s の接続受付処理でエラーが発生しました"), m_listeners[i]->GetPort()));
				return(-1);

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

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

				TRACE_U8(_U16("New connection\n"));
				break;
			}
		}

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



/*!
	接続が来るのを待つ
*/
void CIPv4ListenerBase::WaitNextRequest()
{
	//	イベント数
	int	eventCount = m_listeners.GetCount();
	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 CIPv4ListenerBase::InDisableMode()
{
	if(m_disableNewConnectionEndTime != 0)
	{
		DWORD	tick = GetTickCount();
		if(m_disableNewConnectionEndTime < tick || m_disableNewConnectionEndTime >= tick)
		{
			m_disableNewConnectionEndTime = 0;
			return(0);
		}
		return(1);
	}
	return(0);
}
