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

#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"

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

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

static const Mix::Vector3 CAM_INIT_AT( 0.0f, 0.8f, 0.0f );
static const Mix::Vector2 CAM_INIT_ANGLE( MIX_TO_RAD( 10.0f ), MIX_TO_RAD( 140.0f ) );
static const Float32 CAM_INIT_DIST = 3.5f;

enum MOTION_INDEX
{
	MI_STAND	= 0,
	MI_WALK		= 1,
	MI_RUN		= 2,
	MI_MAX		= 3,
};

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

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;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Scene\\Motion\\Basic" );
	engineConfig.pCaption = L"Scene - Motion ( 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::HID::IKeyboard* pKeyboard = NULL;
		Mix::HID::IMouse* pMouse = NULL;

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

		Mix::Scene::IManager* pSceneMgr = Mix::Scene::GetManagerPtr();
		Mix::Scene::IEffectPackage* pSceneEffectPackage = NULL;
		Mix::Scene::IRenderer* pSceneRenderer = NULL;
		Mix::Scene::IViewCamera* pCamera = NULL;

		Mix::Scene::IActorModel* pModel = NULL;
		Mix::Scene::IMotionController* pMotCtrl = NULL;
		Mix::Scene::MOTION_HANDLE motHandls[MI_MAX] = { NULL };
		UInt32 motIndex = MI_STAND;

		Utility::ViewCameraHelper viewCamHelper;

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

		pIOMgr->MountDirectory( L"Data" );

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

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

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

		pGraphicsMgr->GetDevice( &pGraphicsDev );

		if( pGraphicsMgr->CreateCanvasRenderer( &pCanvasRenderer, MIX_DEBUGNAME ) == 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 = 0;

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

				camConfig.targetSize.Set( SCREEN_WIDTH, SCREEN_HEIGHT );
				camConfig.caps = 0;
				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.1f, 100.0f );

					pSceneRenderer->AddCamera( pCamera );
				}

				if( pSceneMgr->CreateActorModelFromFile( pSceneEffectPackage, L"Data\\Model\\human.mam", &pModel ) == True )
				{
					Mix::Scene::IActorDynamicsDirector* pDynamicsDirector = NULL;
					Mix::Scene::IMotionMixer* pMotMixer = NULL;

					pModel->GetDynamicsDirector( &pDynamicsDirector );
					pModel->GetMotionMixer( &pMotMixer );

					pDynamicsDirector->SetCharacterEnabled( False );
					pDynamicsDirector->SetColliderMode( Mix::Scene::DC_KINEMATIC );

					pMotMixer->SetFlags( Mix::Scene::IMotionMixer::BODY_TRANSFORM | Mix::Scene::IMotionMixer::FRAME_STEP );

					if( pMotMixer->GetControllerByName( L"Body", &pMotCtrl ) == True )
					{
						motHandls[MI_STAND] = pMotCtrl->GetHandleByName( L"Stand" );
						motHandls[MI_WALK] = pMotCtrl->GetHandleByName( L"Walk" );
						motHandls[MI_RUN] = pMotCtrl->GetHandleByName( L"Run" );

						if( motHandls[MI_STAND] != NULL )
						{
							Mix::Scene::MOTION_COMMAND com;

							com.flags = Mix::Scene::MOTION_CF_PLAY;
							com.frame = 0.0f;
							com.handle = motHandls[MI_STAND];
							com.loopCount = Mix::Scene::MOTION_INFINITE_LOOP;
							com.transitionTimeLength = 0.0f;

							pMotCtrl->SendCommand( Mix::Scene::MOTION_CE_DEFAULT, com );
						}
					}

					pModel->Reset();

					pSceneRenderer->AddActorModel( pModel );

					MIX_RELEASE( pMotMixer );
					MIX_RELEASE( pDynamicsDirector );
				}

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

					pSceneRenderer->SetDirectionalLight( pDirLight );
				}

				MIX_RELEASE( pDirLight );
			}
		}

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

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

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

		pEngine->ResetFPS();

		while( pEngine->Update() == True )
		{
			Mix::String tempStr;
			Mix::Vector2 txtSize;

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

			if( pKeyboard->GetKeyState( Mix::HID::KEY_SPACE ) & Mix::HID::PRESSED )
			{
				motIndex = ( motIndex + 1 ) % MI_MAX;

				if( motHandls[motIndex] != NULL )
				{
					Mix::Scene::MOTION_COMMAND com;

					com.flags = Mix::Scene::MOTION_CF_PLAY_INHERIT;
					com.frame = 0.0f;
					com.handle = motHandls[motIndex];
					com.loopCount = Mix::Scene::MOTION_INFINITE_LOOP;
					com.transitionTimeLength = 0.1f;

					pMotCtrl->SendCommand( Mix::Scene::MOTION_CE_DEFAULT, com );
				}
			}

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

			viewCamHelper.Update();

			pCamera->SetView( viewCamHelper.GetAt(), viewCamHelper.GetRotation(), viewCamHelper.GetDistance() );
			pCamera->Update();

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

			/*
				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"  Toggle motion  : Space key\n" );

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

			pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
			pCanvasRenderer->AddString( 16.0f, static_cast<Float32>( SCREEN_HEIGHT ) - txtSize.y - 16.0f, tempStr.GetConstPtr() );

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

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

			pSceneRenderer->Update( pEngine->GetDT() );
			pCanvasRenderer->Update();

			pSceneRenderer->Refresh();
			pModel->Refresh();

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

			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 )
			{
				pCamera->Draw();
				pCanvasRenderer->Draw();

				pGraphicsDev->End();
			}

			pGraphicsDev->Present();
		}

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

		viewCamHelper.Dispose();

		MIX_RELEASE( pMotCtrl );
		MIX_RELEASE( pModel );

		MIX_RELEASE( pCamera );
		MIX_RELEASE( pSceneRenderer );
		MIX_RELEASE( pSceneEffectPackage );

		MIX_RELEASE( pFont );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pMouse );
		MIX_RELEASE( pKeyboard );

		Mix::Finalize();
	}

	return 0;
}
