// TimerFunc.h: CTimerFunc
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_TIMERFUNC_H__4378FAC7_2308_438C_8845_B67541994548__INCLUDED_)
#define AFX_TIMERFUNC_H__4378FAC7_2308_438C_8845_B67541994548__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
class CTimerFunc  
{
public:
	CTimerFunc();
	virtual ~CTimerFunc();
#ifdef _WIN64
	static void CALLBACK TimeProc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
#else
	static void CALLBACK TimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
#endif //_WIN64
	{
		((CTimerFunc*)dwUser)->TimerProc();
	};
	
	BOOL StopTimer()
	{
		BOOL bReval = TRUE;
		WaitForSingleObject(m_hTimerIDMutex, INFINITE);
		bReval = mStopTimer();
		ReleaseMutex(m_hTimerIDMutex);
		return(bReval);
	};
	BOOL StartTimer()
	{
		BOOL bReval = TRUE;
		WaitForSingleObject(m_hTimerIDMutex, INFINITE);
		if(0 != m_TimerID)
		{
			mStopTimer();
		}
		bReval = mStartTimer();
		ReleaseMutex(m_hTimerIDMutex);
		return(bReval);
	};
	DWORD GetRestTime()
	{
		DWORD dwRestTime = 0;

		if(m_bRunning)
		{
			DWORD dwCurTime = timeGetTime();
			if(dwCurTime < m_dwEndTime)	dwRestTime = m_dwEndTime - dwCurTime;
			else
			{
				dwRestTime = 0xFFFFFFFF - dwCurTime;
				dwRestTime += m_dwEndTime + 1;
			}
		}

		return(dwRestTime);
	};
	
	void SetPeriod(UINT Delay, UINT Resolution);
	void GetPeriod(PUINT pDelay, PUINT pResolution);
	
	virtual BOOL TimerStartProc() = 0;
	virtual BOOL TimerProc()
	{
		if(0 < m_dwRest10SecCount)
		{
			m_dwRest10SecCount--;
			return(TRUE);
		}
		BOOL bReval = StopTimer();
		return(bReval);
	};
	virtual BOOL TimerStopProc() = 0;

private:
	BOOL	mStartTimer()
	{
		UINT Delay = m_Delay;

		if(100000 < Delay)
		{
			m_dwRest10SecCount = m_Delay / 1000;
			if(500 < (m_Delay % 1000))
			{
				m_dwRest10SecCount++;
			}
			Delay = 1000;
		}
		else
		{
			m_dwRest10SecCount = 0;
		}

		m_TimerID = timeSetEvent(
			Delay,
			m_Resolution,
			CTimerFunc::TimeProc,
#ifdef _WIN64
			(DWORD_PTR)this,
#else
			(DWORD)this,
#endif //_WIN64
			((0 == m_dwRest10SecCount)?TIME_ONESHOT:TIME_PERIODIC) | TIME_CALLBACK_FUNCTION);
		if(0 == m_TimerID)
		{
			return(FALSE);
		}
		BOOL bReval = TimerStartProc();
		m_dwEndTime = timeGetTime() + m_Delay;
		m_bRunning = TRUE;
		return(bReval);
	};
	BOOL	mStopTimer()
	{
		BOOL bReval = TRUE;
		if(0 != m_TimerID)
		{
			bReval = TimerStopProc();
			timeKillEvent(m_TimerID);
			m_TimerID = 0;
			m_bRunning = FALSE;
			m_dwEndTime = 0;
		}
		return(bReval);
	};


	UINT	m_TimerID;
	
	UINT	m_Delay;
	UINT	m_Resolution;
	HANDLE	m_hTimerIDMutex;

	BOOL	m_bRunning;
	DWORD	m_dwEndTime;
	DWORD	m_dwRest10SecCount;
};

#endif // !defined(AFX_TIMERFUNC_H__4378FAC7_2308_438C_8845_B67541994548__INCLUDED_)
