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

#include "stdafx.h"

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

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

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

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

static const Mix::Vector3 CAM_INIT_AT( 0.0f, 1.0f, 0.0f );
static const Mix::Vector2 CAM_INIT_ANGLE( MIX_TO_RAD( 20.0f ), MIX_TO_RAD( -20.0f ) );
static const Float32 CAM_INIT_DIST = 5.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;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Dynamics\\Joint\\Slider" );
	engineConfig.pCaption = L"Dynamics - SliderJoint";
	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::Dynamics::IWorld* pDynamicsWorld = NULL;
		Mix::Dynamics::IObjectDragger* pDynamicsObjDrg = NULL;

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

		Utility::ViewCameraHelper viewCamHelper;
		Mix::Matrix4x4 viewMat;
		Mix::Matrix4x4 projMat;

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

		pIOMgr->MountDirectory( L"Data" );

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

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

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

		if( pDynamicsMgr->CreateWorld( Mix::Dynamics::WORLD_CONFIG(), &pDynamicsWorld, MIX_DEBUGNAME ) == True )
		{
			Mix::Dynamics::IStaticPlane* pStaticPlane = NULL;
			Mix::Dynamics::IBoxShape* pBoxShape = NULL;
			Mix::Dynamics::IRigidBody* pBaseRigidBody = NULL;
			Mix::Dynamics::IRigidBody* pDoorRigidBody = NULL;
			Mix::Dynamics::ISliderJoint* pJoint = NULL;

			/*
				StaticPlane
			*/

			if( pDynamicsMgr->CreateStaticPlane( Mix::Vector3( 0.0f, 1.0f, 0.0f ), 0.0f, &pStaticPlane, MIX_DEBUGNAME ) == True )
			{
				pDynamicsWorld->AddObject( pStaticPlane );
				MIX_RELEASE( pStaticPlane );
			}

			/*
				RigidBody
			*/

			static const Float32 JOINT_MARGIN = 0.02f;

			static const Mix::Vector3 BASE_HALF_EXTENTS( 2.0f, 0.1f, 0.05f );
			static const Mix::Vector3 DOOR_HALF_EXTENTS( 0.5f, 1.0f, 0.05f );

			static const Mix::Vector3 BASE_INIT_POS( 0.0f, DOOR_HALF_EXTENTS.y * 2.0f + BASE_HALF_EXTENTS.y + JOINT_MARGIN, 0.0f );
			static const Mix::Vector3 DOOR_INIT_POS( 0.0f, BASE_INIT_POS.y - BASE_HALF_EXTENTS.y - DOOR_HALF_EXTENTS.y - JOINT_MARGIN, 0.0f );

			// Base
			if( pDynamicsMgr->CreateBoxShape( BASE_HALF_EXTENTS, &pBoxShape, MIX_DEBUGNAME ) == True )
			{
				if( pDynamicsMgr->CreateRigidBody( 0.0f, pBoxShape, &pBaseRigidBody, MIX_DEBUGNAME ) == True )
				{
					pBaseRigidBody->SetWorldPosition( BASE_INIT_POS );
					pDynamicsWorld->AddObject( pBaseRigidBody );
				}

				MIX_RELEASE( pBoxShape );
			}

			// Door
			if( pDynamicsMgr->CreateBoxShape( DOOR_HALF_EXTENTS, &pBoxShape, MIX_DEBUGNAME ) == True )
			{
				if( pDynamicsMgr->CreateRigidBody( 1.0f, pBoxShape, &pDoorRigidBody, MIX_DEBUGNAME ) == True )
				{
					pDoorRigidBody->SetWorldPosition( DOOR_INIT_POS );
					pDynamicsWorld->AddObject( pDoorRigidBody );
				}

				MIX_RELEASE( pBoxShape );
			}

			// Joint
			if( pDynamicsMgr->CreateSliderJoint(	pDoorRigidBody,
													pBaseRigidBody,
													Mix::Quaternion::Identity(),
													Mix::Quaternion::Identity(),
													Mix::Vector3( 0.0f, DOOR_HALF_EXTENTS.y + JOINT_MARGIN, 0.0f ),
													Mix::Vector3( 0.0f, -BASE_HALF_EXTENTS.y, 0.0f ),
													&pJoint,
													MIX_DEBUGNAME ) == True )
			{
				Float32 linearLimit = BASE_HALF_EXTENTS.x - DOOR_HALF_EXTENTS.x;

				pJoint->SetLinearLimit( -linearLimit, +linearLimit );
				pJoint->SetLinearLimit_OrthoSpring( 0.01f );
				pJoint->SetLinearLimit_OrthoDamper( 0.3f );
				pJoint->SetLinearLimit_LimSpring( 0.5f );
				pJoint->SetLinearLimit_LimDamper( 0.3f );

				pDynamicsWorld->AddJoint( pJoint );
				MIX_RELEASE( pJoint );
			}

			MIX_RELEASE( pDoorRigidBody );
			MIX_RELEASE( pBaseRigidBody );

			/*
				ObjectDragger
			*/

			pDynamicsMgr->CreateObjectDragger( pDynamicsWorld, &pDynamicsObjDrg, MIX_DEBUGNAME );
		}

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

		pGraphicsMgr->GetDevice( &pGraphicsDev );

		pGraphicsMgr->CreatePerspectiveRenderer( &pPersRenderer, MIX_DEBUGNAME );

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

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

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

		viewCamHelper.Initialize( pMouse, pKeyboard, CAM_INIT_AT, CAM_INIT_ANGLE, CAM_INIT_DIST );

		Mix::Matrix4x4::PerspectiveFovLH( MIX_TO_RAD( 60.0f ), camAspect, 0.1f, 1000.0f, projMat );

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

		pEngine->ResetFPS();

		while( pEngine->Update() == True )
		{
			////////////////////////////////////////////////////////////////////////////////////////////////////

			pDynamicsWorld->Update( pEngine->GetDT() );

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

			viewCamHelper.Update();

			Mix::Matrix4x4::LookAtLH( viewCamHelper.GetRotation(), viewCamHelper.GetAt(), viewCamHelper.GetDistance(), viewMat );

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

			Mix::Vector3 eyePos = viewCamHelper.GetAt() + Mix::Matrix4x4( viewCamHelper.GetRotation() ) * Mix::Vector3( 0.0f, 0.0f, viewCamHelper.GetDistance() );
			Mix::Vector2 mousePos = pMouse->GetPos();
			UInt32 mlbState = pMouse->GetButtonState( 0 );

			if( MIX_TESTBIT( mlbState, Mix::HID::PRESSED ) == Mix::HID::PRESSED )
			{
				Mix::Vector2 screenSize = pGraphicsDev->GetScreenSize();
				Mix::Vector3 rayFrom = Mix::Unproject( viewMat, projMat, Mix::Vector3( mousePos.x, mousePos.y, 0.0f ), screenSize );
				Mix::Vector3 rayTo = Mix::Unproject( viewMat, projMat, Mix::Vector3( mousePos.x, mousePos.y, 1.0f ), screenSize );

				pDynamicsObjDrg->CatchStart( eyePos, rayFrom, rayTo );
			}
			else if( MIX_TESTBIT( mlbState, Mix::HID::DOWN ) == Mix::HID::DOWN )
			{
				if( pDynamicsObjDrg->IsCatch() == True )
				{
					Mix::Vector2 screenSize = pGraphicsDev->GetScreenSize();
					Mix::Vector3 rayTo = Mix::Unproject( viewMat, projMat, Mix::Vector3( mousePos.x, mousePos.y, 1.0f ), screenSize );

					pDynamicsObjDrg->CatchContinue( eyePos, rayTo );
				}
			}
			else if( MIX_TESTBIT( mlbState, Mix::HID::RELEASED ) == Mix::HID::RELEASED )
			{
				if( pDynamicsObjDrg->IsCatch() == True )
				{
					pDynamicsObjDrg->Free();
				}
			}

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

			pDynamicsWorld->Debug_Draw( pPersRenderer );

			pPersRenderer->Update();

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

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

			/*
				Mouse Cursor
			*/

			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() );

			/*
				Desription
			*/

			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"  Drag object    : Mouse left button" );

			txtSize = Mix::Graphics::Utility::MeasureString( pFont, tempStr.GetConstPtr() );
			txtPos.x = 16.0f;
			txtPos.y = static_cast<Float32>( SCREEN_HEIGHT ) - txtSize.y - 16.0f;

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

			/*
				Update
			*/

			pCanvasRenderer->Update();

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

			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 )
			{
				pPersRenderer->Draw( viewMat * projMat );
				pCanvasRenderer->Draw();

				pGraphicsDev->End();
			}

			pGraphicsDev->Present();
		}

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

		viewCamHelper.Dispose();

		MIX_RELEASE( pFont );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pPersRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pDynamicsObjDrg );
		MIX_RELEASE( pDynamicsWorld );
		MIX_RELEASE( pMouse );
		MIX_RELEASE( pKeyboard );

		Mix::Finalize();
	}

	return 0;
}
