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

#include "stdafx.h"

#include <list>

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

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

#include "room.h"
#include "hud.h"

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

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

#define TP_WIDTH ( 128.0f )
#define TP_HEIGHT ( 16.0f )
#define TP_OFFSET ( 4.0f )

#define TP_IMPULSE ( 100.0f )

////////////////////////////////////////////////////////////////////////////////////////////////////
// 関数
////////////////////////////////////////////////////////////////////////////////////////////////////

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_DYNAMICS | Mix::EC_PARALLEL;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Dynamics\\Basic" );
	engineConfig.pCaption = L"Dynamics - Basic";
	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::Dynamics::IManager* pDynamicsMgr = Mix::Dynamics::GetManagerPtr();
		Mix::Graphics::IManager* pGraphicsMgr = Mix::Graphics::GetManagerPtr();

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

		Mix::Dynamics::IWorld* pWorld = NULL;
		Mix::Dynamics::IRigidBody* pCameraRigidBody = NULL;
		Mix::Dynamics::WORLD_CONFIG worldConfig;

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

		Mix::Matrix4x4 viewMat;
		Mix::Matrix4x4 projMat;
		Utility::UniversalCameraHelper uvCamHelper;

		Utility::UI::Manager uiManager;
		HUD hud;

		Room room;
		Float32 throwPower = 0.0f;
		Mix::StringW tempStr;

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

		pIOMgr->MountDirectory( L"Data" );

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

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

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

		if( pDynamicsMgr->CreateWorld( worldConfig, &pWorld, L"Sample" ) == True )
		{
			Mix::Dynamics::IStaticPlane* pStaticPlane = NULL;
			Mix::Dynamics::IBoxShape* pBoxShape = NULL;
			Mix::Dynamics::ISphereShape* pSphereShape = NULL;

			if( pDynamicsMgr->CreateStaticPlane( Mix::Vector3( 0.0f, 1.0f, 0.0f ), 0.0f, &pStaticPlane, L"Sample" ) == True )
			{
				pStaticPlane->SetFilterGroup( Room::GROUP_FLOOR );
				pStaticPlane->SetFilterMask( Room::GROUP_FLOOR_MASK );

				pWorld->AddObject( pStaticPlane );

				MIX_RELEASE( pStaticPlane );
			}

			if( pDynamicsMgr->CreateSphereShape( 1.0f, &pSphereShape ) == True )
			{
				if( pDynamicsMgr->CreateRigidBody( 1.0f, pSphereShape, &pCameraRigidBody ) == True )
				{
					pCameraRigidBody->SetFilterGroup( Room::GROUP_CAMERA );
					pCameraRigidBody->SetFilterMask( Room::GROUP_CAMERA_MASK );
					pCameraRigidBody->SetStatus( Mix::Dynamics::IRigidBody::KINEMATIC );

					pWorld->AddObject( pCameraRigidBody );
				}
			}

			MIX_RELEASE( pSphereShape );
			MIX_RELEASE( pBoxShape );
		}

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

		pGraphicsMgr->GetDevice( &pGraphicsDev );

		pGraphicsMgr->CreatePerspectiveRenderer( &pPersRenderer, L"Sample" );

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

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

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

				hud.Initialize( &room, pGraphicsDev->GetScreenSize() );
				hud.Open( &uiManager );
			}

			MIX_RELEASE( pFont_12_200 );
			MIX_RELEASE( pFont_16_400 );
		}

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

		Float32 camAspect = static_cast<Float32>( SCREEN_WIDTH ) / static_cast<Float32>( SCREEN_HEIGHT );

		Mix::Matrix4x4::PerspectiveFovLH( MIX_TO_RAD( 60.0f ), camAspect, 0.1f, 1000.0f, projMat );
		uvCamHelper.Initialize( pKeyboard, pMouse, Mix::Vector3( 0.0f, 3.0f, -20.0f ), Mix::Vector3( 0.0f, 0.0f, 0.0f ) );

		room.Initialize( pDynamicsMgr, pWorld );
		hud.Build();

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

		pEngine->ResetFPS();

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

			uvCamHelper.Update();

			Mix::Matrix4x4::LookAtLH( uvCamHelper.GetEye(), uvCamHelper.GetAt(), uvCamHelper.GetUp(), viewMat );

			pCameraRigidBody->SetWorldPosition( uvCamHelper.GetEye() );

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ルーム : 更新
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( ( throwPower < 1.0f ) &&
				( MIX_TESTBIT( pMouse->GetButtonState( 1 ), Mix::HID::DOWN ) == Mix::HID::DOWN ) )
			{
				throwPower = min( 1.0f, throwPower + pEngine->GetDT() );
			}
			else if(	( throwPower >= 1.0f ) ||
						( MIX_TESTBIT( pMouse->GetButtonState( 1 ), Mix::HID::RELEASED ) == Mix::HID::RELEASED ) )
			{
				Mix::Vector2 mousePos = pMouse->GetPos();
				Mix::Vector3 pos;
				Mix::Dynamics::IRigidBody* pRigidBody;

				pos = Mix::Unproject( viewMat, projMat, Mix::Vector3( mousePos.x, mousePos.y, 0.0f ), pGraphicsDev->GetScreenSize() );
				pos += viewMat.GetColumn( 2 ) * 2.0f;

				pRigidBody = room.AddSphere( 1.0f, pos );
				if( pRigidBody != NULL )
				{
					Mix::Vector3 impulse;

					impulse = Mix::Unproject( viewMat, projMat, Mix::Vector3( mousePos.x, mousePos.y, 1.0f ), pGraphicsDev->GetScreenSize() );
					impulse = ( impulse - pos ).ToNormalize() * throwPower * TP_IMPULSE;

					pRigidBody->SetAngularDamping( 0.9f );
					pRigidBody->ApplyLinearImpulse( impulse );
				}

				throwPower = 0.0f;
			}

			if( MIX_TESTBIT( pMouse->GetButtonState( 0 ), Mix::HID::PRESSED ) == Mix::HID::PRESSED )
			{
				room.MousePressed( uvCamHelper.GetEye(), viewMat, projMat, pMouse->GetPos(), pGraphicsDev->GetScreenSize() );
			}
			else if( MIX_TESTBIT( pMouse->GetButtonState( 0 ), Mix::HID::DOWN ) == Mix::HID::DOWN )
			{
				room.MouseDown( uvCamHelper.GetEye(), viewMat, projMat, pMouse->GetPos(), pGraphicsDev->GetScreenSize() );
			}
			else if( MIX_TESTBIT( pMouse->GetButtonState( 0 ), Mix::HID::RELEASED ) == Mix::HID::RELEASED )
			{
				room.MouseReleased();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ワールドの更新
			////////////////////////////////////////////////////////////////////////////////////////////////////

			pWorld->Update( pEngine->GetDT() );
			pWorld->Debug_Draw( pPersRenderer );

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ルームのリフレッシュ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			room.Refresh();

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

			uvCamHelper.Refresh( pCameraRigidBody->GetWorldPosition() );
			Mix::Matrix4x4::LookAtLH( uvCamHelper.GetEye(), uvCamHelper.GetAt(), uvCamHelper.GetUp(), viewMat );

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ラインアートの更新
			////////////////////////////////////////////////////////////////////////////////////////////////////

			pPersRenderer->Update();

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// キャンバスの更新
			////////////////////////////////////////////////////////////////////////////////////////////////////

			tempStr.Sprintf(	L"FPS   [%f]\n"
								L"STPS  [%f]\n"
								L"IF    [%d/%d]\n"
								L"\n"
								L"Count [%d]",	pEngine->GetFPS(),
												pEngine->GetSleepTimePerSec(),
												pEngine->GetInsomniaFrameCountPerSec(), pEngine->GetInsomniaFrameMax(),
												room.GetObjectCount() );

			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 - 112.0f,
										L"Controls :\n"
										L"  Pick object    : Mouse left button\n"
										L"  Throw ball     : Mouse right button\n"
										L"  Rotate camera  : Mouse middle button\n"
										L"  Move camera    : W S A D key\n" );

			if( throwPower > 0.0f )
			{
				Mix::Vector2 pos = pMouse->GetPos();

				Mix::RectangleF bgRect( pos.x - TP_WIDTH - TP_OFFSET, pos.y - TP_HEIGHT - TP_OFFSET, TP_WIDTH, TP_HEIGHT );
				Mix::RectangleF barRect( pos.x - TP_WIDTH - TP_OFFSET, pos.y - TP_HEIGHT - TP_OFFSET, TP_WIDTH * throwPower, TP_HEIGHT );

				pCanvasRenderer->SetColor( Mix::Vector4( 0.2f, 0.2f, 0.2f, 0.5f ) );
				pCanvasRenderer->AddFillRectangle( bgRect );

				pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 0.0f, 0.0f, 0.5f ) );
				pCanvasRenderer->AddFillRectangle( barRect );

				pCanvasRenderer->SetColor( Mix::Vector4( 0.2f, 0.2f, 0.2f, 1.0f ) );
				pCanvasRenderer->AddRectangle( bgRect );
			}

			uiManager.Update();

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

			pCanvasRenderer->Update();

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

			pGraphicsDev->SetViewBounds( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
			pGraphicsDev->Clear( True, False );

			if( pGraphicsDev->Begin() == True )
			{
				pPersRenderer->Draw( viewMat * projMat );
				pCanvasRenderer->Draw();

				pGraphicsDev->End();
			}

			pGraphicsDev->Present();
		}

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

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

		MIX_RELEASE( pCameraRigidBody );
		MIX_RELEASE( pWorld );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pPersRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pKeyboard );
		MIX_RELEASE( pMouse );

		Mix::Finalize();
	}

	return 0;
}
