//------------------------------------------------------------------------------
// 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 "Input/System/BufferedInput.h"
#include "Core/Thread/SynchronizedBlock.h"
#include "Input/System/LampInput.h"
#include "Input/Keyboard/Keyboard.h"
#include "Input/Keyboard/KeyboardDevice.h"
#include "Input/Mouse/Mouse.h"
#include "Input/Mouse/MouseDevice.h"
#include "Input/Joystick/Joystick.h"
#include "Input/Joystick/JoystickDevice.h"
#include "Core/InputOutput/BinaryWriter.h"
#include "Core/InputOutput/BinaryReader.h"

namespace Lamp{

// 60FPS
const float BufferedInput::interval60FPS = FPSController::interval60FPS;
// 30FPS
const float BufferedInput::interval30FPS = FPSController::interval30FPS;

//------------------------------------------------------------------------------
// コンストラクタ
BufferedInput::BufferedInput() : logWriter_(NULL), logReader_(NULL),
	keyboard_(NULL), keyboardDevice_(NULL), mouse_(NULL), mouseDevice_(NULL){
	// プライオリティをあげる
	setPriority(priorityTimeCritical);
}
//------------------------------------------------------------------------------
// デストラクタ
BufferedInput::~BufferedInput(){
}
//------------------------------------------------------------------------------
// 目標時間間隔の設定
void BufferedInput::setTargetInterval(float targetInterval){
	SynchronizedBlock synchronizedBlock(this);
	fpsController_.setTargetInterval(targetInterval);
}
//------------------------------------------------------------------------------
// 目標時間間隔の取得
float BufferedInput::getTargetInterval(){
	SynchronizedBlock synchronizedBlock(this);
	return fpsController_.getTargetInterval();
}
//------------------------------------------------------------------------------
// 入力があるか
bool BufferedInput::hasMoreInput(){
	Assert((keyboard_ != NULL) && (keyboardDevice_ != NULL));
	Assert((mouse_ != NULL) && (mouseDevice_ != NULL));
	SynchronizedBlock synchronizedBlock(this);
	return (keyboardStates_.getCount() != 0);
}
//------------------------------------------------------------------------------
// 次の入力
void BufferedInput::nextInput(){
	Assert((keyboard_ != NULL) && (keyboardDevice_ != NULL));
	Assert((mouse_ != NULL) && (mouseDevice_ != NULL));
	Assert(hasMoreInput());
	SynchronizedBlock synchronizedBlock(this);
	keyboard_->setNextState(keyboardStates_.popFront());
	mouse_->setNextState(mouseStates_.popFront());
	int joystickCount = joysticks_.getCount();
	for(int i = 0; i < joystickCount; i++){
		joysticks_[i]->setNextState(joystickStates_.popFront());
	}
}
//------------------------------------------------------------------------------
// 入力数の取得
int BufferedInput::getInputCount(){
	Assert((keyboard_ != NULL) && (keyboardDevice_ != NULL));
	Assert((mouse_ != NULL) && (mouseDevice_ != NULL));
	SynchronizedBlock synchronizedBlock(this);
	return keyboardStates_.getCount();
}
//------------------------------------------------------------------------------
// 実行
void BufferedInput::run(Thread* thread){
	Assert((keyboard_ != NULL) && (keyboardDevice_ != NULL));
	Assert((mouse_ != NULL) && (mouseDevice_ != NULL));
	fpsController_.sleep();
	// 停止リクエストが来るまで動作しつづける
	while(!isStopRequested()){
		// デバイスポーリングには排他制御をかけない
		if(logReader_ == NULL){ devicePolling(); }
		{
			SynchronizedBlock synchronizedBlock(this);
			if(logReader_ == NULL){
				// デバイスアップデート
				deviceUpdate();
			}else{
				// ログアップデート
				logUpdate();
			}
			// ログの書き出し
			writeLog();
			// ログ終端チェック
			if((logReader_ != NULL) && (logReader_->isEnd())){
				LampInput::stopLog();
			}
		}
		float interval = fpsController_.sleep();
//		Assert(interval > fpsController_.getTargetInterval() * 0.5f);
//		Assert(interval < fpsController_.getTargetInterval() * 2.f);
	}
}
//------------------------------------------------------------------------------
// デバイスポーリング
void BufferedInput::devicePolling(){
	// 60FPSで1分間入力が取得されなかったら注意しておく
	Assert(keyboardStates_.getCount() < 60 * 60);
	// キーボードのポーリング
	keyboardDevice_->polling();
	// マウスのポーリング
	mouseDevice_->polling();
	// ジョイスティックのポーリング
	int joystickDeviceCount = joystickDevices_.getCount();
	for(int i = 0; i < joystickDeviceCount; i++){
		joystickDevices_[i]->polling();
	}
}
//------------------------------------------------------------------------------
// デバイスアップデート
void BufferedInput::deviceUpdate(){
	// キーボードのアップデート
	keyboardStates_.pushBack(keyboardDevice_->getKeyboardState());
	// マウスのアップデート
	mouseStates_.pushBack(mouseDevice_->getMouseState());
	// ジョイスティックのアップデート
	int joystickDeviceCount = joystickDevices_.getCount();
	for(int i = 0; i < joystickDeviceCount; i++){
		joystickStates_.pushBack(joystickDevices_[i]->getJoystickState());
	}
}
//------------------------------------------------------------------------------
// ログアップデート
void BufferedInput::logUpdate(){
	// キーボードのログ読み込み
	KeyboardState keyboardState;
	keyboardState.readBinary(logReader_);
	keyboardStates_.pushBack(keyboardState);

	// マウスのログ読み込み
	MouseState mouseState;
	mouseState.readBinary(logReader_);
	mouseStates_.pushBack(mouseState);

	// ジョイスティックのログ読み込み
	int joystickCount = joystickDevices_.getCount();
	for(int i = 0; i < joystickCount; i++){
		JoystickState joystickState;
		joystickState.readBinary(logReader_);
		joystickStates_.pushBack(joystickState);
	}
}
//------------------------------------------------------------------------------
// ログの書き出し
void BufferedInput::writeLog(){
	if(logWriter_ == NULL){ return; }
	keyboard_->getState().writeBinary(logWriter_);
	mouse_->getState().writeBinary(logWriter_);
	int joystickCount = joysticks_.getCount();
	for(int i = 0; i < joystickCount; i++){
		joysticks_[i]->getState().writeBinary(logWriter_);
	}
}
//------------------------------------------------------------------------------
// ログ取得
//------------------------------------------------------------------------------
// ログ取得の開始
void BufferedInput::startLogging(BinaryWriter* binaryWriter){
	Assert(logWriter_ == NULL);
	SynchronizedBlock synchronizedBlock(this);
	logWriter_ = binaryWriter;
	logWriter_->writeFloat(fpsController_.getTargetInterval());
}
//------------------------------------------------------------------------------
// ログ取得の終了
void BufferedInput::endLogging(){
	Assert(logWriter_ != NULL);
	SynchronizedBlock synchronizedBlock(this);
	logWriter_ = NULL;
}
//------------------------------------------------------------------------------
// ログ再生
//------------------------------------------------------------------------------
// ログ再生の開始
void BufferedInput::playLog(BinaryReader* binaryReader){
	Assert(logReader_ == NULL);
	SynchronizedBlock synchronizedBlock(this);
	logReader_ = binaryReader;
	fpsController_.setTargetInterval(logReader_->readFloat());
}
//------------------------------------------------------------------------------
// ログ再生の停止
void BufferedInput::stopLog(){
	Assert(logReader_ != NULL);
	SynchronizedBlock synchronizedBlock(this);
	logReader_ = NULL;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
