#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/ViewCameraHelper.h"

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

#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 768

static const Mix::Vector2 TEXT_PADDING( 16.0f, 16.0f );

static const Int32 GRASS_PITCH = 24;
static const Int32 GRASS_DIV = 4;
static const Float32 GRASS_INTERVAL = 0.8f;

static const Int32 GRASS_DIV2 = GRASS_DIV * GRASS_DIV;
static const UInt32 GRASS_NUM = GRASS_PITCH * GRASS_PITCH;
static const UInt32 GRASS_PART_UNIT_NUM = GRASS_NUM / GRASS_DIV2;
static const Int32 GRASS_DIV_PITCH = GRASS_PITCH / GRASS_DIV;
static const Float32 GRASS_BASE_POS = static_cast<Float32>( GRASS_PITCH ) * GRASS_INTERVAL * -0.5f + ( GRASS_INTERVAL * 0.5f );

#ifdef _DEBUG
static const UInt32 DEBUG_DRAW_FLAGS = Mix::Scene::DDF_PLANTER_BOUNDS | Mix::Scene::DDF_SPOTLIGHT_SHAPE;
#endif //_DEBUG

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;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Scene\\Widget\\Planter" );
	engineConfig.pCaption = L"Scene - Widget ( Planter )";
	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::Graphics::Utility::IFont* pFont = NULL;

		Mix::Scene::IEffectPackage* pSceneEffectPackage = NULL;
		Mix::Scene::IRenderer* pSceneRenderer = NULL;
		Mix::Scene::IViewCamera* pCamera = NULL;
		Mix::Scene::ISpotLight* pSpotLight = NULL;

		Utility::ViewCameraHelper viewCamHelper;

		Mix::StringW tempStr;

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

		pIOMgr->MountDirectory( L"Data" );

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

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

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

		pGraphicsMgr->GetDevice( &pGraphicsDev );

		if( pGraphicsMgr->CreateCanvasRenderer( &pCanvasRenderer, L"Sample" ) == 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_WIDGET;

			if( pSceneMgr->CreateRenderer( pSceneEffectPackage, config, &pSceneRenderer, L"Sample" ) == True )
			{
				Mix::Scene::CAMERA_CONFIG camConfig;
				Mix::Scene::ITerrainModel* pLandModel = NULL;
				Mix::Scene::IMaterial* pMaterial;
				Mix::Scene::IDirectionalLight* pDirLight = NULL;

				/*
					背景色
				*/


				/*
					カメラ
				*/

				camConfig.caps = config.caps;
				camConfig.targetSize.Set( SCREEN_WIDTH, SCREEN_HEIGHT );
				camConfig.colorTexFormat = Mix::Graphics::FMT_R8G8B8A8;

				if( pSceneMgr->CreateViewCamera( camConfig, &pCamera ) == True )
				{
					pCamera->SetBackgroundColor( Mix::Vector4( 0.1f, 0.1f, 0.1f, 1.0f ) );
					pCamera->SetProjection( MIX_TO_RAD( 60.0f ), 0.01f, 100.0f );

					pSceneRenderer->AddCamera( pCamera );
				}

				/*
					モデル
				*/

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

					MIX_RELEASE( pLandModel );
				}

				/*
					プランター
				*/

				if( pSceneMgr->CreateMaterialFromFile( pSceneEffectPackage, L"Data\\Material\\Grass.mtl", &pMaterial ) == True )
				{
					Mix::Scene::IFlowerPackage::MODEL models[1];
					Mix::Scene::IFlowerPackage* pFlowerPackage = NULL;

					models[0].uv[0].Set( 0.0f, 0.0f );
					models[0].uv[1].Set( 1.0f, 1.0f );
					models[0].type = Mix::Scene::IFlowerPackage::M_CROSS2;

					if( pSceneMgr->CreateFlowerPackage( pMaterial, 1, models, &pFlowerPackage, MIX_DEBUGNAME ) == True )
					{
						Mix::Scene::IPlanter::UNIT units[GRASS_NUM];
						Mix::Vector3 basePos;
						Mix::Vector3 worldPos;
						Mix::Matrix4x4 wsMat;
						Mix::Matrix4x4 wtMat;
						UInt32 unitIndex;
						Mix::Scene::IPlanter* pPlanter;

						basePos.Set( 0.0f, -0.1f, 0.0f );

						unitIndex = 0;

						for( UInt32 bx = 0; bx < GRASS_DIV; bx++ )
						{
							basePos.x = GRASS_BASE_POS + GRASS_INTERVAL * static_cast<Float32>( bx * GRASS_DIV_PITCH );

							for( UInt32 bz = 0; bz < GRASS_DIV; bz++ )
							{
								basePos.z = GRASS_BASE_POS + GRASS_INTERVAL * static_cast<Float32>( bz * GRASS_DIV_PITCH );

								for( Int32 x = 0; x < GRASS_DIV_PITCH; x++ )
								{
									worldPos.x = basePos.x + GRASS_INTERVAL * static_cast<Float32>( x );
									worldPos.x += ( Mix::RandF() * 2.0f - 1.0f ) * 0.1f;

									for( Int32 z = 0; z < GRASS_DIV_PITCH; z++ )
									{
										worldPos.y = -0.1f - ( Mix::RandF() * 0.2f );
										worldPos.y = basePos.y;

										worldPos.z = basePos.z + GRASS_INTERVAL * static_cast<Float32>( z );
										worldPos.z += ( Mix::RandF() * 2.0f - 1.0f ) * 0.1f;

										wsMat.SetScaling( 1.0f, 1.0f + ( Mix::RandF() * 1.0f ), 1.0f );
										wtMat.SetTranslation( worldPos );

										units[unitIndex].worldMat = wsMat * wtMat;
										units[unitIndex].modelIndex = 0;

										unitIndex++;
									}
								}
							}
						}

						for( UInt32 i = 0; i < GRASS_DIV2; i++ )
						{
							if( pSceneMgr->CreatePlanter(	pFlowerPackage,
															GRASS_PART_UNIT_NUM, &( units[i * GRASS_PART_UNIT_NUM] ),
															Mix::Matrix4x4::Identity(),
															&pPlanter,
															MIX_DEBUGNAME_T( L"Grass" ) ) == True )
							{
								pPlanter->SetLocalLighting( True );
								pSceneRenderer->AddPlanter( pPlanter );
								MIX_RELEASE( pPlanter );
							}
						}

						MIX_RELEASE( pFlowerPackage );
					}

					MIX_RELEASE( pMaterial );
				}

				/*
					ウィジェットのライティングモード
				*/

				pSceneRenderer->SetWidgetLightingMode( Mix::Scene::IRenderer::WL_EVERYONE );

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

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

					pSceneRenderer->SetDirectionalLight( pDirLight );

					MIX_RELEASE( pDirLight );
				}

				/*
					スポットライト
				*/

				if( pSceneMgr->CreateSpotLight( &pSpotLight ) == True )
				{
					pSpotLight->SetEnabled( True );
					pSpotLight->SetCone( MIX_TO_RAD( 1.0f ), MIX_TO_RAD( 30.0f ) );
					pSpotLight->SetRange( 30.0f );
					pSpotLight->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );

					pSceneRenderer->AddSpotLight( pSpotLight );
				}

