#include "StdAfx.h"
#include "PreciseTimer.h"

using namespace System::Threading;

PreciseTimer::PreciseTimer(void)
{

}

PreciseTimer::PreciseTimer(Int64 IntervalInMicrosec)
{
	_threadTimer = nullptr;
	_ignoreEventIfLateBy = Int64::MaxValue;
	_timerIntervalInMicroSec = 0;
	_stopTimer = true;

	Interval = IntervalInMicrosec;
}
/*------------------------------------------------------*/
Int64 PreciseTimer::Interval::get()
{
	return _timerIntervalInMicroSec;
}

void PreciseTimer::Interval::set(Int64 _value)
{
	_timerIntervalInMicroSec = _value;	
}

/*------------------------------------------------------*/
/*------------------------------------------------------*/
Int64 PreciseTimer::IgnoreEventIfLateBy::get()
{
	return _ignoreEventIfLateBy;
}

void PreciseTimer::IgnoreEventIfLateBy::set(Int64 _value)
{
	if (_value <= 0)
		_ignoreEventIfLateBy = Int64::MaxValue;
	else
		_ignoreEventIfLateBy = _value;
}
/*------------------------------------------------------*/
/*------------------------------------------------------*/
bool PreciseTimer::Enabled::get()
{
	return (_threadTimer != nullptr) && _threadTimer->IsAlive;
}

void PreciseTimer::Enabled::set(bool _value)
{
	if (_value)
		Start();
	else
		Stop();
}
/*-----------------------------------------------------*/

void PreciseTimer::Start(void)
{
	if (Enabled || Interval <= 0)
		return;

	_stopTimer = false;

	ThreadStart^ threadStart = gcnew ThreadStart(this,&PreciseTimer::NotificationTimer);
	_threadTimer = gcnew Thread(threadStart);
	_threadTimer->Priority = ThreadPriority::Highest;
	_threadTimer->Start();
}

void PreciseTimer::Stop(void)
{
	_stopTimer = true;

	if (_threadTimer->ManagedThreadId == System::Threading::Thread::CurrentThread->ManagedThreadId)
	{
		return;
	}

	_threadTimer->Abort();
	while(Enabled)
		System::Threading::Thread::SpinWait(10);
	delete _threadTimer;
}

//void PreciseTimer::NotificationTimer(Int64 timerInterval, Int64 ignoreEventIfLateBy, bool stopTimer)
void PreciseTimer::NotificationTimer(void)
{
	int timerCount = 0;
	Int64 nextNotification = 0;

	MicroStopwatch^ microStopwatch = gcnew MicroStopwatch();
	microStopwatch->Start();

	while(!_stopTimer)
	{
		Int64 callbackFunctionExecutionTime = microStopwatch->ElapsedMicroseconds - nextNotification;
		nextNotification += Interval;
		timerCount++;
		Int64 elapsedMicroseconds = 0;

		while( (elapsedMicroseconds = microStopwatch->ElapsedMicroseconds) < nextNotification)
		{
			System::Threading::Thread::SpinWait(10);
		}

		Int64 timerLateBy = elapsedMicroseconds - (timerCount * Interval);

		if (timerLateBy >= IgnoreEventIfLateBy)
		{
			continue;
		}

		PreciseTimerEventArgs^ microTimerEventArgs = gcnew PreciseTimerEventArgs(timerCount, elapsedMicroseconds, timerLateBy, callbackFunctionExecutionTime);
		PreciseTimerElapsed(this, microTimerEventArgs);
	}
	microStopwatch->Stop();
}