//------------------------------------------------------------------------------
// 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
 * FPSコントローラ実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Core/Utility/FPSController.h"
#include "Core/Thread/Thread.h"
#include "Core/Thread/SynchronizedBlock.h"

namespace Lamp{

/// 60FPS
const float FPSController::interval60FPS = 16.6666666f;
/// 30FPS
const float FPSController::interval30FPS = 33.3333333f;

//------------------------------------------------------------------------------
// コンストラクタ
FPSController::FPSController(float targetInterval){
	setTargetInterval(targetInterval);
	tick_ = Timer::getTick();
}
//------------------------------------------------------------------------------
// デストラクタ
FPSController::~FPSController(){
	Assert(backgroundThreads_.getCount() == 0);
}
//------------------------------------------------------------------------------
// FPSを維持するためにカレントスレッドをsleepする
float FPSController::sleep(){
	// sleep時間の算出
	intervalTime_ = Timer::getInterval(tick_);
	tick_ = Timer::getTick();
	sleepTime_ += (targetInterval_ - intervalTime_);
	if(sleepTime_ < 0.f){ sleepTime_ = 0.f; }
	else if(sleepTime_ > targetInterval_){ sleepTime_ = targetInterval_; }
	// インターバルスレッドの再開
	bool hasBackgroundTask = false;
	{
		SynchronizedBlock synchronizedBlock(criticalSection_);
		if(backgroundThreads_.getCount() > 0){
			hasBackgroundTask = true;
			backgroundThreads_[0]->resume();
		}
	}
	// バックグラウンドタスクがある場合は最低1msはsleepする
	u_int correctedSleepTime = (u_int)sleepTime_;
	if(hasBackgroundTask && (correctedSleepTime == 0)){
		correctedSleepTime = 1;
	}
	Thread::sleep(correctedSleepTime);
	// インターバルスレッドの停止
	{
		SynchronizedBlock synchronizedBlock(criticalSection_);
		if(hasBackgroundTask){
			Thread* backgroundThread = backgroundThreads_[0];
			if(backgroundThread->isFinished()){
				backgroundThreads_.remove(0);
			}else{
				backgroundThread->suspend();
			}
		}
	}
	return intervalTime_;
}
//------------------------------------------------------------------------------
// バックグラウンドスレッドの登録
void FPSController::registerBackgroundThread(Thread* backgroundThread){
	SynchronizedBlock synchronizedBlock(criticalSection_);
	Assert(!backgroundThread->isFinished());
	backgroundThreads_.pushBack(backgroundThread);
}
//------------------------------------------------------------------------------
// バックグラウンドスレッド数の取得
int FPSController::getBackgroundThreadCount(){
	SynchronizedBlock synchronizedBlock(criticalSection_);
	return backgroundThreads_.getCount();
}
//------------------------------------------------------------------------------
// バックグラウンドスレッド数の取得
Thread* FPSController::getBackgroundThread(int index){
	SynchronizedBlock synchronizedBlock(criticalSection_);
	return backgroundThreads_[index];
}
//------------------------------------------------------------------------------
// 文字列への変換
String FPSController::toString() const{
	String result;
	result.format("interval %5.2f  sleep %5.2f  processing %5.2f%%",
		getIntervalTime(), getSleepTime(),
		getProcessingTime() / getIntervalTime() * 100.f);
	return result;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
