const uint32 ticksPerSecond = 1000;

class cTimer
{
private:

	bool					isClassInit;

	uint32					framesPerSecond;
	float32					hzPerSecondTicks;
	uint64					hzPerSecondRdtsc;

	static uint64			rdtscPerMillesecond;
	static uint64			rdtscPerSecond;
	static bool				usePerformanceCounter;
	
	uint64					startTimeKeyRdtsc;
	ulong32					startTimeKeyTicks;
	uint64					startTimeKeyRdtscFPS;
	ulong32					startTimeKeyTicksFPS;
	uint64					startTimeKeyFPS;
	float32					frameCount;
	float32					timeElapsed;
	uint64					timeKeyRdtsc;
	ulong32					timeKeyTicks;
	uint64					timeDifferenceRdtsc;
	float32					timeDifferenceTicks;
	uint64					timeRdtsc;
	ulong32					timeTicks;

public:
	cTimer( )
	{
		isClassInit = false;
	}
	
	~cTimer( )
	{
	}

	void TimerInit( uint32 );
	const float32& TimerFPS( );
	bool TimerAlarm( );
	void TimerElapsedTime( ulong32* , uint64* = NULL );
};

_INLINE bool cTimer::TimerAlarm( )
{
	if( !isClassInit )
	{
		//ASSERTPRINT( isClassInit != false );
		return false;	
	}

	if( framesPerSecond )
	{
		if( usePerformanceCounter )
		{
			TimerElapsedTime( NULL, &timeKeyRdtsc );
			
			timeDifferenceRdtsc = timeKeyRdtsc - startTimeKeyRdtsc;

			if( timeDifferenceRdtsc >= hzPerSecondRdtsc )
			{
				startTimeKeyRdtsc = timeKeyRdtsc;
				return true;
			}
			else
				return false;
		}
		else
		{
			TimerElapsedTime( &timeKeyTicks );
			
			if( timeKeyTicks < startTimeKeyTicks )
				startTimeKeyTicks = 0;
			
			timeDifferenceTicks = (float32)(timeKeyTicks - startTimeKeyTicks);

			if( timeDifferenceTicks >= hzPerSecondTicks )
			{
				startTimeKeyTicks = timeKeyTicks;
				return true;
			}
			else
				return false;
		}
	}
	else
		return true;
}

_INLINE const float32& cTimer::TimerFPS( )
{
	if( !isClassInit )
	{
		//ASSERTPRINT( isClassInit != false );
		return frameCount;	
	}

	if( usePerformanceCounter )
	{
		TimerElapsedTime( NULL, &timeKeyRdtsc );
		
		timeDifferenceRdtsc = timeKeyRdtsc - startTimeKeyRdtscFPS;

		if( timeDifferenceRdtsc == 0 )
		{
			//ASSERTPRINT( timeDifferenceRdtsc == 0 );
			timeDifferenceRdtsc = 1;
		}

		frameCount = (float32)( rdtscPerSecond / timeDifferenceRdtsc );

		startTimeKeyRdtscFPS = timeKeyRdtsc;
	}
	else
	{
		TimerElapsedTime( &timeKeyTicks );
		
		if( timeKeyTicks < startTimeKeyTicksFPS )
			startTimeKeyTicksFPS = 0;
		
		timeDifferenceTicks = (float32)( timeKeyTicks - startTimeKeyTicksFPS );
		
		if( timeDifferenceTicks < FLOAT32_EPSILON )
		{
			//ASSERTPRINT( timeDifferenceTicks >= FLOAT32_EPSILON );
			timeDifferenceTicks = FLOAT32_EPSILON;
		}

		frameCount = (float32)ticksPerSecond / timeDifferenceTicks;

		startTimeKeyTicksFPS = timeKeyTicks;
		
	}
	return frameCount;
}

_INLINE void cTimer::TimerElapsedTime( ulong32* timeTicks, uint64* timeRdtsc )
{
	if( !isClassInit )
	{
		//ASSERTPRINT( isClassInit != false );
		return;	
	}

	if( timeRdtsc && usePerformanceCounter )
	{		
		ulong32 dwLow, dwHigh;
		__asm
		{
			rdtsc
			mov dwLow, eax
			mov dwHigh, edx
		}
		*timeRdtsc = ( (uint64)dwHigh << 32 ) | (uint64)dwLow;
	}
	
	if( timeTicks )
	{
		timeBeginPeriod( 1 );
		*timeTicks = timeGetTime( );
		timeEndPeriod( 1 );
	}
}

uint64	cTimer::rdtscPerMillesecond		= 0;
uint64	cTimer::rdtscPerSecond			= 0;
bool	cTimer::usePerformanceCounter	= false;

void cTimer::TimerInit( uint32 fps )
{
	if( isClassInit )
	{
		//ASSERTPRINT( isClassInit != true );
		return;	
	}
	isClassInit = true;	

	static bool isInit = false;
	
	if( !isInit )
	{
		__try
		{
			ulong32 dwLow1, dwHigh1;
			ulong32 dwLow2, dwHigh2;
			uint64	time;
			uint64	time2;
			__asm
			{
				rdtsc
				mov dwLow1, eax
				mov dwHigh1, edx
			}
			
			Sleep( 50 );

			__asm
			{
				rdtsc
				mov dwLow2, eax
				mov dwHigh2, edx
			}

			time = ( (unsigned __int64)dwHigh1 << 32 ) | (unsigned __int64)dwLow1;
			time2 = ( (unsigned __int64)dwHigh2 << 32 ) | (unsigned __int64)dwLow2;
			time2 -= time;

			
			rdtscPerSecond = time2;
			
			__asm
			{
				rdtsc
				mov dwLow1, eax
				mov dwHigh1, edx
			}
			
			Sleep( 50 );

			__asm
			{
				rdtsc
				mov dwLow2, eax
				mov dwHigh2, edx
			}

			time = ( (unsigned __int64)dwHigh1 << 32 ) | (unsigned __int64)dwLow1;
			time2 = ( (unsigned __int64)dwHigh2 << 32 ) | (unsigned __int64)dwLow2;
			time2 -= time;
				
			rdtscPerSecond += time2;
			rdtscPerSecond *= 10;
			rdtscPerMillesecond = rdtscPerSecond / (uint64)ticksPerSecond;
		
			usePerformanceCounter = true;
			DebugMsg( "Accurate rdtsc Timer Activated" );
			LogEntry( "Accurate rdtsc Timer Activated\n\n");

		}
		__except( EXCEPTION_EXECUTE_HANDLER )
		{
			usePerformanceCounter = false;
			DebugMsg( "TimeGetTime Timer Activated" );
			LogEntry( "TimeGetTime Timer Activated\n\n");
		}
		isInit = true;
	}	
	
	framesPerSecond = fps;

	if( framesPerSecond )
	{
		hzPerSecondTicks = (float32)ticksPerSecond/(float32)framesPerSecond;

		if( usePerformanceCounter )
			hzPerSecondRdtsc = rdtscPerSecond / framesPerSecond;
	}

	TimerElapsedTime( &startTimeKeyTicks, &startTimeKeyRdtsc );
	startTimeKeyTicksFPS = startTimeKeyTicks;
	startTimeKeyRdtscFPS = startTimeKeyRdtsc;
}