#ifdef _DEBUG
				Mix::Graphics::Utility::IPerspectiveRenderer* pPersRenderer = NULL;

				if( pGraphicsMgr->CreatePerspectiveRenderer( &pPersRenderer, MIX_DEBUGNAME ) == True )
				{
					pSceneRenderer->Debug_SetPerspectiveRenderer( pPersRenderer );
					MIX_RELEASE( pPersRenderer );
				}
#endif //_DEBUG
			}
		}

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

		viewCamHelper.Initialize(	pMouse,
									pKeyboard,
									Mix::Vector3( 0.0f, 0.5f, 0.0f ),
									Mix::Vector2( MIX_TO_RAD( 10.0f ), MIX_TO_RAD( 10.0f ) ),
									7.0f );

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

		pEngine->ResetFPS();

		while( pEngine->Update() == True )
		{
			Float32 dt = pEngine->GetDT();
			Mix::Scene::IRenderer::WIDGET_LIGHTING_MODE wlMode = pSceneRenderer->GetWidgetLightingMode();

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// 操作
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pKeyboard->GetKeyState( Mix::HID::KEY_L ) & Mix::HID::PRESSED )
			{
				Mix::Scene::IRenderer::WIDGET_LIGHTING_MODE wlMode = pSceneRenderer->GetWidgetLightingMode();

				switch( wlMode )
				{
				case Mix::Scene::IRenderer::WL_DEFAULT:
					wlMode = Mix::Scene::IRenderer::WL_EVERYONE;
					break;
				case Mix::Scene::IRenderer::WL_EVERYONE:
					wlMode = Mix::Scene::IRenderer::WL_DETAILED;
					break;
				case Mix::Scene::IRenderer::WL_DETAILED:
					wlMode = Mix::Scene::IRenderer::WL_DEFAULT;
					break;
				}

				pSceneRenderer->SetWidgetLightingMode( wlMode );
			}

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

				if( MIX_TESTBIT( debDrawFlags, DEBUG_DRAW_FLAGS ) == 0 )
				{
					MIX_SETBIT( debDrawFlags, DEBUG_DRAW_FLAGS );
				}
				else
				{
					MIX_RESETBIT( debDrawFlags, DEBUG_DRAW_FLAGS );
				}

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

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// カメラ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			viewCamHelper.Update();

			if( pCamera != NULL )
			{
				pCamera->SetRotation( viewCamHelper.GetRotation() );
				pCamera->SetAt( viewCamHelper.GetAt() );
				pCamera->SetDistance( viewCamHelper.GetDistance() );
			}

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

			if( pCanvasRenderer != NULL )
			{
				Mix::Vector2 txtSize;

				pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );

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

				switch( wlMode )
				{
				case Mix::Scene::IRenderer::WL_DEFAULT:
					pCanvasRenderer->AddString( TEXT_PADDING.x, TEXT_PADDING.y + 32.0f, L"LIGHTING_MODE[DEFAULT]" );
					break;
				case Mix::Scene::IRenderer::WL_EVERYONE:
					pCanvasRenderer->AddString( TEXT_PADDING.x, TEXT_PADDING.y + 32.0f, L"LIGHTING_MODE[EVERYONE]" );
					break;
				case Mix::Scene::IRenderer::WL_DETAILED:
					pCanvasRenderer->AddString( TEXT_PADDING.x, TEXT_PADDING.y + 32.0f, L"LIGHTING_MODE[DETAILED]" );
					break;
				}

