/***************************************************************************/
/** @file       app_dx.cpp
    @brief      
    @author     shom
****************************************************************************/

#include "pch_core.h"

#include "core/app.h"

#include "core/window.h"

#include "core/graphic/render_dx.h"

#ifdef _DEBUG
#include "core/debug/fps_counter/fps_counter.h"
#endif


/***************************************************************************
	app_private( namespace )
****************************************************************************/

namespace app_private
{
	void	game_loop_init();
	void	game_loop_fin();
	void	proc_msg_and_wait( const u32 tm_wait = 0 );
	void	game_loop_exec( app::fnGameLoopExec in_fnExec );
	void	game_loop_draw( app::fnGameLoopDraw in_fnDraw );
}

namespace
{
	TIMECAPS caps = {0};

	b32 b_game_loop = TRUE;

	u32	tm_start = 0;
	u32 tm_elapsed_on_frame = 0;
	u32 tm_elapsed_from_boot = 0;

	HWND h_wnd;
	HINSTANCE h_inst;
}


void	app::AppInit( HINSTANCE in_h_inst, fnGameInit in_fnGameInit, int nCmdShow )
{
#ifdef _DEBUG
	debug_dip::InitDip();
#endif

	h_wnd = window::InitApp( in_h_inst, nCmdShow );
	h_inst = in_h_inst;

	///--	set to check the memory leak
	_CrtSetDbgFlag(
		_CRTDBG_ALLOC_MEM_DF |	//メモリリークチェック用のヒープ使用
		_CRTDBG_LEAK_CHECK_DF	//プログラム終了時にメモリリークチェック
		);
	///--

	///--	ハードウェアの最小分解能で設定
	timeGetDevCaps( &caps, sizeof( TIMECAPS ) );	//性能取得
	timeBeginPeriod( caps.wPeriodMin );				//設定
	///--

	///--
	g_p_render = new crender();

	if( g_p_render->Initialize( h_wnd ) )
	{
		(in_fnGameInit)( h_wnd, h_inst );

#ifdef _DEBUG
		fps_counter::fps_counter_init();
#endif
	}
	else
	{
		app::StopAppLoop();
	}
	///--
}

int		app::AppFin( fnGameFin in_fnGameFin )
{
	///--
	(in_fnGameFin)();

	g_p_render->Finalize();
	my_safe_del( g_p_render );
	///--

	///--
	timeEndPeriod( caps.wPeriodMin );
	///--

	///--
	const b32 bRet ( window::FinApp( h_wnd, h_inst ) );
	///--

	///--
#ifdef _DEBUG
	debug_dip::FinDip();
#endif
	///--

	return bRet;
}

void	app::AppLoop( fnGameLoopExec in_fnExec, fnGameLoopDraw in_fnDraw )
{
    while( b_game_loop )
	{
		app_private::game_loop_init();

		///--	ゲーム処理
		app_private::game_loop_exec( in_fnExec );
		///--

		if( !b_game_loop ) { break; }

		///--	描画
		app_private::game_loop_draw( in_fnDraw );
		///--

		app_private::game_loop_fin();
    }
}

void	app_private::game_loop_init()
{
	tm_start = get_time();
}

void	app_private::game_loop_fin()
{
	///--	フレーム待ち
	const u32 tm_exec_end = get_time();
	const u32 tm_exec_elapsed = tm_exec_end - tm_start;

	if( app::g_kSec / app::g_kFps > tm_exec_elapsed )
	{
		proc_msg_and_wait( app::g_kSec / app::g_kFps - tm_exec_elapsed - 1/*小数点切り下げ還元*/ );
	}
	else
	{
		//メッセージ処理だけ
		proc_msg_and_wait();
	}
	///--

	const u32 tm_frame_end = get_time();
	tm_elapsed_on_frame = tm_frame_end - tm_start;
	tm_elapsed_from_boot += tm_elapsed_on_frame;
}

void	app_private::proc_msg_and_wait( const u32 tm_wait )
{
	struct local
	{
		static void proc_msg()
		{
			MSG msg;
			my_memzero( (&msg), sizeof( msg ) );
			if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
			//PM_NOREMOVEとして、Win9x/Me系OSでのプロセスのゾンビ化を防ぐ
			{
				if( GetMessage( &msg, NULL, 0, 0 ) )
				{
					TranslateMessage( &msg );
					DispatchMessage( &msg );
				}
				else
				{
					msg.message = WM_QUIT;
				}
			}
		}
	};

	if( tm_wait > 0 )
	{
		const u32 tm_start_to_wait = get_time();

		u32	tm_waiting	= 0;
		while(
			tm_waiting = get_time() - tm_start_to_wait,
			tm_waiting <= tm_wait
			)
		{
			///メッセージ処理
			local::proc_msg();

			///CPUをあける
			Sleep(1);
		}
	}
	else
	{
		local::proc_msg();
	}
}

void	app_private::game_loop_exec( app::fnGameLoopExec in_fnExec )
{
	using namespace window;
	using namespace debug_dip;	

	if(
#ifdef _DEBUG
		( IsDip( eDebugDipItem_PROC_ONLY_FOCUSING ) ? IsWindowActive() : TRUE ) &&
#endif
		!IsMessageBoxAppearing()
		)
	{
		if( (in_fnExec)() )
		{
			app::StopAppLoop();
		}
	}

#ifdef _DEBUG
	fps_counter::fps_counter_update();
#endif
}

void	app_private::game_loop_draw( app::fnGameLoopDraw in_fnDraw )
{
	if( g_p_render->BeginDraw() )
	{
		(in_fnDraw)();

		VERIFY( g_p_render->EndDraw() );
	}
	else
	{
		DEBUG_BREAK();
	}
}

void	app::StopAppLoop()
{
	b_game_loop = FALSE;
}

u32		app::GetTimeElapsedOnFrame()
{
	return tm_elapsed_on_frame;
}

u32		app::GetTimeElapsedFromBoot()
{
	return tm_elapsed_from_boot;	
}

