#include "Mix/Private/Engine.h"

#include "Mix/Private/Resource.h"
#include "Mix/Private/UserFile.h"
#include "Mix/Private/Graphics/Common/Device.h"

namespace Mix{

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Engine : グローバル定義
////////////////////////////////////////////////////////////////////////////////////////////////////

const wchar_t* Engine::DEF_CAPTION = L"Mix アプリケーション";
UInt32 Engine::WINDOW_STYLE = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE;
UInt32 Engine::FULLSCREEN_STYLE = WS_POPUP | WS_VISIBLE;

const wchar_t* Engine::PLUGIN_TYPE_TEXT_TABLE[Mix::Plugin::TYPE_MAX] =
{
	L"UNKNOWN",
	L"TEXTURE_LOADER",
	L"SOUND_DECODER",
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Engine
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Engine* Engine::g_pThis = NULL;

Boolean Engine::CreateInstance( const Mix::ENGINE_CONFIG& config, Mix::UserFile* pSysReport )
{
	MIX_ASSERT( g_pThis == NULL );

	g_pThis = MIX_LIB_NEW_T( Mix::Memory::SECTION_GENERAL, Engine );

	if( g_pThis != NULL )
	{
		if( g_pThis->Initialize( config, pSysReport ) == False )
		{
			MIX_RELEASE( g_pThis );
			return False;
		}
	}
	else
	{
		return False;
	}

	return True;
}

void Engine::ReleaseInstance( void )
{
	if( g_pThis != NULL )
	{
		//念のための機能停止
		g_pThis->Dispose();
	}

	MIX_RELEASE( g_pThis );
}

Engine* Engine::GetInstance( void )
{
	return g_pThis;
}

Engine::Engine( void ) :
m_pMsgProcFunc( NULL ),
m_pMsgProcData( NULL ),
m_pIOMgr( NULL ),
m_pSoundMgr( NULL ),
m_pHIDMgr( NULL ),
m_pGraphicsMgr( NULL ),
m_pDynamicsMgr( NULL ),
m_pSceneMgr( NULL ),
m_pParallelMgr( NULL )
{
	m_Freq.QuadPart = 0;
	m_InvFreqF64 = 0.0;
	m_PeriodCount.QuadPart = 0;
	m_BeforeCount.QuadPart = 0;
	m_BeforeUpdateCount.QuadPart = 0;
	m_ErrorSleepCount.QuadPart = 0;
	m_SleepTimeCount = 0.0;
	m_InsomniaFrameMax = 16;
	m_InsomniaFrameCount = 0;
	m_FrameCount = 0;
	m_BaseFPS = 0;
	m_BaseET = 0.0f;
	m_FPS = 0.0f;
	m_ET = 0.0f;
	m_STPS = 0.0;
	m_IFCPS = 0;

	m_hWnd = NULL;
	m_Caption = L"";
	m_ExternalWndProc = NULL;
	m_bSizeable = False;
	m_bCloseEnabled = True;
	m_bWindowed = False;
	m_bActive = True;
}

Engine::~Engine( void )
{
	MIX_RELEASE( m_pParallelMgr );
	MIX_RELEASE( m_pSceneMgr );
	MIX_RELEASE( m_pGraphicsMgr );
	MIX_RELEASE( m_pSoundMgr );
	MIX_RELEASE( m_pDynamicsMgr );
	MIX_RELEASE( m_pHIDMgr );
	MIX_RELEASE( m_pIOMgr );
}

Boolean Engine::Initialize( const Mix::ENGINE_CONFIG& config, Mix::UserFile* pSysReport )
{
	MIX_ASSERT( pSysReport != NULL );

	MIX_ASSERT( m_pIOMgr == NULL );
	MIX_ASSERT( m_pHIDMgr == NULL );
	MIX_ASSERT( m_pDynamicsMgr == NULL );
	MIX_ASSERT( m_pSoundMgr == NULL );
	MIX_ASSERT( m_pGraphicsMgr == NULL );
	MIX_ASSERT( m_pSceneMgr == NULL );
	MIX_ASSERT( m_pParallelMgr == NULL );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ウィンドウ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( config.hWnd == NULL )
	{
		if( MakeWindow( config.targetSize, config.pCaption, config.hIcon, config.hSmallIcon, config.bAcceptDrop ) == False )
		{
			Dispose_Init();
			return False;
		}
	}
	else
	{
		if( LoadWindow( config.hWnd, config.targetSize ) == False )
		{
			Dispose_Init();
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイル
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( config.flags, Mix::EC_IO_NARROW ) == Mix::EC_IO_NARROW )
	{
		m_pIOMgr = Mix::IO::NarrowManager::CreateInstance();
	}
	else
	{
		m_pIOMgr = Mix::IO::BroadManager::CreateInstance();
	}

	if( m_pIOMgr != NULL )
	{
		if( m_pIOMgr->Initialize( config.pRootDirectoryPath, config.pUserDirectoryPath ) == False )
		{
			Dispose_Init();
			return False;
		}
	}
	else
	{
		Dispose_Init();
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ヒューマンインターフェースデバイス
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( config.flags, Mix::EC_HID ) != 0 )
	{
		m_pHIDMgr = Mix::HID::Manager::CreateInstance();

		if( m_pHIDMgr != NULL )
		{
			if( m_pHIDMgr->Initialize( m_hWnd, ( config.flags & Mix::EC_HID ), pSysReport ) == False )
			{
				Dispose_Init();
				return False;
			}
		}
		else
		{
			Dispose_Init();
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// サウンド
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( config.flags, Mix::EC_SOUND ) == Mix::EC_SOUND )
	{
		m_pSoundMgr = Mix::Sound::Manager::CreateInstance();
	
		if( m_pSoundMgr != NULL )
		{
			if( m_pSoundMgr->Initialize( pSysReport ) == False )
			{
				Dispose_Init();
				return False;
			}
		}
		else
		{
			Dispose_Init();
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// グラフィックス
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( config.flags, Mix::EC_GRAPHICS ) == Mix::EC_GRAPHICS )
	{
		switch( config.shaderModel )
		{
		case Mix::Graphics::SHADER_MODEL_3:
			m_pGraphicsMgr = Mix::Graphics::DX9::Manager::CreateInstance();
			break;
		case Mix::Graphics::SHADER_MODEL_4:
		case Mix::Graphics::SHADER_MODEL_5:
			m_pGraphicsMgr = Mix::Graphics::DX11::Manager::CreateInstance();
			break;
		}

		if( m_pGraphicsMgr != NULL )
		{
			if( m_pGraphicsMgr->Initialize( config.targetSize,
											config.shaderModel, config.bFullscreen, config.bWaitVSync,
											config.textTabSize, config.textTargetSize, config.initialTextTargetNum,
											pSysReport ) == False )
			{
				Dispose_Init();
				return False;
			}
		}
		else
		{
			Dispose_Init();
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ダイナミクス
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( config.flags, Mix::EC_DYNAMICS ) == Mix::EC_DYNAMICS )
	{
		m_pDynamicsMgr = Mix::Dynamics::Manager::CreateInstance( MIX_TESTBIT( config.flags, Mix::EC_PARALLEL ) == Mix::EC_PARALLEL );

		if( m_pDynamicsMgr == NULL )
		{
			Dispose_Init();
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// シーン
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( config.flags, Mix::EC_SCENE ) == Mix::EC_SCENE )
	{
		m_pSceneMgr = Mix::Scene::Common::Manager::CreateInstance();

		if( m_pSceneMgr != NULL )
		{
			if( m_pSceneMgr->Initialize() == False )
			{
				Dispose_Init();
				return False;
			}
		}
		else
		{
			Dispose_Init();
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 並列
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( config.flags, Mix::EC_PARALLEL ) == Mix::EC_PARALLEL )
	{
		m_pParallelMgr = Mix::Parallel::Manager::CreateInstance();

		if( m_pParallelMgr != NULL )
		{
			if( m_pParallelMgr->Initialize() == False )
			{
				Dispose_Init();
				return False;
			}
		}
		else
		{
			Dispose_Init();
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// プラグインのロード
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::StringW pluginDirPath;

	if( Mix::IO::CombinePath( config.pRootDirectoryPath, True, config.pPluginDirectoryPath, pluginDirPath ) == False )
	{
		return False;
	}

	if( Mix::IO::CombinePath( pluginDirPath.GetConstPtr(), False, L"\\", pluginDirPath ) == False )
	{
		return False;
	}

	LoadPlugins( pluginDirPath.GetConstPtr() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// フレームレート
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_BaseFPS = max( 0, config.baseFPS );
	m_BaseET = MIX_FLOAT_RECIPROCAL( static_cast<Float32>( m_BaseFPS ) );
	m_InsomniaFrameMax = max( 0, config.insomniaFrames );

	ResetFPS();

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

	return True;
}

Boolean Engine::MakeWindow( const Mix::Point& targetSize, const wchar_t* pCaption, HICON hIcon, HICON hSmallIcon, Boolean bAcceptDrop )
{
	HINSTANCE hInstance = Mix::GetInternalInstanceHandle();
	WNDCLASSEX wcex = { 0 };

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 初期値の設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_WndStyle = Engine::WINDOW_STYLE;
	m_WndExStyle = ( bAcceptDrop == True )? WS_EX_ACCEPTFILES : 0;
	m_Caption = ( pCaption != NULL )? pCaption : Engine::DEF_CAPTION;
	m_TargetSize = targetSize;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// クラスを登録
	////////////////////////////////////////////////////////////////////////////////////////////////////

	wcex.cbSize			= sizeof( WNDCLASSEX );
	wcex.style			= ( CS_HREDRAW | CS_VREDRAW );
	wcex.lpfnWndProc	= Engine::MessageProcEntry;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hCursor		= ::LoadCursor( NULL, IDC_ARROW );
	wcex.hbrBackground	= static_cast<HBRUSH>( ::GetStockObject( BLACK_BRUSH ) );
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= m_Caption.GetConstPtr();

	if( ( hIcon != NULL ) && ( hSmallIcon != NULL ) )
	{
		wcex.hIcon = hIcon;
		wcex.hIconSm = hSmallIcon;
	}
	else if( ( hIcon != NULL ) && ( hSmallIcon == NULL ) )
	{
		wcex.hIcon = hIcon;
		wcex.hIconSm = hIcon;
	}
	else if( ( hIcon == NULL ) && ( hSmallIcon != NULL ) )
	{
		wcex.hIcon = hSmallIcon;
		wcex.hIconSm = hSmallIcon;
	}
	else
	{
		wcex.hIcon = (HICON)LoadImage( hInstance, MAKEINTRESOURCE( IDI_MIX ), IMAGE_ICON, 0, 0, 0 );
		wcex.hIconSm = (HICON)LoadImage( hInstance, MAKEINTRESOURCE( IDI_MIX_SMALL ), IMAGE_ICON, 0, 0, 0 );
	}

	if( ::RegisterClassEx( &wcex ) == 0 )
	{
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_hWnd = ::CreateWindowEx(	static_cast<DWORD>( m_WndExStyle ),
								m_Caption.GetConstPtr(),
								m_Caption.GetConstPtr(),
								WS_POPUP,
								CW_USEDEFAULT,
								CW_USEDEFAULT,
								CW_USEDEFAULT,
								CW_USEDEFAULT,
								HWND_DESKTOP,
								NULL,
								hInstance,
								NULL );
	if( m_hWnd != NULL )
	{
		SetWindowStyle( True );
		::ShowWindow( m_hWnd, SW_SHOW );
	}
	else
	{
		return False;
	}

	return True;
}

Boolean Engine::LoadWindow( HWND hWnd, const Mix::Point& targetSize )
{
	MIX_ASSERT( hWnd != NULL );

	RECT rect;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 初期値の設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	// ウィンドウスタイル

	m_WndStyle = ::GetWindowLongPtr( hWnd, GWL_STYLE );
	m_WndExStyle = ::GetWindowLongPtr( hWnd, GWL_EXSTYLE );

	// タイトル

	int wndTxtLen = ::GetWindowTextLength( hWnd );
	if( wndTxtLen > 0 )
	{
		wchar_t* wndText = static_cast<wchar_t*>( MIX_LIB_MALLOC( Mix::Memory::SECTION_GENERAL, sizeof( wchar_t ) * ( static_cast<UIntT>( wndTxtLen ) + 1 ) ) );
		if( wndText != NULL )
		{
			if( ::GetWindowText( hWnd, wndText, wndTxtLen + 1 ) > 0 )
			{
				m_Caption = wndText;
			}

			MIX_LIB_FREE( wndText );
		}
	}

	// クライアントのサイズ

	if( MIX_TESTBIT( m_WndStyle, WS_THICKFRAME ) == WS_THICKFRAME )
	{
		if( ::GetClientRect( hWnd, &rect ) == TRUE )
		{
			m_TargetSize.x = rect.right - rect.left;
			m_TargetSize.y = rect.bottom - rect.top;
		}
		else
		{
			return False;
		}

		m_bSizeable = True;
	}
	else
	{
		m_TargetSize = targetSize;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// プロシージャ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_ExternalWndProc = reinterpret_cast<WNDPROC>( GetWindowLongPtr( hWnd , GWLP_WNDPROC ) );
	if( m_ExternalWndProc == NULL )
	{
		return False;
	}

	if( SetWindowLongPtr( hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>( Engine::MessageProcEntry ) ) == 0 )
	{
		return False;
	}

	m_hWnd = hWnd;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// スタイルを適用
	////////////////////////////////////////////////////////////////////////////////////////////////////

	SetWindowStyle( True );

	return True;
}

void Engine::DisposeWindow( void )
{
	m_pMsgProcFunc = NULL;
	m_pMsgProcData = NULL;

	if( ( m_ExternalWndProc == NULL ) && ( m_hWnd != NULL ) )
	{
		::DestroyWindow( m_hWnd );
		m_hWnd = NULL;
	}
}

Boolean Engine::UpdateWindow( void )
{
	MSG msg;

	while( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) == TRUE )
	{
		if( ::GetMessage( &msg, NULL, 0, 0 ) == TRUE )
		{
			::TranslateMessage( &msg );
			::DispatchMessage( &msg );
		}
		else
		{
			return False;
		}
	}

	return True;
}

LRESULT Engine::MessageProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	Boolean bCallProc = True;
	LRESULT ret = 0;

	//設定されているメッセージプロシージャを呼び出す
	if( m_pMsgProcFunc != NULL )
	{
		m_pMsgProcFunc( hWnd, msg, wParam, lParam, m_pMsgProcData );
	}

	//グラフィックスマネージャのメッセージ処理
	if( m_pGraphicsMgr != NULL )
	{
		m_pGraphicsMgr->MessageProc( msg, wParam, lParam );
	}

	//HID マネージャのメッセージ処理
	if( m_pHIDMgr != NULL )
	{
		m_pHIDMgr->MessageProc( msg, wParam, lParam );
	}

	//メッセージを処理する
	switch( msg )
	{
	case WM_PAINT:
		::ValidateRect( hWnd, NULL );
		break;

	case WM_ACTIVATEAPP:
		m_bActive = static_cast<Boolean>( wParam );
		break;

	case WM_SIZE:
		SetCloseEnabled( m_bCloseEnabled );
		break;

	case WM_MOVE:
	case WM_EXITSIZEMOVE:
	case WM_EXITMENULOOP:
		ResetFPS();
		break;

	case WM_STYLECHANGED:
		SetCloseEnabled( m_bCloseEnabled );
		break;

	case WM_IME_SETCONTEXT:
		//変換候補のリストを出さないようにする
		lParam = 0;
		break;
	
	case WM_SYSCOMMAND:
		switch ( wParam & 0xFFF0 )
		{
		//スクリーンセーバーを作動させない
		//モニターパワーを落とさせない
		case SC_SCREENSAVE:
		case SC_MONITORPOWER:
			bCallProc = False;
			break;

		case SC_MOVE:
		case SC_SIZE:
		case SC_MAXIMIZE:
		case SC_KEYMENU:
			if( m_bWindowed == False )
			{
				bCallProc = False;
			}
			break;

		case SC_CLOSE:
			if( m_bCloseEnabled == False )
			{
				bCallProc = False;
			}
			break;
		}
		break;

	case WM_DESTROY:
		DisposeWindow();
		::PostQuitMessage( 0 );
		break;

	case WM_MIX_INTERNALERROR:
		Mix::String text;
		text.Sprintf( L"内部エラーが発生しました。\nアプリケーションを強制終了します。(%d)", wParam );
		::MessageBoxW( hWnd, text.GetConstPtr(), L"Mix", MB_ICONERROR | MB_OK );
		::PostMessage( hWnd, WM_DESTROY, 0, 0 );
		bCallProc = False;
		break;
	}

	if( bCallProc == True )
	{
		if( m_ExternalWndProc == NULL )
		{
			ret = ::DefWindowProc( hWnd, msg, wParam, lParam );
		}
		else
		{
			ret = ::CallWindowProcW( m_ExternalWndProc, hWnd, msg, wParam, lParam );
		}
	}

	return ret;
}

LRESULT CALLBACK Engine::MessageProcEntry( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	return Engine::g_pThis->MessageProc( hWnd, msg, wParam, lParam );
}

Boolean Engine::IsSizeableWindow( void ) const
{
	return m_bSizeable;
}

void Engine::SetWindowStyle( Boolean bWindowed )
{
	if( m_bWindowed == bWindowed )
	{
		return;
	}

	if( bWindowed == True )
	{
		RECT workRect;
		RECT rect;

		rect.left = 0;
		rect.top = 0;
		rect.right = m_TargetSize.x;
		rect.bottom = m_TargetSize.y;

		::AdjustWindowRectEx( &rect, static_cast<DWORD>( m_WndStyle ), ( GetMenu( m_hWnd ) != NULL ), static_cast<DWORD>( m_WndExStyle ) );

		if( ::SystemParametersInfo( SPI_GETWORKAREA, 0, &workRect, 0 ) )
		{
			Int32 workWidth = workRect.right - workRect.left;
			Int32 workHeight = workRect.bottom - workRect.top;
			Int32 ox = ( workWidth > m_TargetSize.x )? ( workRect.left + ( ( workWidth - m_TargetSize.x ) / 2 ) ) : 0;
			Int32 oy = ( workHeight > m_TargetSize.y )? ( workRect.top + ( ( workHeight - m_TargetSize.y ) / 2 ) ) : 0;

			rect.left += ox;
			rect.top += oy;
			rect.right += ox;
			rect.bottom += oy;
		}

		::SetWindowLongPtr( m_hWnd, GWL_STYLE, m_WndStyle );
		::SetWindowLongPtr( m_hWnd, GWL_EXSTYLE, m_WndExStyle );

		::SetWindowPos(	m_hWnd, HWND_NOTOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_SHOWWINDOW );
	}
	else
	{
		::SetWindowLongPtr( m_hWnd, GWL_STYLE, Engine::FULLSCREEN_STYLE );
		::SetWindowLongPtr( m_hWnd, GWL_EXSTYLE, 0 );

		::SetMenu( m_hWnd, NULL );
	}

	//ウィンドウかどうか
	m_bWindowed = bWindowed;
}

void Engine::LoadPlugins( const wchar_t* pDirPath )
{
	MIX_ASSERT( pDirPath != NULL );

	Mix::String searchPath;
	WIN32_FIND_DATAW wfd;
	HANDLE hFind;

	searchPath.Sprintf( L"%s*.*", pDirPath );

	hFind = FindFirstFile( searchPath.GetConstPtr(), &wfd );
	if( hFind != INVALID_HANDLE_VALUE )
	{
		do
		{
			if( ( ::wcscmp( wfd.cFileName, L"." ) == 0 ) ||
				( ::wcscmp( wfd.cFileName, L".." ) == 0 ) )
			{
			}
			else if( MIX_TESTBIT( wfd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY )
			{
				Mix::String nextDirPath;
				nextDirPath.Sprintf( L"%s%s\\", pDirPath, wfd.cFileName );
				LoadPlugins( nextDirPath.GetConstPtr() );
			}
			else
			{
				Mix::StringW filePath;
				HMODULE hModule;

				filePath.Sprintf( L"%s%s", pDirPath, wfd.cFileName );

				hModule = LoadLibrary( filePath.GetConstPtr() );
				if( hModule != NULL )
				{
					void* pProcAddr = ::GetProcAddress( hModule, "GetInformation" );

					if( pProcAddr != NULL )
					{
						const Mix::Plugin::INFORMATION& info = static_cast<PPluginGetInformation>( pProcAddr )();
						Boolean bLoaded = False;
					
						MIX_ASSERT( ( sizeof( Engine::PLUGIN_TYPE_TEXT_TABLE ) / sizeof( wchar_t* ) ) > info.type );

						if( info.type == Mix::Plugin::TEXTURE_LOADER )
						{
							if( m_pGraphicsMgr != NULL )
							{
								MIX_ASSERT( m_pGraphicsMgr->GetDevicePtr() != NULL );

								if( m_pGraphicsMgr->GetDevicePtr()->LoadPlugin( info.type, hModule ) == True )
								{
									bLoaded = True;
								}
							}
						}
						else if( info.type == Mix::Plugin::SOUND_DECODER )
						{
							if( m_pSoundMgr != NULL )
							{
								if( m_pSoundMgr->LoadPlugin( info.type, hModule ) == True )
								{
									bLoaded = True;
								}
							}
						}

						if( bLoaded == True )
						{
							MIX_LOG_INFO( L"プラグインをロード : FilePath[%s] Type[%s] Name[%s] Version[%s]",
								filePath.GetConstPtr(), Engine::PLUGIN_TYPE_TEXT_TABLE[info.type], info.pName, info.pVersion );
						}
						else
						{
							::FreeLibrary( hModule );
						}
					}
				}
			}
		}
		while( FindNextFile( hFind, &wfd ) == TRUE );

		FindClose( hFind );
	}
}

void Engine::Dispose( void )
{
	if( m_pParallelMgr != NULL )
	{
		m_pParallelMgr->Dispose();
	}

	if( m_pSceneMgr != NULL )
	{
		m_pSceneMgr->Dispose();
	}

	if( m_pGraphicsMgr != NULL )
	{
		m_pGraphicsMgr->Dispose();
	}

	if( m_pHIDMgr != NULL )
	{
		m_pHIDMgr->Dispose();
	}
}

void Engine::Dispose_Init( void )
{
	Dispose();
	DisposeWindow();
}

Mix::IO::IManager* Engine::GetIOManagerPtr( void ) const
{
	return m_pIOMgr;
}

Mix::HID::IManager* Engine::GetHIDManagerPtr( void ) const
{
	return m_pHIDMgr;
}

Mix::Graphics::IManager* Engine::GetGraphicsManagerPtr( void ) const
{
	return m_pGraphicsMgr;
}

Mix::Sound::IManager* Engine::GetSoundManagerPtr( void ) const
{
	return m_pSoundMgr;
}

Mix::Dynamics::IManager* Engine::GetDynamicsManagerPtr( void ) const
{
	return m_pDynamicsMgr;
}

Mix::Parallel::IManager* Engine::GetParallelManagerPtr() const
{
	return m_pParallelMgr;
}

Mix::Scene::IManager* Engine::GetSceneManagerPtr( void ) const
{
	return m_pSceneMgr;
}

Mix::IO::Manager* Engine::GetInternalIOManagerPtr( void ) const
{
	return m_pIOMgr;
}

Mix::HID::Manager* Engine::GetInternalHIDManagerPtr( void ) const
{
	return m_pHIDMgr;
}

Mix::Graphics::Common::Manager* Engine::GetInternalGraphicsManagerPtr( void ) const
{
	return m_pGraphicsMgr;
}

Mix::Sound::Manager* Engine::GetInternalSoundManagerPtr( void ) const
{
	return m_pSoundMgr;
}

Mix::Dynamics::Manager* Engine::GetInternalDynamicsManagerPtr( void ) const
{
	return m_pDynamicsMgr;
}

Mix::Parallel::Manager* Engine::GetInternalParallelManagerPtr() const
{
	return m_pParallelMgr;
}

Mix::Scene::Common::Manager* Engine::GetInternalSceneManagerPtr( void ) const
{
	return m_pSceneMgr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Engine
////////////////////////////////////////////////////////////////////////////////////////////////////

Int32 Engine::GetBaseFPS( void ) const
{
	return m_BaseFPS;
}

Float32 Engine::GetBaseDT( void ) const
{
	return m_BaseET;
}

void Engine::ResetFPS( void )
{
	::QueryPerformanceFrequency( &m_Freq );
	m_InvFreqF64 = ( m_Freq.QuadPart > 0l )? ( 1.0 / static_cast<Float64>( m_Freq.QuadPart ) ) : 0.0;
	m_PeriodCount.QuadPart = ( m_BaseFPS > 0 )? ( m_Freq.QuadPart / m_BaseFPS ) : 0;
	::QueryPerformanceCounter( &m_BeforeCount );
	m_BeforeUpdateCount.QuadPart = m_BeforeCount.QuadPart;
	m_ErrorSleepCount.QuadPart = 0;
	m_SleepTimeCount = 0.0;
	m_InsomniaFrameCount = 0;
	m_FrameCount = 0;

	m_FPS = static_cast<Float32>( m_BaseFPS );
	m_ET = MIX_FLOAT_RECIPROCAL( m_FPS );
	m_STPS = 0.0;
	m_IFCPS = 0;
}

Float32 Engine::GetFPS( void ) const
{
	return m_FPS;
}

Float32 Engine::GetDT( void ) const
{
	return m_ET;
}

Float32 Engine::GetSleepTimePerSec( void ) const
{
	return static_cast<Float32>( m_STPS );
}

Int32 Engine::GetInsomniaFrameMax( void ) const
{
	return m_InsomniaFrameMax;
}

Int32 Engine::GetInsomniaFrameCountPerSec( void ) const
{
	return m_IFCPS;
}

HWND Engine::GetWindowHandle( void ) const
{
	return m_hWnd;
}

Mix::Point Engine::GetClientSize( void ) const
{
	RECT rect;

	if( ::GetClientRect( m_hWnd, &rect ) == FALSE )
	{
		return Mix::Point::Zero();
	}

	return Mix::Point( rect.right - rect.left, rect.bottom - rect.top );
}

Boolean Engine::IsActive( void ) const
{
	return m_bActive;
}

const wchar_t* Engine::GetCaption( void ) const
{
	return m_Caption.GetConstPtr();
}

void Engine::SetCaption( const wchar_t* pCaption )
{
	m_Caption = pCaption;
	::SetWindowTextW( m_hWnd, m_Caption.GetConstPtr() );
}

Boolean Engine::IsCloseEnabled( void ) const
{
	return m_bCloseEnabled;
}

void Engine::SetCloseEnabled( Boolean bEnable )
{
	HMENU hMenu = ::GetSystemMenu( m_hWnd, FALSE );
	if( hMenu != NULL )
	{
		::EnableMenuItem( hMenu, SC_CLOSE, ( bEnable == True )? MF_ENABLED : MF_GRAYED );
		m_bCloseEnabled = bEnable;
	}
}

void Engine::SetMessageProc( Mix::MessageProcPtr pMsgProc, void* pData )
{
	m_pMsgProcFunc = pMsgProc;
	m_pMsgProcData = pData;
}

Boolean Engine::Update( void )
{
	LARGE_INTEGER currentCount;
	LARGE_INTEGER periodCount;

	Boolean bLeaveLoop = True;
	Boolean bContinue = True;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// フレームレート
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_BaseFPS > 0 )
	{
		LARGE_INTEGER afterUpdateCount;
		LARGE_INTEGER diffUpdateCount;

		::QueryPerformanceCounter( &afterUpdateCount );
		diffUpdateCount.QuadPart = ( afterUpdateCount.QuadPart - m_BeforeUpdateCount.QuadPart ) + m_ErrorSleepCount.QuadPart;

		if( m_PeriodCount.QuadPart > diffUpdateCount.QuadPart )
		{
			LARGE_INTEGER sleepCount;
			UInt32 sleepTime;

			LARGE_INTEGER sleepBeginCount;
			LARGE_INTEGER sleepEndCount;
			LARGE_INTEGER sleepDiffCount;

			//スリープしたい時間を求める
			sleepCount.QuadPart = m_PeriodCount.QuadPart - diffUpdateCount.QuadPart;
			sleepTime = static_cast<UInt32>( sleepCount.QuadPart * 1000 / m_Freq.QuadPart );

			//スリープ
			::QueryPerformanceCounter( &sleepBeginCount );
			::Sleep( sleepTime );
			::QueryPerformanceCounter( &sleepEndCount );

			//実際にスリープした時間を求める
			sleepDiffCount.QuadPart = sleepEndCount.QuadPart - sleepBeginCount.QuadPart;

			//スリープ時間の誤差
			m_ErrorSleepCount.QuadPart = sleepDiffCount.QuadPart - sleepCount.QuadPart;

			//スリープ時間の合計
			m_SleepTimeCount += static_cast<Float64>( sleepDiffCount.QuadPart ) * m_InvFreqF64;
		}
		else
		{
			if( m_InsomniaFrameCount >= m_InsomniaFrameMax )
			{
				LARGE_INTEGER sleepBeginCount;
				LARGE_INTEGER sleepEndCount;

				//別スレッドにタイムスライスを譲る
				::QueryPerformanceCounter( &sleepBeginCount );
				::Sleep( 0 );
				::QueryPerformanceCounter( &sleepEndCount );

				//スリープ時間の誤差は無し
				m_ErrorSleepCount.QuadPart = 0;

				//スリープ時間の合計
				m_SleepTimeCount += static_cast<Float64>( sleepEndCount.QuadPart - sleepBeginCount.QuadPart ) * m_InvFreqF64;

				//スリープできなかったフレームのカウントをリセット
				m_InsomniaFrameCount = 0;
			}
			else
			{
				//スリープできなかったフレームのカウント
				m_InsomniaFrameCount++;
			}
		}

		::QueryPerformanceCounter( &m_BeforeUpdateCount );
	}

	m_FrameCount++;

	::QueryPerformanceCounter( &currentCount );
	periodCount.QuadPart = currentCount.QuadPart - m_BeforeCount.QuadPart;

	if( periodCount.QuadPart >= m_Freq.QuadPart )
	{
//		MIX_TRACELINE( L"Process : SleepTimeCount[%f] InsomniaCount[%d]", m_SleepTimeCount, m_InsomniaFrameCount );

		m_FPS = MIX_FLOAT_DIV( static_cast<Float32>( m_FrameCount ), static_cast<Float32>( currentCount.QuadPart - m_BeforeCount.QuadPart ) ) * m_Freq.QuadPart;
		m_ET = MIX_FLOAT_RECIPROCAL( m_FPS );
		m_STPS = m_SleepTimeCount;
		m_IFCPS = m_InsomniaFrameCount;

		::QueryPerformanceFrequency( &m_Freq );
		m_InvFreqF64 = ( m_Freq.QuadPart > 0l )? ( 1.0 / static_cast<Float64>( m_Freq.QuadPart ) ) : 0.0;
		m_PeriodCount.QuadPart = ( m_BaseFPS > 0 )? ( m_Freq.QuadPart / m_BaseFPS ) : 0;

		m_SleepTimeCount = 0.0;
		m_InsomniaFrameCount = 0;
		m_FrameCount = 0;

		::QueryPerformanceCounter( &m_BeforeCount );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 並列
	////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
	if( m_pParallelMgr != NULL )
	{
		m_pParallelMgr->Debug_Update();
	}
#endif //_DEBUG

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ウィンドウ、グラフィックスの更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	do
	{
		if( UpdateWindow() == True )
		{
			if( m_pGraphicsMgr != NULL )
			{
				bLeaveLoop = m_pGraphicsMgr->Update();
			}
		}
		else
		{
			bContinue = False;
		}
	}
	while( ( bLeaveLoop == False ) && ( bContinue == True ) );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイル、インプット、シーンの更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( bContinue == True )
	{
		if( m_pHIDMgr != NULL )
		{
			m_pHIDMgr->Update();
		}

		if( m_pSceneMgr != NULL )
		{
			m_pSceneMgr->Update( m_ET );
		}
	}

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

	return bContinue;
}

void Engine::Shutdown( void )
{
	::PostMessageW( m_hWnd, WM_CLOSE, 0, 0 );
}

}
