//------------------------------------------------------------------------------
// 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/SimpleFramework.h"
#include "Graphics/System/LampGraphics.h"
#include "Graphics/System/GraphicsDevice.h"
#include "Input/System/LampInput.h"
#include "Sound/System/LampSound.h"
#include "Core/Utility/FPSController.h"

namespace Lamp{

// インスタンス
SimpleFramework* SimpleFramework::instance_ = NULL;

//------------------------------------------------------------------------------
// 生成、破棄
//------------------------------------------------------------------------------
// コンストラクタ
SimpleFramework::SimpleFramework(const String& name) : name_(name){
	// メンバクリア
	windowHandle_ = NULL;
	minimumWindowSize_.set(320, 240);
	backGroundColor_.set(0x80, 0x80, 0x80, 0);
	fpsController_ = new FPSController();
	keyboard_ = NULL;
	mouse_ = NULL;
	// デフォルトではウィンドウモードで起動
	startFullscreen_ = false;
	// LampGraphics初期化
	LampGraphics::initialize();
}
//------------------------------------------------------------------------------
// デストラクタ
SimpleFramework::~SimpleFramework(){
	SafeDelete(fpsController_);
}
//------------------------------------------------------------------------------
// 実行
int SimpleFramework::execute(HINSTANCE instance){
	int result = 1;
	// フレームワーク初期化
	if(frameworkInitialize(instance)){
		// ユーザ初期化呼び出し
		if(initialize()){
			// フレームワーク実行
			result = frameworkExecute();
		}
		// ユーザ後始末呼び出し
		finalize();
	}
	// フレームワーク後始末
	frameworkFinalize();
	return result;
}
//------------------------------------------------------------------------------
// フレームワークメソッド
//------------------------------------------------------------------------------
// フレームワークの初期化
bool SimpleFramework::frameworkInitialize(HINSTANCE instanceHandle){
	// ウィンドウハンドルが指定されていなければウィンドウを作成する
	if(windowHandle_ == NULL){
		createWindowParameter_.windowName_ = name_;
		createWindowParameter_.instanceHandle_ = instanceHandle;
		// フレームワークのプロシージャはパラメータがNULLだった場合のみ使用する
		if(createWindowParameter_.windowProcedure_ == NULL){
			createWindowParameter_.windowProcedure_ =
				registrationWindowProcedure;
		}
		windowHandle_ = WindowCreator::create(createWindowParameter_);
	}
	// ウィンドウが作成できなければ初期化失敗
	if(windowHandle_ == NULL){ return false; }

	// Graphicsデバイス初期化
	if(!LampGraphics::initializeDevice(windowHandle_, startFullscreen_)){
		return false;
	}
	// 初回背景クリア
	GraphicsDevice::getInstance()->clear(backGroundColor_);

	// LampInput初期化
	if(!LampInput::initialize(
		instanceHandle, windowHandle_, LampInput::modeBuffering)){
		return false;
	}
	keyboard_ = LampInput::getKeyboard();
	mouse_ = LampInput::getMouse();

	// LampSound初期化
	if(!LampSound::initialize(windowHandle_)){ return false; }

	// インスタンスポインタ設定
	Assert(instance_ == NULL);
	instance_ = this;
	return true;
}
//------------------------------------------------------------------------------
// フレームワークの後始末
void SimpleFramework::frameworkFinalize(){
	// インスタンスポインタクリア
	instance_ = NULL;
	// LampSoundの後始末
	LampSound::finalize();
	// LampInputの後始末
	LampInput::finalize();
	// Lampグラフィクスの後始末
	LampGraphics::finalize();
	// ウィンドウの破棄
	WindowCreator::destroy(windowHandle_);
	// Lampコアの後始末
	LampCore::finalize();
}
//------------------------------------------------------------------------------
// フレームワークの実行
int SimpleFramework::frameworkExecute(){
	// アプリケーションがアクティブでない時も動作させる
	// アクティブでない時に動作させないようにするにはGetMessage()を使用する
	MSG message;
	bool gotMessage;
	message.message = WM_NULL;
	::PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE);
	// 入力バッファを一旦クリアする
	if(LampInput::getInputMode() == LampInput::modeBuffering){
		LampInput::bufferClear();
	}
	// メッセージループ
	while(message.message != WM_QUIT){
		// メッセージ処理
		gotMessage = (PeekMessage(&message, NULL, 0, 0, PM_REMOVE) != 0);
		if(gotMessage){
			TranslateMessage(&message);
			DispatchMessage(&message);
		}else{
			// メッセージが無ければメインループを実行する
			mainLoop();
		}
	}
	return 0;
}
//------------------------------------------------------------------------------
// ループ内の処理
//------------------------------------------------------------------------------
// メインループ
void SimpleFramework::mainLoop(){
	// 1/60秒ごとに追加される入力バッファの数だけ実行する
	// これにより描画フレームレートに左右されないゲーム速度を実現できる
	// ゲーム速度が1FPS以下になるようであれば永久ループを防ぐため入力を切り捨てる
	int inputCount = 0;
	LampInput::waitForInput();
	while(LampInput::hasMoreInput()){
		LampInput::nextInput();
		frameworkRun();
		inputCount++;
		if(inputCount == 60){
			LampInput::bufferClear();
			break;
		}
	}
	// 描画準備
	frameworkRenderSetup();
	// プレゼンテーションを行う
	frameworkPresentation();
	// 描画
	frameworkRender();
}
//------------------------------------------------------------------------------
// フレームワーク実行
void SimpleFramework::frameworkRun(){
	run();
	// Escキーで終了
	if(keyboard_->down(Keyboard::keyEscape)){ PostQuitMessage(0); }
}
//------------------------------------------------------------------------------
// フレームワークレンダリング準備
void SimpleFramework::frameworkRenderSetup(){
	renderSetup();
}
//------------------------------------------------------------------------------
// フレームワークプレゼンテーション
bool SimpleFramework::frameworkPresentation(){
	// サウンドのプレゼンテーション
	LampSound::presentation();

	// FPSの調整、これにより60FPS以上にならない
	fpsController_->sleep();

	// グラフィックスのプレゼンテーション
	return GraphicsDevice::getInstance()->presentation();
}
//------------------------------------------------------------------------------
// フレームワークレンダリング
void SimpleFramework::frameworkRender(){
	// 背景クリア
	GraphicsDevice::getInstance()->clear(backGroundColor_);
	render();
}
//------------------------------------------------------------------------------
// ウィンドウプロシージャ関連
//------------------------------------------------------------------------------
// 登録用ウィンドウプロシージャ
LRESULT CALLBACK SimpleFramework::registrationWindowProcedure(
	HWND windowHandle, u_int message, WPARAM wParam, LPARAM lParam){
	// 初期化前ならデフォルトプロシージャのみを呼ぶ
	if(instance_ == NULL){
		return DefWindowProc(windowHandle, message, wParam, lParam);
	}

	// ユーザプロシージャ呼び出し
	LRESULT result;
	result = instance_->windowProcedure(windowHandle, message, wParam, lParam);
	if(result != 0){ return result; }

	// Lampプロシージャ呼び出し
	result = LampCore::windowProcedure(windowHandle, message, wParam, lParam);
	if(result != 0){ return result; }

	// フレームワークプロシージャ呼び出し
	return instance_->frameworkWindowProcedure(
		windowHandle, message, wParam, lParam);
}
//------------------------------------------------------------------------------
// フレームワークウィンドウプロシージャ
LRESULT SimpleFramework::frameworkWindowProcedure(
	HWND windowHandle, u_int message, WPARAM wParam, LPARAM lParam){
	switch(message){
	// 最小サイズの設定
	case WM_GETMINMAXINFO:
		((MINMAXINFO*)lParam)->ptMinTrackSize.x = minimumWindowSize_.width;
		((MINMAXINFO*)lParam)->ptMinTrackSize.y = minimumWindowSize_.height;
		break;
	}
	// 最後にデフォルトウィンドウプロシージャを呼ぶ
	return DefWindowProc(windowHandle, message, wParam, lParam);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
