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

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

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

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

const static Float32 VC_WIDTH = 1.475f * 0.5f;
const static Float32 VC_HEIGHT = 1.78f * 0.5f;// * 0.5f;
const static Float32 VC_LENGTH = 3.395f * 0.5f;
const static Mix::Vector3 VW_AXIS( -1.0f, 0.0f, 0.0f );

const static Mix::Vector3 VW_DIR( 0.0f, -1.0f, 0.0f );
const static Float32 VW_OFFSET_Z = 1.0f;
const static Float32 VW_OFFSET_Y = -VC_HEIGHT;
const static Float32 VW_SUS_LEN = 0.3f;
const static Float32 VW_SUS_SPRING = 15.0f;
const static Float32 VW_TIRE_RADIUS = 0.35f;
const static Float32 VW_TIRE_FRICTION = 9.0f;

const static Mix::Vector3 VC_INIT_POS( 0.0f, 1.8f, 0.0f );
const static Mix::Vector3 VC_TARGET_OFFSET( 0.0f, 1.0f, 0.0f );

const static Float32 VW_STEERING_POWER = 0.01f;
const static Float32 VW_STEERING_LIMIT = 0.4f;
const static Float32 VW_ENGINE_FORCE_F = +50.0f;
const static Float32 VW_ENGINE_FORCE_B = -30.5f;
const static Float32 VW_ENGINE_BREAKING_FORCE = 0.1f;
const static Float32 VW_FOOT_BREAKING_FORCE = 0.3f;

