#include"CThreadController.h"
#include"../Debug/CAssert.h"
#include"../Debug/CWarning.h"
#include"../Debug/CException.h"
#include"../Debug/CTrace.h"
#include"../../Setup/Win32.h"

namespace Maid
{

	/*!
	 	@class	CThreadController CThreadController.h
	 	@brief	スレッド作成クラス
	\n			スレッドで実行している関数側は以下のルールを守ること
	\n			BRIGEDATA::IsExit が true の時は即座に return すること
	\n
	\n			複数のスレッドで Cランタイムを使用する場合の注意
	\n
	\n				http://msdn2.microsoft.com/ja-jp/library/7t9ha0zh(VS.80).aspx
	\n
	\n				Libcmt.lib を使用してビルドしたプログラムから C ランタイム ルーチンを呼び出す場合は、
	\n				_beginthread 関数または _beginthreadex 関数でスレッドを起動する必要があります。
	\n				Win32 の ExitThread 関数および CreateThread 関数は使用しないでください。
	\n				また、C ランタイム ライブラリのデータ構造体へアクセス中のスレッドがあって、
	\n				その完了を待っている複数のスレッドが存在する場合に SuspendThread を使うと、
	\n				デッドロック状態になります。
	\n
	\n
	\n			分かりやすくいうと　２つのスレッドがそれぞれ printf() してるときに
	\n			片方のスレッドを SuspendThread() すると、デッドロックすることがあります。
	\n
	\n			うかつにとめないほうがいいみたいです。

	 */


void CThreadController::Sleep( unt time )
{
	::Sleep( time );
}



/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 */
CThreadController::CThreadController()
{
	m_hThread = NULL;
	m_BrigeData.IsExit    = false;
	m_BrigeData.IsExecute = false;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! デストラクタ
/*!
 */
CThreadController::~CThreadController()
{
	Close();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スレッドで行う関数の設定
/*!
 *	\param	Function	[i ]	設定する関数
 */
void CThreadController::SetFunc( const THREADFUNCTION& Function )
{
	m_Func = Function;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スレッドの起動
/*!
 */
void CThreadController::Execute( PRIORITY priority )
{
	Close();
	unt32 no;
	m_BrigeData.IsExecute = true;

	if( priority!=PRIORITY_NORMAL )
	{
		m_hThread = ::_beginthreadex( NULL, 0, Thread, this, CREATE_SUSPENDED, &no );
		SetPriority( priority );
		Resume();
	}else
	{
		m_hThread = ::_beginthreadex( NULL, 0, Thread, this, 0, &no );
	}
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スレッドの終了
/*!
 */
void CThreadController::Close()
{
	if( m_hThread!=NULL )
	{
		m_BrigeData.IsExit = true;
		while( m_BrigeData.IsExecute ) { ::WaitForSingleObject((HANDLE)m_hThread,100); }
		::CloseHandle( (HANDLE)m_hThread );
		m_hThread = NULL;
	}

	m_BrigeData.IsExit    = false;
	m_BrigeData.IsExecute = false;
	m_BrigeData.IsAssert  = false;

	const mstring m = m_ExceptionText;
	m_ExceptionText.clear();

	if( !m.empty() )
	{
		throw CException(m);
	}
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 優先度の変更
/*!
 *	\param	priority	[i ]	新しい優先度
 */
void CThreadController::SetPriority( PRIORITY priority )
{
	int p;
	switch( priority )
	{
	case PRIORITY_HIGH2:	{ p = THREAD_PRIORITY_HIGHEST; }break;
	case PRIORITY_HIGH1:	{ p = THREAD_PRIORITY_ABOVE_NORMAL; }break;
	case PRIORITY_NORMAL:	{ p = THREAD_PRIORITY_NORMAL; }break;
	case PRIORITY_LOW1:		{ p = THREAD_PRIORITY_BELOW_NORMAL; }break;
	case PRIORITY_LOW2:		{ p = THREAD_PRIORITY_LOWEST; }break;
	}
	::SetThreadPriority( (HANDLE)m_hThread, p );
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スレッドを一時止める
/*!
 */
void CThreadController::Pause()
{
	::SuspendThread( (HANDLE)m_hThread );
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 停止していたスレッドを起動する
/*!
 */
void CThreadController::Resume()
{
	::ResumeThread( (HANDLE)m_hThread );
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スレッドの処理が終わったか？
/*!
 */
bool CThreadController::IsThreadExited() const
{
	return !m_BrigeData.IsExecute;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スレッドの処理が強制終了
/*!
 */
bool CThreadController::IsThreadAssert() const
{
	return m_BrigeData.IsAssert;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スレッドの戻り値の取得
/*!
	IsThreadExited()==true のときだけ呼び出すこと
 */
unt CThreadController::GetReturnCode() const
{
	MAID_ASSERT( !IsThreadExited(), "スレッドは実行中です" );
	if( !IsThreadExited() ) { return 0; }

	DWORD ret;
	::GetExitCodeThread( (HANDLE)m_hThread, &ret );
	return ret;
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スレッドをメンバ関数で起動させるための踏み台関数
/*!
 	@param	pVoid	[i ]	常に _beginthreadex を呼び出した CThread* であること
 */
unsigned __stdcall CThreadController::Thread( void* pVoid )
{
	unt RetCode = 0;
	CThreadController* pThis = (CThreadController*)pVoid;

	try
	{
		RetCode = pThis->m_Func( pThis->m_BrigeData );
	}
	catch( CException& e )
	{
		//	別スレッドの例外はメインスレッドでキャッチできないので
		//	ログだけだして終了
		e.WriteLog();

		pThis->m_ExceptionText = e.Text;
		pThis->m_BrigeData.IsAssert = true;
	}

	pThis->m_BrigeData.IsExecute = false;
	_endthreadex(RetCode);

	return RetCode;
}


}
