#include "CObjTimer.h"

#ifndef SCOL

long CObjTimer::IDcount = 0;

void CALLBACK CObjTimerCallback( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2 )
{
	CObjTimer *Timer = (CObjTimer *)dwUser;
	TimedElement *expired = Timer->WatchedElements;

	// on relance l'evt suivant
	Timer->StopTimer();
	Timer->lastTick = timeGetTime();
	Timer->WatchedElements = expired->next;
	expired->object->Timer( expired->ID );
	Timer->WatchedElements = Timer->AddEvent( Timer->WatchedElements, expired, Timer->lastTick, Timer->lastTick, 0 );
	Timer->StartTimer( Timer->WatchedElements->frequency );
}

// timer associated functions

int CObjTimer::StartTimer( UINT delay )
{
	if ( this->timerID == NO_TIMER && delay != 0 )
		this->timerID = timeSetEvent( delay, TIMER_RESOLUTION, CObjTimerCallback, (DWORD)this, TIME_ONESHOT );

	return this->timerID;
}

int CObjTimer::StopTimer( )
{
	int res = -1;

	if ( this->timerID != NO_TIMER )
	{
		res = timeKillEvent( this->timerID );
		this->timerID = NO_TIMER;
	}

	return res == 0;
}

// end of timer associated functions

#else
void releaseTimers( TimersList *list )
{
	if ( list != NULL )
	{
		mt_del( list->id );
		releaseTimers( list->next );
		free( list );
	}
		
}

#endif


CObjTimer::CObjTimer ( )
{
	this->WatchedElements = NULL;
#ifndef SCOL
	this->lastTick = 0;
	this->timerID = NO_TIMER;
	this->ElementsCount = 0;
#else
	this->list = NULL;
#endif
}

void freeWatchedElements( TimedElement *elts )
{
	if ( elts != NULL )
	{
		freeWatchedElements( elts->next );
#ifdef SCOL
		mt_del( elts->ID );
#endif
		free( elts );
	}
}

CObjTimer::~CObjTimer ()
{
#ifndef SCOL
	StopTimer();
#else
	releaseTimers( this->list );
#endif
	freeWatchedElements( this->WatchedElements );
}

#ifndef SCOL

TimedElement *CObjTimer::AddEvent( TimedElement * eltsList, TimedElement *elt, DWORD currentTime, DWORD lastTime, int flushTimer )
{
	if ( eltsList == NULL )
	{
		if ( flushTimer )
			StartTimer( elt->frequency );
		return elt;
	}
	
	if ( lastTime + eltsList->frequency < currentTime + elt->frequency ) // le nouvel evt tombe avant le prochain prevu
	{
		if ( flushTimer )
		{
			StopTimer();
			StartTimer( elt->frequency );
		}
		elt->next = eltsList;
		return elt;
	}
	else
	{
		eltsList->next = AddEvent( eltsList->next, elt, currentTime, lastTime, 0 );
		return eltsList;
	}
}

TimedElement *CObjTimer::RemoveEvent( TimedElement * eltsList, int eltID, int flushTimer )
{
	TimedElement *result;

	if ( eltsList == NULL )
		return NULL;

	if ( eltsList->ID == eltID )
	{
		result = eltsList->next;
		free( eltsList );
		if ( flushTimer && result == NULL )
			StopTimer();
		this->ElementsCount--; // on ne peut pas diminuer le compteur d'identifiants !!
		return result;
	}
	else
	{
		eltsList->next = RemoveEvent( eltsList->next, eltID, 0 );
		return eltsList;
	}
}

int CObjTimer::Register( int frequency, CObjectBase *object )
{
	TimedElement *newElement = (TimedElement *)malloc( sizeof( TimedElement ) );

	newElement->frequency = frequency;
	newElement->next = NULL;
	newElement->object = object;
	newElement->ID = this->IDcount;
	this->IDcount++;
	this->ElementsCount++;

	this->WatchedElements = AddEvent( this->WatchedElements, newElement, timeGetTime(), this->lastTick, 1 );
	return newElement->ID;
}

int CObjTimer::Unregister( int ID )
{
	short relaunchTimer = 0;
	int ElementsNber = this->ElementsCount;

	if ( this->WatchedElements == NULL )
		return -1;

	if ( this->WatchedElements->ID == ID && this->WatchedElements->next != NULL )
		relaunchTimer = 1;
	this->WatchedElements = RemoveEvent( this->WatchedElements, ID, 1 );

	if ( relaunchTimer )
	{
		StopTimer();
		StartTimer( this->WatchedElements->frequency );
	}

	if ( ElementsNber == this->ElementsCount + 1 )
		return 0;
	else
		return -1;
}

#else

int CObjTimerCallback( int i, int param )
{
	return ((CObjectBase *)param)->Timer( i );
}

TimedElement *CObjTimer::AddEvent( TimedElement * eltsList, TimedElement *elt )
{
	if ( elt == NULL )
		return eltsList;

	elt->next = eltsList;

	return elt;
}

TimedElement *CObjTimer::RemoveEvent( TimedElement * eltsList, int eltID )
{
	TimedElement *result;

	if ( eltsList == NULL ) return NULL;

	if ( eltsList->ID == eltID )
	{
		result = eltsList->next;
		free( eltsList );
		return result;
	}
	else
	{
		eltsList->next = RemoveEvent( eltsList->next, eltID );
		return eltsList;
	}
}

TimersList *addTimersList( TimersList *list, int id )
{
	TimersList *elt = (TimersList *)malloc( sizeof( TimersList ) );

	elt->id = id;
	elt->next = list;
	return elt;
}

TimersList *remTimersList( TimersList *list, int id )
{
	TimersList *result;

	if ( list == NULL ) return NULL;

	if ( list->id == id )
	{
		result = list->next;
		free( list );
		return result;
	}
	else
	{
		list->next = remTimersList( list->next, id );
		return list;
	}
}

int CObjTimer::Register( int frequency, CObjectBase *object )
{
	TimedElement *newElement;
	int timerID;

	timerID = mt_start( frequency, (int)object, CObjTimerCallback );

	if ( timerID != -1 )
	{
		newElement = (TimedElement *)malloc( sizeof( TimedElement ) );
		newElement->next = NULL;
		newElement->ID = timerID;
		this->WatchedElements = AddEvent( this->WatchedElements, newElement );
		this->list = addTimersList( this->list, newElement->ID );
	}
	else
		MMechostr( MSKTRACE, "CObjTimer: Timer creation failed\n" );


	return timerID;
}

int CObjTimer::Unregister( int ID )
{
	if ( this->WatchedElements == NULL || ID == -1 )
		return -1;

	this->WatchedElements = RemoveEvent( this->WatchedElements, ID );
	this->list = remTimersList( this->list, ID );

	return( mt_del( ID ) );
}

#endif