#include "StdAfx.h"
#include "server.h"
#include "04WebServer.h"
#include "connection.h"
#include "dateutility.h"
#include "intervaltimer.h"
#include "TimeOut.h"
#include "ServerStartException.h"

/*!
	サーバー実行開始
*/
CServer::CServer(void)
	: m_settingRoot("04WebServer")
{
	DEBUG_SET_THREAD_CLASS_NAME("CServer");

	try
	{
		//	設定情報をロード
		if(m_settingRoot.LoadFromXMLFile(GET_APP()->GetAppPath() + "04WebServer.xml"))
			throw CServerStartException("04WebServer.xmlのロードに失敗しました");

		//	設定環境領域を取得
		m_setting = m_settingRoot.GetAccess("Setting");

		//	サーバ情報環境領域を取得
		m_serverInfo = m_infoRoot.GetAccess("ServerInfo");

		//	サーバ情報の設定
		InitServerInfo();

		//	タイムアウト時間の設定
		CTimeOutMgr::GetGlobalManager()->SetDefaultTimeOutTime(m_setting.GetConfig("Basic::TimeOut",300)*1000);

		//	変数初期化
		m_statusHideTime = -1;
		m_overloadFlag = 0;

		//	最大同時接続数取得
		m_maxConnect = m_setting.GetKeyInt("Basic::MaxConnect", 100);

		//	高負荷設定取得
		m_overloadKeepTimer.SetInterval(m_setting.GetKeyInt("Enhance::OverloadKeepTime", 30) * 1000);
		m_overloadThreshold = m_setting.GetKeyInt("Enhance::OverloadThresholdActiveConnection", 90) * m_maxConnect / 100;
		if(m_overloadThreshold < 0)
			m_overloadThreshold = 0;
		if(m_overloadThreshold > m_maxConnect)
			m_overloadThreshold = m_maxConnect;

		//	コンテキスト初期化
		m_contextMgr.Start(m_setting);

		//	MimeTypesのロード
		m_mimeTypes.Load(m_setting);

		//	帯域制限有効
		m_bandWidthMgr.Load(m_setting);

		//	ダイジェスト認証マネージャ初期化
		m_authMgr.Start(m_setting);

		//	SSL初期化
		InitSSL();

		//	ログ初期化
		m_log.Init(this);

		//	待ち受け開始
		StartListen();

		//	スレッド開始
		if(StartThread())
			throw CServerStartException(_T("スレッドの開始に失敗しました"));
	}
	catch(CServerStartException e)
	{
		//	ログに記録
		m_log.WriteSystemLog(_T("サーバの起動に失敗しました: ") + e.m_info);

		//	もう一度投げる
		throw e;
	}	
	m_log.WriteSystemLog(_T("サーバを起動しました"));
}


CServer::~CServer(void)
{
	EndThread();
}


/*!
 	サーバ情報の設定
*/
void CServer::InitServerInfo(void)
{
	//	サーバーネーム
	m_serverInfo.SetConfig("ServerName", SERVER_NAME);

	//	バージョン
	m_serverInfo.SetConfig("ServerVersion", SERVER_VER);

	//	シグニチャ
	CString	sig = m_setting.GetConfig("Basic::ServerSignature", "%ServerName%/%ServerVersion%");
	sig = m_serverInfo.GetKeySwapString(sig);
	m_serverInfo.SetConfig("ServerSignature", sig);
	
	//	サーバールート
	m_serverInfo.SetConfig("ServerRoot", GET_APP()->GetAppPath());

	//	Settingを自動置き換えに
	m_settingRoot.AddAutoSwap(new CSettingSwap(m_serverInfo));
}

/*!
	SSL初期化
*/
void CServer::InitSSL()
{
	//	SSLは有効？
	if(m_setting.GetKeyInt(_T("Basic::EnableSSL"), 0))
		m_sslContext.Start(m_setting);
}

/*!
 	待ち受け開始
*/
int CServer::StartListen(void)
{
	//	リスナ起動
	m_listener.Start(m_setting);

	//	アドレス登録
	CIPAddress localhost = CIPAddress::GetLocalhost();
	m_serverInfo.SetConfig("ServerAddress", (CString)localhost);
	m_serverInfo.SetConfig("ServerHostName", localhost.GetHostName());

	//	ポート登録
	m_serverInfo.SetConfig("ServerPort", m_setting.GetConfig("Basic::Port", 80));

	return(0);
}

