//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * スレッド実装
 * @author Junpee
 */

#include "LampBasic.h"
#include <process.h>
#include "Core/Thread/Thread.h"
#include "Core/Thread/SynchronizedBlock.h"

// timeBeginPeriod, timeEndPeriod
#include <windows.h>
#pragma comment(lib, "winmm.lib")

namespace Lamp{

/**
 * スレッドコールバック
 * @param userData ユーザデータ
 * @return 終了コード
 */
u_int __stdcall threadCallback(void* userData){
	Thread* thread = (Thread*)userData;
	return thread->executeWrapper();
}

// 不正なスレッドハンドル
const HANDLE Thread::invalidThreadHandle = (HANDLE)-1L;

//------------------------------------------------------------------------------
// 初期化、終了処理
//------------------------------------------------------------------------------
// 初期化
void Thread::initialize(){
	// Windowsタイマの精度を上げる
	::timeBeginPeriod(1);
}
//------------------------------------------------------------------------------
// 後始末
void Thread::finalize(){
	// Windowsタイマの精度を下げる
	::timeEndPeriod(1);
}
//------------------------------------------------------------------------------
// コンストラクタ
Thread::Thread() : Runnable(),
	threadHandle_(invalidThreadHandle), threadID_(0), isFinished_(false){
	runnable_ = this;
	createThread();
}
//------------------------------------------------------------------------------
// コンストラクタ
Thread::Thread(Runnable* runnable) : Runnable(),
	threadHandle_(invalidThreadHandle), threadID_(0), isFinished_(false){
	runnable_ = runnable;
	createThread();
}
//------------------------------------------------------------------------------
// コンストラクタ
Thread::Thread(LockObject* lockObject, bool deleteLockObject) :
	Runnable(lockObject, deleteLockObject),
	threadHandle_(invalidThreadHandle), threadID_(0), isFinished_(false){
	runnable_ = this;
	createThread();
}
//------------------------------------------------------------------------------
// コンストラクタ
Thread::Thread(
	Runnable* runnable, LockObject* lockObject, bool deleteLockObject) :
	Runnable(lockObject, deleteLockObject),
	threadHandle_(invalidThreadHandle), threadID_(0), isFinished_(false){
	runnable_ = runnable;
	createThread();
}
//------------------------------------------------------------------------------
// デストラクタ
Thread::~Thread(){
	Assert(isFinished_);
}
//------------------------------------------------------------------------------
// スレッドの作成
void Thread::createThread(){
	// 同期が取られている場所からしか呼ばれないので同期を取らない
	Assert(threadHandle_ == invalidThreadHandle);
	Assert(threadID_ == 0);
	threadHandle_ = (HANDLE)::_beginthreadex(NULL, getStackSize(),
		threadCallback, this, CREATE_SUSPENDED, &threadID_);
	if(threadHandle_ == invalidThreadHandle){
		ErrorOut("Thread::createThread() _beginthreadex()に失敗しました");
	}
	isFinished_ = false;
}
//------------------------------------------------------------------------------
// スレッドの操作
//------------------------------------------------------------------------------
// スレッドを開始する
void Thread::start(){
	// スレッドに対するロック
	SynchronizedBlock synchronizedBlock(this);
	// 実行終了後に再度startが呼ばれた場合はスレッドを再作成する
	if(isFinished_){ createThread(); }
	Assert(getThreadHandle() != invalidThreadHandle);
	Assert(getThreadID() != 0);
	if(::ResumeThread(getThreadHandle()) == 0xFFFFFFFF){
		ErrorOut("Thread::start() ResumeThread()に失敗しました");
	}
}
//------------------------------------------------------------------------------
// スレッドをセットアップする
void Thread::setup(){
	// スレッドに対するロック
	SynchronizedBlock synchronizedBlock(this);
	// 実行終了後に再度startが呼ばれた場合はスレッドを再作成する
	if(isFinished_){ createThread(); }
	Assert(getThreadHandle() != invalidThreadHandle);
	Assert(getThreadID() != 0);
}
//------------------------------------------------------------------------------
// スレッドの終了を依頼する
void Thread::requestStop(){
	// スレッドに対するロック
	SynchronizedBlock synchronizedBlock(this);
	if(isFinished()){ return; }
	Assert(getThreadHandle() != invalidThreadHandle);
	Assert(getThreadID() != 0);
	runnable_->setStopRequested(true);
}
//------------------------------------------------------------------------------
// スレッドの終了を依頼し、終了を待つ
bool Thread::stop(u_int milliseconds){
	if(isFinished()){ return true; }
	requestStop();
	return join(milliseconds);
}
//------------------------------------------------------------------------------
// スレッドの終了を待つ
bool Thread::join(u_int milliseconds){
	// ロックをかけるとデッドロックになる
	if(isFinished()){ return true; }
	Assert(getThreadHandle() != invalidThreadHandle);
	Assert(getThreadID() != 0);
	u_int result = ::WaitForSingleObject(getThreadHandle(), milliseconds);
	if(result == WAIT_OBJECT_0){
		return true;
	}else if(result == WAIT_TIMEOUT){
		return false;
	}else if(result == WAIT_ABANDONED){
		ErrorOut("Thread::join() Mutexを保持したままThreadが終了しました");
	}else if(result == WAIT_FAILED){
		Assert(false);
		return false;
//		DebugOut("%d\n", GetLastError());
//		ErrorOut("Thread::join() WaitForSingleObject()に失敗しました");
	}
	return true;
}
//------------------------------------------------------------------------------
// プライオリティの設定
void Thread::setPriority(Priority priority){
	Assert(getThreadHandle() != invalidThreadHandle);
	Assert(getThreadID() != 0);
	// スレッドに対するロック
	SynchronizedBlock synchronizedBlock(this);
	if(SetThreadPriority(getThreadHandle(), priority) == 0){
		ErrorOut("Thread::setPriority() SetThreadPriority()に失敗しました");
	}
}
//------------------------------------------------------------------------------
// プライオリティの取得
Thread::Priority Thread::getPriority(){
	Assert(getThreadHandle() != invalidThreadHandle);
	Assert(getThreadID() != 0);
	// スレッドに対するロック
	SynchronizedBlock synchronizedBlock(this);
	int result = GetThreadPriority(getThreadHandle());
	if(result == THREAD_PRIORITY_ERROR_RETURN){
		ErrorOut("Thread::getPriority() GetThreadPriority()に失敗しました");
	}
	return (Priority)result;
}
//------------------------------------------------------------------------------
// サスペンド
void Thread::suspend(){
	// スレッドに対するロック
	SynchronizedBlock synchronizedBlock(this);
	if(isFinished()){ return; }
	Assert(::GetCurrentThreadId() != getThreadID());
	if(::SuspendThread(getThreadHandle()) == 0xFFFFFFFF){
		ErrorOut("Thread::suspend() SuspendThread()に失敗しました");
	}
}
//------------------------------------------------------------------------------
// レジューム
void Thread::resume(){
	// スレッドに対するロック
	SynchronizedBlock synchronizedBlock(this);
	if(isFinished()){ return; }
	if(::ResumeThread(getThreadHandle()) == 0xFFFFFFFF){
		ErrorOut("Thread::resume() ResumeThread()に失敗しました");
	}
}
//------------------------------------------------------------------------------
// 使用不可
//------------------------------------------------------------------------------
// スレッドの実行ラッパ
u_int Thread::executeWrapper(){
	// 開始処理
	{
		// スレッドに対するロック
		SynchronizedBlock synchronizedBlock(this);
		runnable_->setStopRequested(false);
	}
	// 実行
	runnable_->run(this);
	// 終了処理
	{
		// スレッドに対するロック
		SynchronizedBlock synchronizedBlock(this);
		isFinished_ = true;
		::CloseHandle(getThreadHandle());
		threadHandle_ = invalidThreadHandle;
		threadID_ = 0; 
	}
	return 0;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
