#include "stdafx.h"

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

#include "Utility/Common.h"
#include "Utility/UniversalCameraHelper.h"
#include "Utility/UI/Manager.h"

#include "hud.h"

#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 768

struct POINTLIGHT_INFO
{
	Mix::Vector3 pos;
	Float32 minRadius;
	Float32 maxRadius;

	POINTLIGHT_INFO( const Mix::Vector3& _pos, Float32 _minRadius, Float32 _maxRadius )
	{
		pos = _pos;
		minRadius = _minRadius;
		maxRadius = _maxRadius;
	}
};

static const POINTLIGHT_INFO POINTLIGHT_INFO_TABLE[] =
{
	POINTLIGHT_INFO( Mix::Vector3(  -4.0f,   -5.5f,  8.5f ), 0.5f, 4.0f ),
	POINTLIGHT_INFO( Mix::Vector3(  -4.5f,  -11.3f, -3.5f ), 0.5f, 6.0f ),
	POINTLIGHT_INFO( Mix::Vector3(  -4.5f, -20.85f, 15.7f ), 0.5f, 6.0f ),
	POINTLIGHT_INFO( Mix::Vector3( -34.0f,  -28.5f, 15.5f ), 0.5f, 8.0f ),
};

static const UInt32 POINTLIGHT_INFO_COUNT = sizeof( POINTLIGHT_INFO_TABLE ) / sizeof( POINTLIGHT_INFO );

