#pragma once
#include "Workspace.h"
#include "threadskeleton.h"
#include "response.h"
#include "request.h"
#include "alias.h"
#include "bandwidthmgr.h"
#include "ProtocolSocket.h"
#include "AcceptedSocketQueue.h"
#include "TextInfoExceptionBase.h"
#include "InterruptEvents.h"

/*!
	接続の処理状態
*/
enum CONNECTION_PROCESS_STATUS
{
	CONNECTION_PROCESS_NOCHANGE				= -1,

	CONNECTION_PROCESS_STARTING				= 0,
	CONNECTION_PROCESS_RECVING_HEADER		= 1000,
	CONNECTION_PROCESS_RESTART				= 2000,
	CONNECTION_PROCESS_PRECREATING_RESPONSE	= 3000,
	CONNECTION_PROCESS_RECVING_BODY			= 4000,
	CONNECTION_PROCESS_CREATING_RESPONSE	= 5000,
	CONNECTION_PROCESS_SENDING_HEADER		= 6000,
	CONNECTION_PROCESS_SENDING_BODY			= 7000,
	CONNECTION_PROCESS_COMPLETE				= 8000,
};


/*
	クラスをあらかじめ宣言
*/
class CServer;
class CConnection;
class CConnectionInterrupt;


/*!
	生成エラー例外
*/
DEFINE_TEXT_INFO_EXCEPTION(CConnectionException);

/*!
	内部エラー例外
*/
DEFINE_TEXT_INFO_EXCEPTION(CServerFatalException);


/*!
	レスポンス処理の中断を確認する
*/
class CConnectionInterrupt : public CInterruptEvents
{
public:
	CConnectionInterrupt(CConnection *connection);
	CConnectionInterrupt(CConnectionInterrupt &other);
	
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//	過去の互換
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//!	中断を確認
	operator int();

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//	CInterruptEvents
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//!	中断を確認
	virtual int IsInterrupt();

	//!	中断原因を取得
	virtual CString GetInterruptInfo();

	//!	中断を待機
	virtual int WaitInterrupt(int timeout);

	//!	中断処理イベントをすべて取得
	virtual void GetAllInterruptEvents(CArray<HANDLE> &events);

private:
	//!	親の接続
	CConnection		*m_connection;

	//!	切断原因
	CString			m_interruptInfo;
};


/*!
	接続を処理するクラス
*/
class CConnection : public CThreadSkeleton
{
	friend class CConnectionInterrupt;

public:
	//!	接続を生成
	CConnection(CServer *server, CAcceptedSocket newSocket);

	//!	接続を破棄
	virtual ~CConnection(void);

	//!	処理を中断
	virtual void Break();

protected:
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//	接続処理
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//!	メインループ
	void ThreadMain();

	//!	対応するソケットラッパ生成
	CProtocolSocket *CreateProtocolSocket(SOCKET socket, int socketType);

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//	１リクエストを処理
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//!	1リクエストを処理
	int OneRequest(CWorkspaceAccess context);

	//!	リスタート(リスタート不可の場合は、例外を投げる)
	void RestartConnectionProcess(CWorkspaceAccess &contex);

	//!	新しいコンテキストを生成
	CWorkspaceAccess NewContext();

	//!	コンテキストを破棄	
	void DeleteContext(CWorkspaceAccess context);

	//	ログへ出力
	void WriteLog(CWorkspaceAccess &context);


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//	レスポンス作成
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//!	高負荷かチェック
	void CheckServerOverload(CWorkspaceAccess &context);

	//!	アクセスチェック
	void CheckAccess(CWorkspaceAccess &context,CRequest &request,CAlias &alias,CBandWidthReference &bandRef);

	//!	認証チェック
	void CheckAuthorize(CWorkspaceAccess &context,CRequest &request,CAlias &alias);

	//!	ホストチェック
	int CheckKickHost(CWorkspaceAccess &context,CRequest &request,CAlias &alias);

	//!	移動の確認
	void CheckMove(CWorkspaceAccess &context,CRequest &request,CAlias &alias);

	//!	WebDAV処理
	void WebDAV(CWorkspaceAccess &context,CRequest &request,CAlias &alias);

	//!	Index確認
	void CheckIndex(CWorkspaceAccess &context,CRequest &request,CAlias &alias);

	//!	CGI
	void CGI(CWorkspaceAccess &context,CRequest &request,CAlias &alias);

	//!	SSI
	void SSI(CWorkspaceAccess &context,CRequest &request,CAlias &alias);

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//	通信処理
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//!	リクエストヘッダを受信
	CString RecvHeader();

	//!	レスポンス処理（リクエストボディ受信、レスポンス生成、レスポンスヘッダ送信、レスポンスボディ送信）
	void SendResponse(CWorkspaceAccess &context, CRequest &request, CAlias &alias, CBandWidthReference &bandRef, CResponse *response);

	//!	リクエストボディ受信
	void RecvData(CWorkspaceAccess &context, CRequest &request, CAlias &alias,CBandWidthReference &bandRef, CResponse *response);

	//!	レスポンスボディ送信
	void SendData(CWorkspaceAccess &context, CRequest &request, CAlias &alias, CBandWidthReference &bandRef, CResponse *response, __int64 length);

	//!	致命的エラーを送信
	void SendFatalError(CString info);

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//	ツール関数
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//!	レスポンスヘッダを生成
	CString MakeHeaderString(CWorkspaceAccess responseInfo);

	//!	ユーザが有効かチェック
	int CheckUser(CString userName, CString passwd, CAlias alias);

	//!	帯域制御用ウェイト
	void Delay(int tick);
	
	//!	レスポンスを送信可能な状態かチェック
	int CanSendResponse(CWorkspaceAccess &context);

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//	ステータス
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//!	送受信サイズを更新
	void UpdateTransSize(CWorkspaceAccess &context);

	//!	処理状況変更
	void UpdateConnectionStatus(CWorkspaceAccess &context, CString statusString, CONNECTION_PROCESS_STATUS statusCode = CONNECTION_PROCESS_NOCHANGE, int isRestart = FALSE); 

	//!	処理状況を取得
	CONNECTION_PROCESS_STATUS GetConnectionProcessStatus(CWorkspaceAccess &context);

protected:
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//	レスポンス作成
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//!	タイムアウト
	int						m_serverTimeout;

	//!	KeepAlive時間
	int						m_maxKeepAliveTime;

	//!	KeepAlive許可
	int						m_keepAlive;

	//!	日付処理用環境領域
	CWorkspace				m_date;

	//!	送信済みデータサイズ
	__int64					m_sendSize;

	//!	受信済みデータサイズ
	__int64					m_recvSize;

	//!	受信済みリクエストボディサイズ
	__int64					m_recvDataSize;;

	//!	サーバ
	CServer					*m_server;

	//!	中断管理クラス
	CConnectionInterrupt	m_interrupt;

	//!	生ソケット
	SOCKET					m_rawSocket;

	//!	ソケットタイプ
	int						m_rawSocketType;

	//!	ソケットラッパ
	CProtocolSocket			*m_protocolSocket;
};

