////////////////////////////////////////////////////////////////////////////////////////////////////
// インクルード
////////////////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <vector>

#include "Mix/IO.h"
#include "Mix/HID.h"
#include "Mix/Graphics.h"
#include "Mix/Scene.h"

#include "Utility/Common.h"
#include "Utility/ViewCameraHelper.h"

#ifdef _DEBUG
	#include <crtdbg.h>
#endif //_DEBUG

////////////////////////////////////////////////////////////////////////////////////////////////////
// 定数
////////////////////////////////////////////////////////////////////////////////////////////////////

/*
	スクリーン
*/

#define SCREEN_WIDTH ( 1024 )
#define SCREEN_HEIGHT ( 768 )

/*
	キャラクターの位置テーブル
*/

static const UInt32 CHAR_NUM = 4;
static const Float32 CHAR_SPACE = 2.5f;
static const Float32 CHAR_Y = 1.0f;
static const Mix::Vector3 CHAR_POS_TABLE[CHAR_NUM] =
{
	Mix::Vector3( -CHAR_SPACE, CHAR_Y, -CHAR_SPACE ),
	Mix::Vector3( -CHAR_SPACE, CHAR_Y, +CHAR_SPACE ),
	Mix::Vector3( +CHAR_SPACE, CHAR_Y, +CHAR_SPACE ),
	Mix::Vector3( +CHAR_SPACE, CHAR_Y, -CHAR_SPACE ),
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// メイン
////////////////////////////////////////////////////////////////////////////////////////////////////

int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow )
{
#ifdef _DEBUG
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif //_DEBUG

	Mix::ENGINE_CONFIG engineConfig;

	engineConfig.flags = Mix::EC_IO_NARROW | Mix::EC_KEYBOARD | Mix::EC_MOUSE | Mix::EC_GRAPHICS | Mix::EC_SCENE | Mix::EC_DYNAMICS | Mix::EC_PARALLEL;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Scene\\Dynamics\\Ragdoll" );
	engineConfig.pCaption = L"Scene - Dynamics ( Ragdoll )";
	engineConfig.targetSize = Mix::Point( SCREEN_WIDTH, SCREEN_HEIGHT );
	engineConfig.shaderModel = Mix::Graphics::SHADER_MODEL_3;

	if( Mix::Initialize( engineConfig ) == True )
	{
		Mix::IEngine* pEngine = Mix::GetEnginePtr();

		Mix::IO::IManager* pIOMgr = Mix::IO::GetManagerPtr();

		Mix::HID::IManager* pInputMgr = Mix::HID::GetManagerPtr();
		Mix::HID::IKeyboard* pKeyboard = NULL;
		Mix::HID::IMouse* pMouse = NULL;

		Mix::Dynamics::IManager* pDynamicsMgr = Mix::Dynamics::GetManagerPtr();

		Mix::Graphics::IManager* pGraphicsMgr = Mix::Graphics::GetManagerPtr();
		Mix::Graphics::IDevice* pGraphicsDev = NULL;
		Mix::Graphics::Utility::ICanvasRenderer* pCanvasRenderer = NULL;
		Mix::Graphics::Utility::IFont* pFont = NULL;

		Mix::Scene::IManager* pSceneMgr = Mix::Scene::GetManagerPtr();
		Mix::Scene::IEffectPackage* pSceneEffectPackage = NULL;
		Mix::Scene::IRenderer* pSceneRenderer = NULL;
		Mix::Scene::IViewCamera* pCamera = NULL;

#ifdef _DEBUG
		Mix::Graphics::Utility::IPerspectiveRenderer* pDebDrawer = NULL;
#endif //_DEBUG

		std::vector<Mix::Scene::IActorModel*> actorList;
		std::vector<Mix::Scene::IActorModel*>::iterator it_a_begin;
		std::vector<Mix::Scene::IActorModel*>::iterator it_a_end;
		std::vector<Mix::Scene::IActorModel*>::iterator it_a;

		Utility::ViewCameraHelper camHelper;

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pIOMgr->MountDirectory( L"Data" );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pInputMgr->GetKeyboard( &pKeyboard );
		pInputMgr->GetMouse( &pMouse );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pGraphicsMgr->GetDevice( &pGraphicsDev );

		if( pGraphicsMgr->CreateCanvasRenderer( &pCanvasRenderer, MIX_DEBUGNAME ) == True )
		{
			if( pGraphicsMgr->CreateFontFromFile( L"Data\\Font\\mspg_16_400.fnt", &pFont ) == True )
			{
				pCanvasRenderer->SetFont( pFont );
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////

		if( pSceneMgr->CreateEffectPackage( L"Data\\", L"effects", &pSceneEffectPackage ) == True )
		{
			Mix::Scene::RENDERER_CONFIG config;

			config.caps = Mix::Scene::RCAP_DYNAMICS | Mix::Scene::RCAP_SHADOW_MAPPING;

			if( pSceneMgr->CreateRenderer( pSceneEffectPackage, config, &pSceneRenderer, MIX_DEBUGNAME ) == True )
			{
				Mix::Scene::CAMERA_CONFIG camConfig;
				Mix::Graphics::ITexture* pReflectTex = NULL;
				Mix::Scene::IDirectionalLight* pDirLight = NULL;
				Mix::Scene::ITerrainModel* pFloorModel = NULL;
				Mix::Scene::IFloor* pFloor = NULL;
				Mix::Scene::IActorModel* pCharModel = NULL;

				/*
					反射
				*/

				if( pGraphicsDev->CreateTextureFromFile( L"Data\\Texture\\cube.dds", &pReflectTex ) == True )
				{
					pSceneRenderer->SetEnvironmentTexture( pReflectTex );
					MIX_RELEASE( pReflectTex );
				}

				/*
					カメラ
				*/

				camConfig.targetSize.Set( SCREEN_WIDTH, SCREEN_HEIGHT );
				camConfig.caps = Mix::Scene::RCAP_DYNAMICS | Mix::Scene::RCAP_SHADOW_MAPPING;
				camConfig.depthBuffFormat = Mix::Graphics::FMT_D16;
				camConfig.colorTexFormat = Mix::Graphics::FMT_R8G8B8A8;

				if( pSceneMgr->CreateViewCamera( camConfig, &pCamera ) == True )
				{
					Mix::Scene::ICamera::SHADOW_MAPPING_SETTINGS smSettings = pCamera->GetShadowMappingSettings();

					pCamera->SetBackgroundColor( Mix::Vector4( 0.1f, 0.1f, 0.1f, 1.0f ) );
					pCamera->SetProjection( MIX_TO_RAD( 60.0f ), 0.1f, 100.0f );

					smSettings.depthBias = 0.1f;
					pCamera->SetShadowMappingSettings( smSettings );
					pCamera->SetShadowMappingEnabled( True );

					pSceneRenderer->AddCamera( pCamera );
				}

				/*
					ディレクショナルライト
				*/

				if( pSceneMgr->CreateDirectionalLight( &pDirLight ) == True )
				{
					pDirLight->SetEnabled( True );
					pDirLight->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
					pDirLight->SetDirection( Mix::Vector3( -1.0f, -1.0f, -1.0f ).ToNormalize() );

					pSceneRenderer->SetDirectionalLight( pDirLight );

					MIX_RELEASE( pDirLight );
				}

				/*
					床
				*/

				if( pSceneMgr->CreateTerrainModelFromFile( pSceneEffectPackage, L"Data\\Model\\floor.mtm", &pFloorModel ) == True )
				{
					pSceneRenderer->AddTerrainModel( pFloorModel );
					MIX_RELEASE( pFloorModel );
				}

				if( pSceneMgr->CreateFloor( Mix::Vector3( 0.0f, 1.0f, 0.0f ), 0.0f, &pFloor ) == True )
				{
					pSceneRenderer->SetFloor( pFloor );
					MIX_RELEASE( pFloor );
				}

				/*
					キャラクター
				*/

				if( pSceneMgr->CreateActorModelFromFile( pSceneEffectPackage, L"Data\\Model\\human.mam", &pCharModel ) == True )
				{
					for( UInt32 i = 0; i < CHAR_NUM; i++ )
					{
						Mix::Scene::IActorModel* pCloneModel = NULL;

						if( pCharModel->Clone( &pCloneModel ) == True )
						{
							Mix::Scene::IActorDynamicsDirector* pDynDir = NULL;
							Mix::Matrix4x4 worldMat;

							pCloneModel->GetDynamicsDirector( &pDynDir );

							pDynDir->SetCharacterEnabled( False );
							pDynDir->SetCharacterMode( Mix::Scene::DKC_RECEIVE );
							pDynDir->SetColliderEnabled( True );
							pDynDir->SetColliderMode( Mix::Scene::DC_RAGDOLL );
							pDynDir->SetSensorEnabled( True );

							worldMat.SetTranslation( CHAR_POS_TABLE[i] );
							pCloneModel->SetWorldMatrix( worldMat );
							pCloneModel->Reset();

							pSceneRenderer->AddActorModel( pCloneModel );
							actorList.push_back( pCloneModel );

							MIX_RELEASE( pDynDir );
						}
					}

					MIX_RELEASE( pCharModel );
				}
			}
		}

#ifdef _DEBUG
		pGraphicsMgr->CreatePerspectiveRenderer( &pDebDrawer, MIX_DEBUGNAME );
		pSceneRenderer->Debug_SetPerspectiveRenderer( pDebDrawer );
#endif //_DEBUG

		////////////////////////////////////////////////////////////////////////////////////////////////////

		camHelper.Initialize(	pMouse,
								pKeyboard,
								Mix::Vector3( 0.0f, 1.0f, 0.0f ),
								Mix::Vector2( MIX_TO_RAD( 20.0f ), MIX_TO_RAD( 180.0f ) ),
								10.0f );

		it_a_begin = actorList.begin();
		it_a_end = actorList.end();

		pEngine->ResetFPS();

		////////////////////////////////////////////////////////////////////////////////////////////////////

		while( pEngine->Update() == True )
		{
			Float32 dt = pEngine->GetDT();
			Float32 baseDt = pEngine->GetBaseDT();

			Mix::String tempStr;
			Mix::Vector2 txtSize;

			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pKeyboard->GetKeyState( Mix::HID::KEY_R ) & Mix::HID::PRESSED )
			{
				MIX_ASSERT( actorList.size() == CHAR_NUM );

				for( UInt32 i = 0; i < CHAR_NUM; i++ )
				{
					Mix::Scene::IActorModel* pModel = actorList[i];
					Mix::Scene::IActorDynamicsDirector* pDynDir = NULL;
					Mix::Matrix4x4 worldMat;

					worldMat.SetTranslation( CHAR_POS_TABLE[i] );
					pModel->SetWorldMatrix( worldMat );
					pModel->Reset( Mix::Scene::IActorModel::R_ALL );

					pModel->GetDynamicsDirector( &pDynDir );
					pDynDir->Activate();

					MIX_RELEASE( pDynDir );
				}
			}

			if( pMouse->GetButtonState( 0 ) & Mix::HID::DOWN )
			{
				pCamera->DragObject( pMouse->GetPos() );
			}

#ifdef _DEBUG
			if( pKeyboard->GetKeyState( Mix::HID::KEY_D ) & Mix::HID::PRESSED )
			{
				UInt32 drawFlags = pCamera->Debug_GetDrawFlags();

				if( MIX_TESTBIT( drawFlags, Mix::Scene::DDF_ACTORMODEL_MESH | Mix::Scene::DDF_TERRAINMODEL_MESH ) != 0 )
				{
					drawFlags = Mix::Scene::DDF_DYNAMICS;
				}
				else
				{
					drawFlags = Mix::Scene::DDF_ACTORMODEL_MESH | Mix::Scene::DDF_TERRAINMODEL_MESH;
				}

				pCamera->Debug_SetDrawFlags( drawFlags );
			}
#endif //_DEBUG

			////////////////////////////////////////////////////////////////////////////////////////////////////

			/*
				マウスカーソル
			*/

			Utility::DrawMouseCursor( pCanvasRenderer, pMouse->GetPos() );

			/*
				FPS
			*/

			tempStr.Sprintf( L"FPS %f", pEngine->GetFPS() );

			pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
			pCanvasRenderer->AddString( 16.0f, 16.0f, tempStr.GetConstPtr() );

			/*
				説明
			*/

			Float32 txtOffset;

#ifdef _DEBUG
			tempStr.Sprintf( L"%s",	L"Controls :\n"
									L"  Pick object    : Mouse left button\n"
									L"  Rotate camera  : Mouse middle button\n"
									L"  Move camera    : Mouse middle button + Shift key\n"
									L"  Distant camera : Mouse middle button + Ctrl key or Mouse wheel\n"
									L"  Reset          : Keyboard R key\n"
									L"  Toggle debug   : Keyboard D key" );

			txtOffset = 32.0f;
#else //_DEBUG
			tempStr.Sprintf( L"%s",	L"Controls :\n"
									L"  Pick object    : Mouse left button\n"
									L"  Rotate camera  : Mouse middle button\n"
									L"  Move camera    : Mouse middle button + Shift key\n"
									L"  Distant camera : Mouse middle button + Ctrl key or Mouse wheel\n"
									L"  Reset          : Keyboard R key\n" );

			txtOffset = 16.0f;
#endif //_DEBUG

			txtSize = Mix::Graphics::Utility::MeasureString( pFont, tempStr.GetConstPtr() );

			pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
			pCanvasRenderer->AddString( 16.0f, static_cast<Float32>( SCREEN_HEIGHT ) - txtSize.y - txtOffset, tempStr.GetConstPtr() );

#ifdef _DEBUG
			Mix::Scene::DEBUG_PROFILE_RENDERER_GENERAL debProfGene;

			if( pSceneRenderer->Debug_GetProfile( Mix::Scene::DPT_GENERAL, &debProfGene, sizeof( debProfGene ) ) > 0 )
			{
				Utility::BAR_ELEMENT barElems[4];
				static const UInt32 barElemNum = sizeof( barElems ) / sizeof( Utility::BAR_ELEMENT );
				Mix::Rectangle barRect( 0, SCREEN_HEIGHT - 16, SCREEN_WIDTH, 16 );

				barElems[0].value = debProfGene.updElapsedTime;
				barElems[1].value = debProfGene.refElapsedTime;
				barElems[2].value = debProfGene.drawElapsedTime;
				barElems[3].value = debProfGene.finElapsedTime;

				Utility::FillBarColor( barElems, barElemNum );
				Utility::DrawBar( pCanvasRenderer, barRect, barElems, barElemNum, dt );
			}
#endif //_DEBUG

			////////////////////////////////////////////////////////////////////////////////////////////////////

			camHelper.Update();
			pCamera->SetView( camHelper.GetAt(), camHelper.GetRotation(), camHelper.GetDistance() );
			pCamera->Update();

			for( it_a = it_a_begin; it_a != it_a_end; ++it_a )
			{
				( *it_a )->Update( dt );
			}

			pSceneRenderer->Update( dt, baseDt );

			pCanvasRenderer->Update();

			////////////////////////////////////////////////////////////////////////////////////////////////////

			for( it_a = it_a_begin; it_a != it_a_end; ++it_a )
			{
				( *it_a )->Refresh();
			}

			pSceneRenderer->Refresh();

			////////////////////////////////////////////////////////////////////////////////////////////////////

			pGraphicsDev->SetViewBounds( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
			pGraphicsDev->Clear( True, False, Mix::Vector4( 0.1f, 0.1f, 0.1f, 1.0f ) );

			if( pGraphicsDev->Begin() == True )
			{
				pCamera->Draw();
				pCanvasRenderer->Draw();

				pGraphicsDev->End();
			}

			pGraphicsDev->Present();
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////

		camHelper.Dispose();

		for( it_a = it_a_begin; it_a != it_a_end; ++it_a )
		{
			MIX_RELEASE( ( *it_a ) );
		}

#ifdef _DEBUG
		MIX_RELEASE( pDebDrawer );
#endif //_DEBUG

		MIX_RELEASE( pCamera );
		MIX_RELEASE( pSceneRenderer );
		MIX_RELEASE( pSceneEffectPackage );

		MIX_RELEASE( pFont );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pMouse );
		MIX_RELEASE( pKeyboard );

		Mix::Finalize();
	}

	return 0;
}