#ifdef _DEBUG
				const Mix::Point& screenSize = pCamera->GetConfig().targetSize;

				Mix::Scene::DEBUG_PROFILE_CAMERA_WIDGET debProfW;
				Mix::Scene::DEBUG_PROFILE_CAMERA_DRAW_TRANSPARENCY debProfT;

				pCamera->Debug_GetProfile( Mix::Scene::DPT_WIDGET, &debProfW, sizeof( debProfW ) );
				pCamera->Debug_GetProfile( Mix::Scene::DPT_DRAW_TRANSPARENCY, &debProfT, sizeof( debProfT ) );

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

				tempStr.Sprintf( L"VISIBLE[%d/%d] SUBSET[%d] QUAD[%d] OBJECT[%d] COMMAND[%d]",
					debProfW.pl.vCount, debProfW.pl.tCount,
					debProfW.subsetCount,
					debProfW.quadCount,
					debProfW.objCount,
					debProfW.comCount );

				pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
				pCanvasRenderer->AddString( TEXT_PADDING.x, TEXT_PADDING.y + 64.0f, tempStr.GetConstPtr() );

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

				tempStr.Sprintf(	L"W_SORT_OBJ     %f sec\n"
									L"W_ASSIGN_LIGHT %f sec\n"
									L"W_GEN_COM      %f sec\n"
									L"T_SORT_OBJ     %f sec\n"
									L"T_GEN_COM      %f sec\n"
									L"T_DRAW         %f sec\n"
									L"\n"
									L"W:WIDGET T:TRANSPARENCY",
					debProfW.slElapsedTime,
					debProfW.alElapsedTime,
					debProfW.goElapsedTime,
					debProfT.soElapsedTime,
					debProfT.gcElapsedTime,
					debProfT.frElapsedTime );

				pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
				pCanvasRenderer->AddString( TEXT_PADDING.x, TEXT_PADDING.y + 96.0f, tempStr.GetConstPtr() );

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

				Utility::BAR_ELEMENT barElems[6];
				UInt32 numBarElem = sizeof( barElems ) / sizeof( Utility::BAR_ELEMENT );
				Mix::Rectangle barRect( 0, screenSize.y - 16, screenSize.x, 16 );

				barElems[0].value = debProfW.slElapsedTime;
				barElems[1].value = debProfW.alElapsedTime;
				barElems[2].value = debProfW.goElapsedTime;
				barElems[3].value = debProfT.soElapsedTime;
				barElems[4].value = debProfT.gcElapsedTime;
				barElems[5].value = debProfT.frElapsedTime;

				Utility::FillBarColor( barElems, numBarElem );
				Utility::DrawBar( pCanvasRenderer, barRect, barElems, numBarElem, dt );

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

				tempStr.Sprintf( L"%s", L"Controls :\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"  Toggle lighting mode : L key\n"
										L"  Toggle debug draw    : D key" );

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

				pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
				pCanvasRenderer->AddString( TEXT_PADDING.x, static_cast<Float32>( SCREEN_HEIGHT ) - txtSize.y - TEXT_PADDING.y * 2.0f, tempStr.GetConstPtr() );
#else //_DEBUG
				tempStr.Sprintf( L"%s", L"Controls :\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"  Toggle lighting mode : L key" );

				txtSize = Mix::Graphics::Utility::MeasureString( pFont, tempStr.GetConstPtr() );
				pCanvasRenderer->AddString( TEXT_PADDING.x, static_cast<Float32>( SCREEN_HEIGHT ) - txtSize.y - TEXT_PADDING.y, tempStr.GetConstPtr() );
#endif //_DEBUG

				pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
				Utility::DrawMouseCursor( pCanvasRenderer, pMouse->GetPos() );
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// 更新
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pCamera != NULL )
			{
				pCamera->Update();

				if( pSpotLight != NULL )
				{
					pSpotLight->SetDirection( pCamera->GetViewForward() );
					pSpotLight->SetPosition( pCamera->GetEye() );
					pSpotLight->Refresh();
				}
			}

			if( pSceneRenderer != NULL )
			{
				pSceneRenderer->Update( dt, pEngine->GetBaseDT() );
			}

			if( pCanvasRenderer != NULL )
			{
				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();
		}

		viewCamHelper.Dispose();

		MIX_RELEASE( pCamera );
		MIX_RELEASE( pSpotLight );
		MIX_RELEASE( pSceneRenderer );
		MIX_RELEASE( pSceneEffectPackage );
		MIX_RELEASE( pFont );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pKeyboard );
		MIX_RELEASE( pMouse );

		Mix::Finalize();
	}

	return 0;
}