int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow )
{
	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;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Scene\\PostEffect\\HDRToneMapping" );
	engineConfig.pCaption = L"Scene - PostEffect ( HDR ToneMapping )";
	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::Graphics::IManager* pGraphicsMgr = Mix::Graphics::GetManagerPtr();
		Mix::Scene::IManager* pSceneMgr = Mix::Scene::GetManagerPtr();

		Mix::HID::IMouse* pMouse = NULL;
		Mix::HID::IKeyboard* pKeyboard = NULL;

		Mix::Graphics::IDevice* pGraphicsDev = NULL;
		Mix::Graphics::Utility::ICanvasRenderer* pCanvasRenderer = NULL;

		Mix::Scene::IEffectPackage* pSceneEffectPackage = NULL;
		Mix::Scene::IRenderer* pSceneRenderer = NULL;
		Mix::Scene::IUniversalCamera* pCamera = NULL;
		Mix::Scene::IGhost* pKinematicGhost = NULL;
		Mix::Scene::IKinematicCharacter* pKinematicCharacter = NULL;

		Mix::StringW tempStr;

		Utility::UniversalCameraHelper uvCamHelper;
		Utility::UI::Manager uiManager;

		HUD hud;

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

		pIOMgr->MountDirectory( L"Data" );

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

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

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

		pGraphicsMgr->GetDevice( &pGraphicsDev );

		if( pGraphicsMgr->CreateCanvasRenderer( &pCanvasRenderer, L"Sample" ) == True )
		{
			Mix::Graphics::Utility::IFont* pFont_16_400 = NULL;

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

			MIX_RELEASE( pFont_16_400 );
		}

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

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

			config.caps = Mix::Scene::RCAP_DYNAMICS | Mix::Scene::RCAP_LUMINOSITY | Mix::Scene::RCAP_BLOOM | Mix::Scene::RCAP_DEPTH_OF_FIELD;

			if( pSceneMgr->CreateRenderer( pSceneEffectPackage, config, &pSceneRenderer, L"Sample" ) == True )
			{
				Mix::Scene::CAMERA_CONFIG camConfig;
				Mix::Graphics::ITexture* pTempTex = NULL;
				Mix::Scene::IDirectionalLight* pDirLight = NULL;
				Mix::Scene::ITerrainModel* pBasementModel = NULL;
				Mix::Scene::ICamera::LUMINOSITY_SETTINGS lumSettings;

				camConfig.caps = Mix::Scene::RCAP_LUMINOSITY | Mix::Scene::RCAP_BLOOM | Mix::Scene::RCAP_DEPTH_OF_FIELD;
				camConfig.targetSize.Set( SCREEN_WIDTH, SCREEN_HEIGHT );
				camConfig.colorTexFormat = Mix::Graphics::FMT_R16G16B16A16F;
				camConfig.bloomOverflowNum = 2;

				pSceneRenderer->SetGlobalAmbientColor( Mix::Vector4( 0.0f, 0.0f, 0.0f, 1.0f ) );

				if( pSceneMgr->CreateUniversalCamera( camConfig, &pCamera ) == True )
				{
					Mix::Scene::ICamera::BLOOM_SETTINGS bloomSettings = pCamera->GetBloomSettings();
					Mix::Scene::ICamera::LUMINOSITY_SETTINGS lumSettings = pCamera->GetLuminositySettings();

					pCamera->SetBackgroundColor( Mix::Vector4( 0.2f, 0.2f, 0.2f, 1.0f ) );
					pCamera->SetProjection( MIX_TO_RAD( 80.0f ), 0.1f, 200.0f );

					pCamera->SetAmbientOcclusionEnabled( True );
					if( pGraphicsDev->CreateTextureFromFile( L"Data\\Texture\\noise.png", &pTempTex ) == True )
					{
						pCamera->SetAmbientOcclusionTexture( pTempTex );
						MIX_RELEASE( pTempTex );
					}

					pCamera->SetShadowMappingEnabled( True );

					bloomSettings.brightPass.white = 1.0f;
					bloomSettings.brightPass.offset = 0.8f;
					bloomSettings.brightPass.threshold = 0.5f;
					bloomSettings.intensity = 1.1f;
					pCamera->SetBloomEnabled( True );
					pCamera->SetBloomSettings( bloomSettings );

					lumSettings.flags = Mix::Scene::ICamera::LUM_TONE_MAPPING;
					pCamera->SetLuminositySettings( lumSettings );

					pCamera->SetFilmicType( Mix::Scene::ICamera::FI_HABLE );

					pCamera->SetDofEnabled( True );

					pSceneRenderer->AddCamera( pCamera );
				}

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

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

					pSceneRenderer->SetDirectionalLight( pDirLight );

					MIX_RELEASE( pDirLight );
				}

				if( pSceneMgr->CreateTerrainModelFromFile( pSceneEffectPackage, L"Data\\Model\\basement.mtm", &pBasementModel ) == True )
				{
					pSceneRenderer->AddTerrainModel( pBasementModel );

					MIX_RELEASE( pBasementModel );
				}

				for( UInt32 i = 0; i < POINTLIGHT_INFO_COUNT; i++ )
				{
					const POINTLIGHT_INFO& info = POINTLIGHT_INFO_TABLE[i];
					Mix::Scene::IPointLight* pPointLight = NULL;

					if( pSceneMgr->CreatePointLight( &pPointLight ) == True )
					{
						pPointLight->SetEnabled( True );
						pPointLight->SetPosition( info.pos );
						pPointLight->SetRadius( info.minRadius, info.maxRadius );
						pPointLight->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );

						pSceneRenderer->AddPointLight( pPointLight );

						MIX_RELEASE( pPointLight );
					}
				}

				if( pSceneMgr->CreateKinematicGhost( 1.4f, 0.3f, 0.1f, &pKinematicGhost, MIX_DEBUGNAME ) == True )
				{
					pKinematicGhost->SetWorldPosition( Mix::Vector3( 0.0f, 10.0f, 0.0f ) );
					Mix::Scene::IDynamicsObject* pDynamicsObject = NULL;

					if( pKinematicGhost->GetDynamicsObject( &pDynamicsObject ) == True )
					{
						pKinematicCharacter = static_cast<Mix::Scene::IKinematicCharacter*>( pDynamicsObject );
					}

					pSceneRenderer->AddGhost( pKinematicGhost );
				}

				lumSettings = pCamera->GetLuminositySettings();

				lumSettings.flags = Mix::Scene::ICamera::LUM_TONE_MAPPING;
				lumSettings.rods = 0.6f;
				lumSettings.white = 0.4f;

				pCamera->SetLuminositySettings( lumSettings );
				pCamera->ForceLuminosityAdaptation();

				pCamera->SetDofEnabled( True );
				pSceneRenderer->SetFogEnabled( True );
			}
		}

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

		uvCamHelper.Initialize( pKeyboard, pMouse, Mix::Vector3( 0.0f, 1.7f, 0.0f ), Mix::Vector3( 0.0f, 0.0f, 1.0f ), 0.05f );

		if( ( pMouse != NULL ) &&
			( pCanvasRenderer != NULL ) )
		{
			Mix::Graphics::Utility::IFont* pFont_12_200 = NULL;

			if( pGraphicsMgr->CreateFontFromFile( L"Data\\Font\\mspg_12_200.fnt", &pFont_12_200 ) == True )
			{
				uiManager.Initialize( pMouse, pCanvasRenderer, pFont_12_200 );

				hud.Initialize( pCamera );
				hud.Open( &uiManager );

				MIX_RELEASE( pFont_12_200 );
			}
		}

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

		pEngine->ResetFPS();

		while( pEngine->Update() == True )
		{
			////////////////////////////////////////////////////////////////////////////////////////////////////
			// カメラ : 更新
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pCamera != NULL )
			{
				uvCamHelper.Update();

				if( pKinematicCharacter != NULL )
				{
					if( pKinematicCharacter->OnGround() == True )
					{
						pKinematicCharacter->SetLinearVelocity( uvCamHelper.GetVelocity() * Mix::Vector3( 1.0f, 0.0f, 1.0f ) );
					}

					if( pKeyboard->GetKeyState( Mix::HID::KEY_SPACE ) & Mix::HID::PRESSED )
					{
						pKinematicCharacter->Jump();
					}
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// シーンレンダラー : 更新
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pSceneRenderer != NULL )
			{
				pSceneRenderer->Update( pEngine->GetDT() );
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// カメラ : リフレッシュ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pKinematicCharacter != NULL )
			{
				uvCamHelper.Refresh( pKinematicGhost->GetWorldPosition() + Mix::Vector3( 0.0f, 1.4f, 0.0f ) );
			}

			if( pCamera != NULL )
			{
				pCamera->SetView( uvCamHelper.GetEye(), uvCamHelper.GetAt(), uvCamHelper.GetUp() );
				pCamera->Update();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// キャンバス
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pCanvasRenderer != NULL )
			{
				tempStr.Sprintf(	L"FPS  [%f]\n"
									L"STPS [%f]\n"
									L"IF   [%d/%d]",	pEngine->GetFPS(),
														pEngine->GetSleepTimePerSec(),
														pEngine->GetInsomniaFrameCountPerSec(), pEngine->GetInsomniaFrameMax() );

				pCanvasRenderer->SetColor(		Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
				pCanvasRenderer->AddString(	16.0f, 16.0f, tempStr.GetConstPtr() );
				pCanvasRenderer->AddString(	16.0f, pGraphicsDev->GetScreenSize().y - 80.0f,
									L"Controls :\n"
									L"  Rotate camera  : Mouse middle button\n"
									L"  Move camera    : W S A D key\n" );

#ifdef _DEBUG
				if( pSceneRenderer != NULL )
				{
					const Int32 CHAR_HEIGHT = 16;
					const Int32 IMAGE_SIZE = 112;
					const Int32 C_TO_I_MAGIN = 4;
					const Int32 I_TO_C_MAGIN = 16;
					const Int32 Y_STEP = CHAR_HEIGHT + IMAGE_SIZE + C_TO_I_MAGIN + I_TO_C_MAGIN;

					Mix::Graphics::ITexture* pTex = NULL;

					Mix::Point strPos( 20, 96 );
					Mix::Rectangle texRect( 16, strPos.y + CHAR_HEIGHT + C_TO_I_MAGIN, IMAGE_SIZE, IMAGE_SIZE );
					Mix::Vector4 textColor( 1.0f, 1.0f, 1.0f, 1.0f );
					Mix::Vector4 bgColor( 1.0f, 1.0f, 1.0f, 1.0f );
					Mix::Vector4 frameColor( 0.2f, 0.2f, 0.2f, 1.0f );

					////////////////////////////////////////////////////////////////////////////////////////////////////
					// Luminance
					////////////////////////////////////////////////////////////////////////////////////////////////////

					pCanvasRenderer->SetColor( textColor );
					pCanvasRenderer->AddString( strPos, L"Luminance" );

					if( pCamera->Debug_GetImage( Mix::Scene::DIT_LUMINOSITY, 0, &pTex ) == True )
					{
						Mix::Rectangle srcRect( 0, 0, pTex->GetWidth(), pTex->GetHeight() );
						Mix::Rectangle tileRect( 0, 0, 56, 56 );

						pCanvasRenderer->SetColor( bgColor );

						tileRect.x = texRect.x;
						tileRect.y = texRect.y;
						pCanvasRenderer->AddImage( pTex, tileRect, srcRect );

						tileRect.x = texRect.x + tileRect.width;
						tileRect.y = texRect.y + tileRect.height;
						pCanvasRenderer->AddImage( pTex, tileRect, srcRect );

						MIX_RELEASE( pTex );
					}

					if( pCamera->Debug_GetImage( Mix::Scene::DIT_LUMINOSITY, 1, &pTex ) == True )
					{
						Mix::Rectangle srcRect( 0, 0, pTex->GetWidth(), pTex->GetHeight() );
						Mix::Rectangle tileRect( 0, 0, 56, 56 );

						pCanvasRenderer->SetColor( bgColor );

						tileRect.x = texRect.x;
						tileRect.y = texRect.y + tileRect.height;
						pCanvasRenderer->AddImage( pTex, tileRect, srcRect );

						tileRect.x = texRect.x + tileRect.width;
						tileRect.y = texRect.y;
						pCanvasRenderer->AddImage( pTex, tileRect, srcRect );

						MIX_RELEASE( pTex );
					}

					pCanvasRenderer->SetColor( frameColor );
					pCanvasRenderer->AddRectangle( texRect );

					strPos.y += Y_STEP;
					texRect.y += Y_STEP;

					////////////////////////////////////////////////////////////////////////////////////////////////////
					// BrightPass
					////////////////////////////////////////////////////////////////////////////////////////////////////

					if( pCamera->Debug_GetImage( Mix::Scene::DIT_BLOOM, 0, &pTex ) == True )
					{
						pCanvasRenderer->SetColor( textColor );
						pCanvasRenderer->AddString( strPos, L"Birght Pass" );

						pCanvasRenderer->SetColor( bgColor );
						pCanvasRenderer->AddImage( pTex, texRect, Mix::Rectangle( 0, 0, pTex->GetWidth(), pTex->GetHeight() ) );

						pCanvasRenderer->SetColor( frameColor );
						pCanvasRenderer->AddRectangle( texRect );

						MIX_RELEASE( pTex );

						strPos.y += Y_STEP;
						texRect.y += Y_STEP;
					}

					////////////////////////////////////////////////////////////////////////////////////////////////////
					// Bloom
					////////////////////////////////////////////////////////////////////////////////////////////////////

					UInt32 bloomTexCount = pCamera->Debug_GetImageNum( Mix::Scene::DIT_BLOOM );

					if( bloomTexCount > 0 )
					{
						pCanvasRenderer->SetColor( textColor );
						pCanvasRenderer->AddString( strPos, L"Bloom" );

						for( UInt32 i = 0; i < bloomTexCount; i++ )
						{
							if( pCamera->Debug_GetImage( Mix::Scene::DIT_BLOOM, i + 1, &pTex ) == True )
							{
								pCanvasRenderer->SetColor( bgColor );
								pCanvasRenderer->AddImage( pTex, texRect, Mix::Rectangle( 0, 0, pTex->GetWidth(), pTex->GetHeight() ) );

								pCanvasRenderer->SetColor( frameColor );
								pCanvasRenderer->AddRectangle( texRect );

								MIX_RELEASE( pTex );

								strPos.y += IMAGE_SIZE - 1;
								texRect.y += IMAGE_SIZE - 1;
							}
						}
					}
				}
#endif //_DEBUG

				uiManager.Update();

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

				pCanvasRenderer->Update();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// リフレッシュ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pSceneRenderer != NULL )
			{
				pSceneRenderer->Refresh();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// 描画
			////////////////////////////////////////////////////////////////////////////////////////////////////

			pGraphicsDev->SetViewBounds( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );

			if( pGraphicsDev->Begin() == True )
			{
				if( pCamera != NULL )
				{
					pCamera->Draw();
				}

				if( pCanvasRenderer != NULL )
				{
					pCanvasRenderer->Draw();
				}

				pGraphicsDev->End();
			}

			pGraphicsDev->Present();
		}

		uiManager.Dispose();
		uvCamHelper.Dispose();

		MIX_RELEASE( pKinematicCharacter );
		MIX_RELEASE( pKinematicGhost );
		MIX_RELEASE( pSceneRenderer );
		MIX_RELEASE( pSceneEffectPackage );
		MIX_RELEASE( pCamera );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pKeyboard );
		MIX_RELEASE( pMouse );
	}

	Mix::Finalize();

	return 0;
}
