#include "Mix/Tool/CLR/Core/Engine.h"

namespace Mix{ namespace Tool{

Engine::Engine( int baseFps ) :
m_FrameCount( 0 ),
m_NoDelayCount( 0 ),
m_Freq( new LARGE_INTEGER() ),
m_Period( new LARGE_INTEGER() ),
m_BeforeTime( new LARGE_INTEGER() ),
m_AfterTime( new LARGE_INTEGER() ),
m_TimeDiff( new LARGE_INTEGER() ),
m_SleepTime( new LARGE_INTEGER() ),
m_OverSleepTime( new LARGE_INTEGER() ),
m_PrevCalcTime( new LARGE_INTEGER() ),
m_TempTime( new LARGE_INTEGER() ),
m_DynamicsWorld( nullptr ),
m_GraphicsManager( nullptr )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////

	//基本的なこと
	m_BaseFps = ( baseFps > 0 )? baseFps : 60;
	m_Fps = static_cast<float>( m_BaseFps );
	m_Dt = 1.0f / static_cast<float>( m_BaseFps );
	m_Mag = 1.0f;

	//周波数を取得
	::QueryPerformanceFrequency( m_Freq );

	//フレームレートの維持に必要な変数を初期化
	m_Period->QuadPart = m_Freq->QuadPart / m_BaseFps;
	::QueryPerformanceCounter( m_BeforeTime );
	m_OverSleepTime->QuadPart = 0;

	//計測開始時間を取得
	m_PrevCalcTime->QuadPart = m_BeforeTime->QuadPart;

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

	try
	{
		m_DynamicsWorld = gcnew Mix::Tool::Dynamics::World();
		m_GraphicsManager = gcnew Mix::Tool::Graphics::Manager();
	}
	catch( System::Exception^ )
	{
		throw;
	}
}

Engine::~Engine( void )
{
	if( m_GraphicsManager != nullptr )
	{
		delete m_GraphicsManager;
	}

	if( m_DynamicsWorld != nullptr )
	{
		delete m_DynamicsWorld;
	}

	if( m_Freq != NULL ) { delete m_Freq; m_Freq = NULL; }
	if( m_Period != NULL ) { delete m_Period; m_Period = NULL; }
	if( m_BeforeTime != NULL ) { delete m_BeforeTime; m_BeforeTime = NULL; }
	if( m_AfterTime != NULL ) { delete m_AfterTime; m_AfterTime = NULL; }
	if( m_TimeDiff != NULL ) { delete m_TimeDiff; m_TimeDiff = NULL; }
	if( m_SleepTime != NULL ) { delete m_SleepTime; m_SleepTime = NULL; }
	if( m_OverSleepTime != NULL ) { delete m_OverSleepTime; m_OverSleepTime = NULL; }
	if( m_PrevCalcTime != NULL ) { delete m_PrevCalcTime; m_PrevCalcTime = NULL; }
	if( m_TempTime != NULL ) { delete m_TempTime; m_TempTime = NULL; }
}

void Engine::Update( void )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// FPSの更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	//フレーム数を制限する
	::QueryPerformanceCounter( m_AfterTime );
	m_TimeDiff->QuadPart = m_AfterTime->QuadPart - m_BeforeTime->QuadPart;
	m_SleepTime->QuadPart = ( m_Period->QuadPart - m_TimeDiff->QuadPart ) - m_OverSleepTime->QuadPart;

	if( m_SleepTime->QuadPart > 0 )
	{
		//休止が取れる場合
		::Sleep( static_cast<unsigned int>( m_SleepTime->QuadPart * 1000 / m_Freq->QuadPart ) );

		//Sleepの誤差
		::QueryPerformanceCounter( m_TempTime );
		m_OverSleepTime->QuadPart = ( m_TempTime->QuadPart - m_AfterTime->QuadPart ) - m_SleepTime->QuadPart;
	}
	else
	{
		//休止が取れない場合
		m_OverSleepTime->QuadPart = 0;
		if( ( ++m_NoDelayCount ) > 16 )
		{
			::Sleep( 0 );
			m_NoDelayCount = 0;
		}
	}

	::QueryPerformanceCounter( m_BeforeTime );

	//フレーム数を進める
	m_FrameCount++;

	//FPSを算出
	::QueryPerformanceCounter( m_TempTime );
	if( ( m_TempTime->QuadPart - m_PrevCalcTime->QuadPart ) > m_Freq->QuadPart )
	{
		m_Fps = ( static_cast<float>( m_FrameCount ) / static_cast<float>( m_TempTime->QuadPart - m_PrevCalcTime->QuadPart ) ) * m_Freq->QuadPart;
		m_Dt = ( MIX_FLOAT_IS_ZERO( m_Fps ) == false )? ( 1.0f / m_Fps ) : 0.0f;
		m_Mag = m_Fps / static_cast<float>( m_BaseFps );

		m_PrevCalcTime->QuadPart = m_TempTime->QuadPart;
		m_FrameCount = 0;
	}

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

	//グラフィックスマネージャ
	m_GraphicsManager->Update( m_BaseFps, m_Fps, m_Mag );

	//ダイナミクスワールド
	m_DynamicsWorld->Update( m_Fps );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リフレッシュ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_GraphicsManager->Refresh();

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

	m_GraphicsManager->Draw();
}

int Engine::BaseFps::get( void )
{
	return m_BaseFps;
}

float Engine::Fps::get( void )
{
	return m_Fps;
}

float Engine::Dt::get( void )
{
	return m_Dt;
}

float Engine::Mag::get( void )
{
	return m_Mag;
}

Mix::Tool::Dynamics::World^ Engine::DynamicsWorld::get( void )
{
	return m_DynamicsWorld;
}

Mix::Tool::Graphics::Manager^ Engine::GraphicsManager::get( void )
{
	return m_GraphicsManager;
}

}}