/*!
	スレッド本体
*/
void CServer::ThreadMain()
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CServer::ThreadMain");

	//	スレッドを優先に
	::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);

	CIntervalTimer	timer(1000);
	while(!GetBreak())
	{
		try
		{		
			//	接続数は最大に到達しているか？
			if(m_connections.GetSize() < m_maxConnect)
				WaitNextConnection(200);	//	新規接続を待つ
			else
				Sleep(50);					//	処理完了まで待つ

			//	切断した接続を破棄
			ClearCompletedConnections();

			//	オーバーロードの検出
			CheckServerLoad();

			//	情報の更新は１秒ごと
			if(timer.IsTimeout())
				UpdateServerInfo();
		}
		catch(CMemoryException *e)
		{
			e->Delete();

			//	メモリ不足のため、サーバ一時停止
			m_listener.EnableListen(FALSE);
			WaitForSomeObjects(30000, GetBreakEvent());
			m_listener.EnableListen(TRUE);

			m_log.WriteSystemLog("SystemError : メモリ不足のため、いくつかの接続を強制切断しました");
			SetServerStatus("メモリ不足のため、いくつかの接続を強制切断しました。");
		}

	}

	//	受け付け停止
	m_listener.ShutdownListen();

	//	全スレッド停止
	KillAllConnection();

	//	ログの更新
	m_log.WriteSystemLog("サーバを停止しました");
	m_log.UpdateLog();
}


/*!
	次の接続を待つ
*/
void CServer::WaitNextConnection(int timeout)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CServer::ThreadMain::WaitNextConnection");

	//	接続要求は？
	CAcceptedSocket	newSocket;
	if(GetNextAccess(newSocket, timeout))
	{
		CConnection	*connect = NULL;
		try
		{
			connect = new CConnection(this, newSocket);
			m_connections.Add(connect);
		}
		catch(CConnectionException e)
		{
			//	エラー？
			m_log.WriteSystemLog("SystemError : %s",e.GetErrorInfo());
			if(connect)
				delete connect;

			TRACE("SystemError : %s",e.GetErrorInfo());
		}
		catch(CMemoryException *e)
		{
			//	掃除
			if(connect)
				delete connect;
			newSocket.Abort();

			//	もう一度投げる
			throw e;
		}
	}
}

/*!
	次のアクセスを取得
*/
int CServer::GetNextAccess(CAcceptedSocket &nextAccess, int waitTime)
{
	//	接続まで待つ
	if(WaitForSomeObjects(waitTime, m_listener.GetIAcceptedSockeQueueAccess()->GetSemaphore()) == 1)
	{
		nextAccess = m_listener.GetIAcceptedSockeQueueAccess()->GetNewConnection();
		return(1);
	}
	return(0);
}


/*!
	切断した接続を破棄
*/
void CServer::ClearCompletedConnections()
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CServer::ThreadMain::ClearCompletedConnections");

	//	終わった接続の削除
	for(int i=(int)m_connections.GetSize()-1;i>=0;i--)
	{
		if(((CConnection *)m_connections[i])->IsRunning() == 0)
		{
			delete (CConnection *)m_connections[i];
			m_connections.RemoveAt(i);
		}
	}
}

/*!
	サーバ負荷調整
*/
void CServer::CheckServerLoad()
{
	//	オーバーロードの検出
	if(m_connections.GetSize() >= m_overloadThreshold || m_listener.QueueIsOverload())
	{
		if(!m_overloadFlag.GetCounter())
			SetServerStatus("サーバは高負荷状態です。");
		m_overloadFlag = 1;

		//	一定時間保持
		m_overloadKeepTimer.Reset();
	}
	else
	{
		//	保持時間経過
		if(m_overloadKeepTimer.IsTimeout())
		{
			if(m_overloadFlag.GetCounter())
				SetServerStatus("サーバは一時的に高負荷状態になりました。");
			m_overloadFlag = 0;
		}
	}
}

