//------------------------------------------------------------------------------
// 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
 * Lampグラフィクス実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Graphics/System/LampGraphics.h"
#include "Graphics/Enumeration/GraphicsDeviceEnumeration.h"
#include "Graphics/DeviceSelector/GraphicsDeviceSelector.h"
#include "Graphics/System/GraphicsDeviceSettings.h"
#include "Graphics/DeviceSelector/DesktopGraphicsDeviceSelector.h"
#include "Graphics/System/GraphicsDevice.h"
#include "Graphics/System/GraphicsDeviceCapacity.h"
#include "Graphics/Renderer/RenderingDevice.h"
#include "Graphics/Shader/ShaderManager.h"
#include "Graphics/System/GraphicsDeviceObjectHolder.h"

namespace Lamp{

// ウィンドウハンドル
HWND LampGraphics::windowHandle_ = NULL;
// グラフィックスデバイスの列挙
GraphicsDeviceEnumeration* LampGraphics::enumeration_ = NULL;
// グラフィックスデバイスセレクタ
GraphicsDeviceSelector* LampGraphics::selector_ = NULL;
// グラフィックスデバイス設定
GraphicsDeviceSettings* LampGraphics::settings_ = NULL;
// グラフィックスデバイス
GraphicsDevice* LampGraphics::device_ = NULL;
// Direct3Dデバイス
Direct3DDevice* LampGraphics::direct3DDevice_ = NULL;
// グラフィックスデバイス能力
GraphicsDeviceCapacity* LampGraphics::deviceCapacity_ = NULL;
// レンダリングデバイス
RenderingDevice* LampGraphics::renderingDevice_ = NULL;
// シェーダマネージャ
ShaderManager* LampGraphics::shaderManager_ = NULL;
// Direct3D
Direct3D* LampGraphics::direct3D_ = NULL;
// デバイスオブジェクトホルダ
ArrayList<GraphicsDeviceObjectHolder*> LampGraphics::deviceObjectHolders_;

// シーン配列
ArrayList<Scene*> LampGraphics::sceneArray_(scenesCapacity_);
// シーンデータベース
HashMap<String, Scene*> LampGraphics::sceneDatabase_(scenesCapacity_, 1.f);

// 初期化フラグ
bool LampGraphics::isInitialized_ = false;
// デバイス初期化フラグ
bool LampGraphics::deviceInitialized_ = false;

//------------------------------------------------------------------------------
// 初期化
void LampGraphics::initialize(){
	if(isInitialized_){ return; }
	LampCore::initialize();
	enumeration_ = new GraphicsDeviceEnumeration();
	selector_ = new DesktopGraphicsDeviceSelector();
	settings_ = new GraphicsDeviceSettings();
	device_ = new GraphicsDevice();
	deviceCapacity_ = new GraphicsDeviceCapacity();
	renderingDevice_ = new RenderingDevice(deviceCapacity_);
	shaderManager_ = new ShaderManager();
	// 初期化フラグ立てる
	isInitialized_ = true;
	return;
}
//------------------------------------------------------------------------------
// デバイスの初期化
bool LampGraphics::initializeDevice(
	HWND windowHandle, bool startFullscreen){
	Assert(isInitialized_);
	if(deviceInitialized_){ return true; }
	windowHandle_ = windowHandle;
	// Direct3Dの作成
	direct3D_ = Direct3DCreate(D3D_SDK_VERSION);
	if(direct3D_ == NULL){
		ErrorOut("LampGraphics::initialize() Direct3DCreate()");
		return false;
	}
	// グラフィックスデバイスの列挙を行う
	if(!enumeration_->enumerate()){
		ErrorOut("LampGraphics::initialize() Enumerate()");
		return false;
	}
	// デバイス設定の初期化
	if(!selector_->chooseDeviceSettings(windowHandle_, startFullscreen)){
		ErrorOut("LampGraphics::initialize() chooseDeviceSettings()");
		return false;
	}
	// デバイスの初期化
	device_->initializeWindowHandle(windowHandle_);
	if(!device_->initialize()){
		ErrorOut("LampGraphics::initialize() initializeDevice()");
		return false;
	}
	// デバイス初期化フラグ立てる
	deviceInitialized_ = true;
	return true;
}
//------------------------------------------------------------------------------
// 後始末
void LampGraphics::finalize(){
	// デバイス初期化の後始末
	device_->cleanup();
	// Direct3Dの解放
	SafeRelease(direct3D_);
	windowHandle_ = NULL;
	// デバイス初期化フラグクリア
	deviceInitialized_ = false;

	// 初期化の後始末
	SafeDelete(shaderManager_);
	SafeDelete(renderingDevice_);
	SafeDelete(deviceCapacity_);
	SafeDelete(device_);
	SafeDelete(selector_);
	SafeDelete(settings_);
	SafeDelete(enumeration_);
	// 初期化フラグクリア
	isInitialized_ = false;

	// シーンデータベースの整理
	Assert(getSceneCount() == 0);
	if(sceneArray_.getCapacity() != scenesCapacity_){
		sceneArray_.setCapacity(scenesCapacity_);
		sceneDatabase_.setCapacity(scenesCapacity_);
	}
}
//------------------------------------------------------------------------------
// ウィンドウプロシージャ
LRESULT LampGraphics::windowProcedure(
	HWND windowHandle, u_int message, WPARAM wParam, LPARAM lParam){
	// デバイスのプロシージャを呼び出す
	return device_->windowProcedure(windowHandle, message, wParam, lParam);
}
//------------------------------------------------------------------------------
// グラフィックスデバイスセレクタの設定
void LampGraphics::setDeviceSelector(GraphicsDeviceSelector* selector){
	SafeDelete(selector_);
	selector_ = selector;
}
//------------------------------------------------------------------------------
// デバイス関係
//------------------------------------------------------------------------------
// デバイスがリセットされた
void LampGraphics::deviceReset(){
	direct3DDevice_ = device_->getDirect3DDevice();
	// デバイス能力のリセットを最優先で行う
	deviceCapacity_->deviceReset(direct3DDevice_);
	// デフォルトステートブロックの構築を次に行う
	renderingDevice_->restoreDefaultStateBlock(direct3DDevice_);
}
//------------------------------------------------------------------------------
// デバイスオブジェクトの初期化
bool LampGraphics::initializeDeviceObjects(){
	bool result = true;
	int objectHolderCount = getDeviceObjectHolderCount();
	for(int i = 0; i < objectHolderCount; i++){
		GraphicsDeviceObjectHolder* holder = getDeviceObjectHolder(i);
		if(!holder->initializeGraphicsDeviceObjects()){ result = false; }
	}
	return result;
}
//------------------------------------------------------------------------------
// デバイスオブジェクトの削除
void LampGraphics::deleteDeviceObjects(){
	int objectHolderCount = getDeviceObjectHolderCount();
	for(int i = 0; i < objectHolderCount; i++){
		getDeviceObjectHolder(i)->deleteGraphicsDeviceObjects();
	}
}
//------------------------------------------------------------------------------
// デバイスオブジェクトのリストア
bool LampGraphics::restoreDeviceObjects(){
	bool result = true;
	// デバイスオブジェクトホルダのリストア
	int objectHolderCount = getDeviceObjectHolderCount();
	for(int i = 0; i < objectHolderCount; i++){
		GraphicsDeviceObjectHolder* holder = getDeviceObjectHolder(i);
		if(!holder->restoreGraphicsDeviceObjects()){ result = false; }
	}
	return result;
}
//------------------------------------------------------------------------------
// デバイスオブジェクトの無効化
void LampGraphics::invalidateDeviceObjects(){
	// デバイスオブジェクトホルダの無効化
	int objectHolderCount = getDeviceObjectHolderCount();
	for(int i = 0; i < objectHolderCount; i++){
		getDeviceObjectHolder(i)->invalidateGraphicsDeviceObjects();
	}
	// デフォルトステートブロックの無効化
	renderingDevice_->invalidateDefaultStateBlock();
}
//------------------------------------------------------------------------------
// シーン関係
//------------------------------------------------------------------------------
// シーンの作成
Scene* LampGraphics::createScene(const String& name){
	// 名前の長さチェック
	if(name.getSize() == 0){
		ErrorOut("LampGraphics::createScene() name.getSize() == 0");
		return NULL;
	}
	// 名前の重複チェック
	if(search(name) != NULL){
		ErrorOut("LampGraphics::createScene() repetition name %s",
			name.getBytes());
		return NULL;
	}
	Scene* scene = new Scene(name);
	sceneDatabase_.put(name, scene);
	sceneArray_.add(scene);
	addDeviceObjectHolder(scene);
	return scene;
}
//------------------------------------------------------------------------------
// シーンの破棄
void LampGraphics::destroyScene(Scene* scene){
	removeDeviceObjectHolder(scene);
	sceneArray_.removeByValue(scene);
	sceneDatabase_.remove(scene->getName());
	delete scene;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