static const Float32 CAM_DIST = 8.0f;
static const Float32 CAM_ELEV = MIX_TO_RAD( 20.0f );
static const Float32 CAM_ELEV_COIFFE = MIX_TO_RAD( 1.0f );
static const Float32 CAM_ELEV_MIN = MIX_TO_RAD( 1.0f );
static const Float32 CAM_ELEV_MAX = MIX_TO_RAD( 80.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_GRAPHICS | Mix::EC_DYNAMICS;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Dynamics\\Controller\\Vehicle" );
	engineConfig.pCaption = L"Dynamics - Vehicle";
	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::Dynamics::IManager* pDynamicsMgr = Mix::Dynamics::GetManagerPtr();
		Mix::Dynamics::IWorld* pDynamicsWorld = NULL;
		Mix::Dynamics::IVehicle* pDynamicsVehicle = 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::FollowCameraHelper fwCamHelper;
		Mix::Matrix4x4 viewMat;
		Mix::Matrix4x4 projMat;

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

		pIOMgr->MountDirectory( L"Data" );

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

		pInputMgr->GetKeyboard( &pKeyboard );

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

		if( pDynamicsMgr->CreateWorld( Mix::Dynamics::WORLD_CONFIG(), &pDynamicsWorld, L"Sample" ) == True )
		{
			Mix::Dynamics::IStaticPlane* pStaticPlane = NULL;
			Mix::Dynamics::IBoxShape* pChassisShape = NULL;

			if( pDynamicsMgr->CreateStaticPlane( Mix::Vector3( 0.0f, 1.0f, 0.0f ), 0.0f, &pStaticPlane, L"Sample" ) == True )
			{
				pStaticPlane->Debug_SetDrawExtent( 100 );
				pDynamicsWorld->AddObject( pStaticPlane );
			}

			if( pDynamicsMgr->CreateBoxShape( VC_WIDTH, VC_HEIGHT, VC_LENGTH, &pChassisShape, L"Sample" ) == True )
			{
				Mix::Dynamics::VEHICLE_WHEEL_DESC vwDescs[4];

				vwDescs[0].axis = VW_AXIS;
				vwDescs[0].bFront = True;
				vwDescs[0].connectionPoint = Mix::Vector3( -VC_WIDTH, VW_OFFSET_Y, VW_OFFSET_Z );
				vwDescs[0].direction = VW_DIR;
				vwDescs[0].suspensionRestLength = VW_SUS_LEN;
				vwDescs[0].tireRadius = VW_TIRE_RADIUS;

				vwDescs[1].axis = VW_AXIS;
				vwDescs[1].bFront = True;
				vwDescs[1].connectionPoint = Mix::Vector3( VC_WIDTH, VW_OFFSET_Y, VW_OFFSET_Z );
				vwDescs[1].direction = VW_DIR;
				vwDescs[1].suspensionRestLength = VW_SUS_LEN;
				vwDescs[1].tireRadius = VW_TIRE_RADIUS;

				vwDescs[2].axis = VW_AXIS;
				vwDescs[2].bFront = False;
				vwDescs[2].connectionPoint = Mix::Vector3( -VC_WIDTH, VW_OFFSET_Y, -VW_OFFSET_Z );
				vwDescs[2].direction = VW_DIR;
				vwDescs[2].suspensionRestLength = VW_SUS_LEN;
				vwDescs[2].tireRadius = VW_TIRE_RADIUS;

				vwDescs[3].axis = VW_AXIS;
				vwDescs[3].bFront = False;
				vwDescs[3].connectionPoint = Mix::Vector3( VC_WIDTH, VW_OFFSET_Y, -VW_OFFSET_Z );
				vwDescs[3].direction = VW_DIR;
				vwDescs[3].suspensionRestLength = VW_SUS_LEN;
				vwDescs[3].tireRadius = VW_TIRE_RADIUS;

				if( pDynamicsMgr->CreateVehicle( 80.9135725f, pChassisShape, vwDescs, 4, &pDynamicsVehicle, L"Sample" ) == True )
				{
					pDynamicsVehicle->SetWorldPosition( VC_INIT_POS );
					pDynamicsWorld->AddObject( pDynamicsVehicle );

					for( UInt32 i = 0; i < 4; i++ )
					{
						Mix::Dynamics::IVehicleWheel* pWheel = pDynamicsVehicle->GetWheelPtr( i );
						Mix::Dynamics::IVehicleWheel::SUSPENSION sus = pWheel->GetSuspension();
						Mix::Dynamics::IVehicleWheel::TIRE tire = pWheel->GetTire();

						sus.springStiffness = VW_SUS_SPRING;
						tire.friction = VW_TIRE_FRICTION;

						pWheel->SetSuspension( sus );
						pWheel->SetTire( tire );
					}
				}
			}

			MIX_RELEASE( pChassisShape );
			MIX_RELEASE( pStaticPlane );
		}

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

		pGraphicsMgr->GetDevice( &pGraphicsDev );

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

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

		fwCamHelper.Initialize( pDynamicsVehicle->GetWorldRotation(), pDynamicsVehicle->GetWorldPosition(), -CAM_DIST, CAM_ELEV );

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

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

		pEngine->ResetFPS();

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

			Float32 vwEngineForce = 0.0f;
			Float32 vwBreakingForce = 0.0f;
			Float32 vwSteeringValue = pDynamicsVehicle->GetWheelPtr( 0 )->GetSteeringValue();

			//ステアリング
			if( pKeyboard->GetKeyState( Mix::HID::KEY_LEFT ) & Mix::HID::DOWN )
			{
				vwSteeringValue -= VW_STEERING_POWER;
			}
			else if( pKeyboard->GetKeyState( Mix::HID::KEY_RIGHT ) & Mix::HID::DOWN )
			{
				vwSteeringValue += VW_STEERING_POWER;
			}

			//アクセル
			if( pKeyboard->GetKeyState( Mix::HID::KEY_W ) & Mix::HID::DOWN )
			{
				vwEngineForce = VW_ENGINE_FORCE_F;
			}
			else if( pKeyboard->GetKeyState( Mix::HID::KEY_S ) & Mix::HID::DOWN )
			{
				vwEngineForce = VW_ENGINE_FORCE_B;
			}
			else
			{
				vwBreakingForce += VW_ENGINE_BREAKING_FORCE;
			}

			//ブレーキ
			if( pKeyboard->GetKeyState( Mix::HID::KEY_SPACE ) & Mix::HID::DOWN )
			{
				vwBreakingForce += VW_FOOT_BREAKING_FORCE;
			}

			//リセット
			if( pKeyboard->GetKeyState( Mix::HID::KEY_R ) & Mix::HID::PRESSED )
			{
				pDynamicsVehicle->Reset( Mix::Quaternion::Identity(), VC_INIT_POS );
			}

			//ステアリングを設定
			vwSteeringValue = MIX_CLAMP( vwSteeringValue, -VW_STEERING_LIMIT, +VW_STEERING_LIMIT );
			pDynamicsVehicle->GetWheelPtr( 0 )->SetSteeringValue( vwSteeringValue );
			pDynamicsVehicle->GetWheelPtr( 1 )->SetSteeringValue( vwSteeringValue );

			//エンジンフォースを設定
			pDynamicsVehicle->GetWheelPtr( 0 )->SetEngineForce( vwEngineForce );
			pDynamicsVehicle->GetWheelPtr( 1 )->SetEngineForce( vwEngineForce );

			//ブレーキングフォースを設定
			pDynamicsVehicle->GetWheelPtr( 0 )->SetBreakingForce( vwBreakingForce );
			pDynamicsVehicle->GetWheelPtr( 1 )->SetBreakingForce( vwBreakingForce );
			pDynamicsVehicle->GetWheelPtr( 2 )->SetBreakingForce( vwBreakingForce );
			pDynamicsVehicle->GetWheelPtr( 3 )->SetBreakingForce( vwBreakingForce );

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

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

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

			Float32 camElev = fwCamHelper.GetElevation();

			if( pKeyboard->GetKeyState( Mix::HID::KEY_UP ) & Mix::HID::DOWN )
			{
				camElev += CAM_ELEV_COIFFE;
			}
			else if( pKeyboard->GetKeyState( Mix::HID::KEY_DOWN ) & Mix::HID::DOWN )
			{
				camElev -= CAM_ELEV_COIFFE;
			}

			fwCamHelper.SetElevation( MIX_CLAMP( camElev, CAM_ELEV_MIN, CAM_ELEV_MAX ) );
			fwCamHelper.Update( pDynamicsVehicle->GetWorldRotation(), pDynamicsVehicle->GetWorldPosition() );

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

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

			pDynamicsWorld->Debug_Draw( pPersRenderer );

			pPersRenderer->Update();

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

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

			/*
				FPS
			*/

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

			/*
				Speed
			*/

			tempStr.Sprintf( L"%.2f km/h", pDynamicsVehicle->GetCurrentSpeedKmHour() );

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

			/*
				Desription
			*/

			tempStr.Sprintf( L"%s",	L"Controls :\n"
									L"  Steering  : Left or Right key\n"
									L"  Elevation : Up or Down key\n"
									L"  Accel     : W or S key\n"
									L"  Brake     : Space key\n"
									L"  Reset     : R key\n" );

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

			/*
				Update
			*/

			pCanvasRenderer->Update();

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

			if( pKeyboard->GetKeyState( Mix::HID::KEY_TAB ) & Mix::HID::PRESSED )
			{
				pEngine->ResetFPS();
			}

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

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

		MIX_RELEASE( pFont );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pPersRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pDynamicsVehicle );
		MIX_RELEASE( pDynamicsWorld );
		MIX_RELEASE( pKeyboard );

		Mix::Finalize();
	}

	return 0;
}