/*!
	サーバ情報を更新
*/
void CServer::UpdateServerInfo()
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CServer::ThreadMain::UpdateInfo");

	//	現在の接続数
	m_serverInfo.SetKeyInt("CurrentConnection", m_connections.GetSize());

	//	現在までの接続数
	m_serverInfo.SetKeyInt("AllConnected", m_bandWidthMgr.GetAllCount());

	//	現在までの転送量
	m_serverInfo.SetKeyInt64("AllRecvSize", m_bandWidthMgr.GetAllRecv());
	m_serverInfo.SetKeyInt64("AllSendSize", m_bandWidthMgr.GetAllSend());

	//	過去１時間の接続数
	m_serverInfo.SetKeyInt("Connected1Hour", m_bandWidthMgr.GetCount1h());

	//	過去１時間の転送量
	m_serverInfo.SetKeyInt64("RecvSize1Hour", m_bandWidthMgr.GetRecv1h());
	m_serverInfo.SetKeyInt64("SendSize1Hour", m_bandWidthMgr.GetSend1h());

	//	ステータス更新から１日たったか？
	if(m_statusHideTime != -1 && m_statusHideTime < CTime::GetCurrentTime())
	{
		m_statusHideTime = -1;
		GET_APP()->SetServerStatus("サーバは正常に作動中です。");
	}

	//	ログの更新
	m_log.UpdateLog();
}

/*!
	オーバーロード状態か取得
*/
int CServer::ServerIsOverload()
{
	return(m_overloadFlag.GetCounter());
}



/*!
	全ての接続の停止
*/
void CServer::KillAllConnection()
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CServer::KillAllConnection");

	//	サーバー停止フラグ
	m_serverInfo.SetConfig("StopServer",1);

	for(int i=(int)m_connections.GetSize()-1;i>=0;i--)
	{
		((CConnection *)m_connections[i])->Break();
	}

	for(int i=(int)m_connections.GetSize()-1;i>=0;i--)
	{
		delete (CConnection *)m_connections[i];
		m_connections.RemoveAt(i);
	}
}


/*!
	サーバステータスの設定
*/
void CServer::SetServerStatus(CString status, CTimeSpan viewTime)
{
	GET_APP()->SetServerStatus(status);
	m_statusHideTime = CTime::GetCurrentTime() + viewTime;
}

/*!
	接続数の取得
*/
int CServer::GetConnectCount()
{
	return(m_serverInfo.GetConfig("CurrentConnection",0));
}



/*!
	Settingの置き換えコールバック
*/
CString CSettingSwap::SwapString(CString key,CString data)
{
	//	DATE?
	if(	data.Find("Date") != -1 || 
		data.Find("Time") != -1 || 
		data.Find("GMT") != -1 || 
		data.Find("Year") != -1 || 
		data.Find("Month") != -1 || 
		data.Find("Day") != -1 || 
		data.Find("DayOfWeek") != -1 || 
		data.Find("MonthString") != -1 || 
		data.Find("DayOfWeekString") != -1 || 
		data.Find("Hour") != -1 || 
		data.Find("Minute") != -1 || 
		data.Find("Second") != -1)
	{
		CWorkspace	m_date;
		CWorkspaceAccess date = m_date.GetAccess("Date");
		date.Clear();

		//	時刻
		CTime	time=CTime::GetCurrentTime();
		date.SetConfig("Date",time.Format("%Y/%m/%d"));
		date.SetConfig("Time",time.Format("%H:%M:%S"));
		date.SetConfig("GMT",CDateUtility::GetGMTString(time));

		date.SetConfig("Year",time.Format("%Y"));
		date.SetConfig("Month",time.Format("%m"));
		date.SetConfig("Day",time.Format("%d"));
		date.SetConfig("DayOfWeek",time.Format("%w"));

		date.SetConfig("MonthString",time.Format("%b"));
		date.SetConfig("DayOfWeekString",time.Format("%a"));

		date.SetConfig("Hour",time.Format("%H"));
		date.SetConfig("Minute",time.Format("%M"));
		date.SetConfig("Second",time.Format("%S"));
		data = date.GetKeySwapString(data,"");
	}

	//	Infoによる置き換え（m_serverInfoのコールバックにm_setting使用厳禁！　デッドロックの可能性あり）
	data = m_serverInfo.GetKeySwapString(data,"");

	return(data);
}



