//------------------------------------------------------------------------------
// 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 "Framework/System/BasicFramework.h"
#include "Core/Utility/FPSMeasurement.h"
#include "Graphics/System/LampGraphics.h"
#include "Graphics/Enumeration/GraphicsDeviceEnumeration.h"
#include "Graphics/System/GraphicsDevice.h"
#include "Graphics/Renderer/RenderingDevice.h"
#include "Input/System/LampInput.h"

#include "Core/System/StringMethod.h" // StdStrcpy

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ、デストラクタ
//------------------------------------------------------------------------------
// コンストラクタ
BasicFramework::BasicFramework(const String& name) : SimpleFramework(name){
	// フォント記述初期化
	font_ = NULL;
	::memset(&fontDescription_, 0, sizeof(D3DXFONT_DESC));
	fontDescription_.Height = 14;
	fontDescription_.Weight = FW_NORMAL; 
	fontDescription_.Weight = FW_BOLD; 
	fontDescription_.CharSet = SHIFTJIS_CHARSET;
	StdStrcpy(fontDescription_.FaceName, "ＭＳ ゴシック");
	// FPS計測初期化
	drawFPSMeasurement_ = new FPSMeasurement();
	gameFPSMeasurement_ = new FPSMeasurement();
	drawFPS_ = gameFPS_ = 0.f;
	// グラフィックスデバイスを確認するための設定
	GraphicsDeviceEnumeration::getInstance()->setConfirmGraphicsDevice(this);
	// デバイスオブジェクトホルダ登録
	LampGraphics::addDeviceObjectHolder(this);
}
//------------------------------------------------------------------------------
// デストラクタ
BasicFramework::~BasicFramework(){
	// デバイスオブジェクトホルダ登録解除
	LampGraphics::removeDeviceObjectHolder(this);
	// FPS計測後始末
	delete gameFPSMeasurement_;
	delete drawFPSMeasurement_;
}
//------------------------------------------------------------------------------
// フレームワークメソッド
//------------------------------------------------------------------------------
// メインループ
void BasicFramework::mainLoop(){
	// 1/60秒ごとに追加される入力バッファの数だけ実行する
	// これにより描画フレームレートに左右されないゲーム速度を実現できる
	// ゲーム速度が1FPS以下になるようであれば永久ループを防ぐため入力を切り捨てる
	int inputCount = 0;
	LampInput::waitForInput();
	while(LampInput::hasMoreInput()){
		LampInput::nextInput();
		frameworkRun();
		// ゲームFPS計測
		gameFPS_ = gameFPSMeasurement_->measurement();
		inputCount++;
		if(inputCount == 60){
			LampInput::bufferClear();
			break;
		}
	}
	// 描画準備
	frameworkRenderSetup();
	// プレゼンテーションを行う
	frameworkPresentation();
	// 描画
	frameworkRender();
	frameworkDrawInformation();
	// 描画FPS計測
	drawFPS_ = drawFPSMeasurement_->measurement();
}
//------------------------------------------------------------------------------
// フレームワーク実行
void BasicFramework::frameworkRun(){
	SimpleFramework::frameworkRun();
	// Alt+Enterキーでフルスクリーン切替
	if(keyboard_->down(Keyboard::keyEnter) &&
		(keyboard_->pressed(Keyboard::keyLeftAlt) ||
		keyboard_->pressed(Keyboard::keyRightAlt))){
		GraphicsDevice::getInstance()->toggleFullscreen();
	}
	// F9キーでデバイス再構築
	if(keyboard_->down(Keyboard::keyF9)){
		GraphicsDevice::getInstance()->rebuild();
	}
}
//------------------------------------------------------------------------------
// フレームワーク情報表示
void BasicFramework::frameworkDrawInformation(){
	if(font_ == NULL){ return; }
	RenderingDevice* device = RenderingDevice::getInstance();
	if(!device->beginScene()){ return; }
	// 描画矩形算出
	::GetClientRect(windowHandle_, &informationDrawRect_);
	int gap = 5;
	informationDrawRect_.top += gap;
	informationDrawRect_.bottom -= gap;
	informationDrawRect_.left += gap * 2;
	informationDrawRect_.right -= gap * 2;
	// 情報描画
	drawInformation();
	device->endScene();
}
//------------------------------------------------------------------------------
// 情報描画関連
//------------------------------------------------------------------------------
// 情報描画
void BasicFramework::drawInformation(){
	drawInformationString(getFPSString(), Color4c::white, true, true);
}
//------------------------------------------------------------------------------
// 情報文字列描画
void BasicFramework::drawInformationString(
	const String& message, Color4c color, bool alignRight, bool alignBottom){
	if(font_ == NULL){ return; }
	// アライメントフラグ作成
	u_int alignFlag = 0;
	if(alignRight){
		alignFlag |= (DT_RIGHT | DT_SINGLELINE);
	}else{
		alignFlag |= DT_LEFT;
	}
	if(alignBottom){
		alignFlag |= (DT_BOTTOM | DT_SINGLELINE);
	}else{
		alignFlag |= DT_TOP;
	}
	// 文字描画
	font_->DrawText(NULL, message.getBytes(), -1, &informationDrawRect_,
		alignFlag, color.getARGB());
}
//------------------------------------------------------------------------------
// FPS文字列の取得
String BasicFramework::getFPSString() const{
	String fpsString;
	fpsString.format("Game %4.1f  Draw %4.1f", gameFPS_, drawFPS_);
	return fpsString;
}
//------------------------------------------------------------------------------
// デバイスオブジェクト関連
//------------------------------------------------------------------------------
// デバイスオブジェクトの初期化
bool BasicFramework::initializeGraphicsDeviceObjects(){
	// フォント初期化
	if(DirectXFailed(D3DXCreateFontIndirect(
		LampGraphics::getDirect3DDevice(), &fontDescription_, &font_))){
		ErrorOut("BasicFramework::initializeGraphicsDeviceObjects() "
			"フォントの作成に失敗しました");
		SafeRelease(font_);
		return false;
	}
	return true;
}
//------------------------------------------------------------------------------
// デバイスオブジェクトの削除
void BasicFramework::deleteGraphicsDeviceObjects(){
	// フォント後始末
	SafeRelease(font_);
}
//------------------------------------------------------------------------------
// デバイスオブジェクトのリストア
bool BasicFramework::restoreGraphicsDeviceObjects(){
	// フォント
	if(font_ != NULL){
		if(DirectXFailed(font_->OnResetDevice())){
			ErrorOut("BasicFramework::restoreGraphicsDeviceObjects() "
				"フォントの作成に失敗しました");
			SafeRelease(font_);
			return false;
		}
	}
	return true;
}
//------------------------------------------------------------------------------
// デバイスオブジェクトの無効化
void BasicFramework::invalidateGraphicsDeviceObjects(){
	// フォント
	if(font_ != NULL){ font_->OnLostDevice(); }
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
