/*=========================================================================
*
* DEVTimer.hpp (PRIVATE HEADER):  class DEV_Timer
*
*
* Version			1.0
* Creation date         97/01/15
* Revision date
*
* Benoit Germain
*=======================================================================*/
#ifdef ACTIVE_EDITOR

#include "stdafx.h"

#ifndef __DEVTIMER_HPP__
#define __DEVTIMER_HPP__

#include "acp_base.h"

//**************************************
#ifndef CPA_EXPORT
#if defined(CPA_WANTS_IMPORT)
#define CPA_EXPORT __declspec(dllimport)
#elif defined(CPA_WANTS_EXPORT)
#define CPA_EXPORT __declspec(dllexport)
#else
#define CPA_EXPORT
#endif
#endif
//**************************************

/////////////////////////////////////////////////////////////////////////////
// DEV_TimerXXX class
/////////////////////////////////////////////////////////////////////////////

#define C_ulDefaultTimerElapse 100

//pointer to a function of the timer vector array of handlers
typedef void (*tdfn_vTimerVector)(void *);
template <class CWndDerivedClass>
class clVectorNode
{
 //friend class CWndDerivedClass;
 friend class DEV_Timer<CWndDerivedClass>;

private:
 tdfn_vTimerVector m_fn_vVectorHandler;
 CWndDerivedClass *m_p_oOwner;
 //tdfn_vTimerVector fn_vVectorHandler;
 clVectorNode *m_p_oNext;

public:
 void m_vCallVector() { if ( m_fn_vVectorHandler ) m_fn_vVectorHandler(m_p_oOwner); };
 clVectorNode *m_p_oGetNext() { return m_p_oNext; };
};

template <class CWndDerivedClass>
class DEV_Timer
{
 friend class CWndDerivedClass;

public:
 DEV_Timer(CWndDerivedClass *p_oOwner, unsigned long ulMilliseconds = C_ulDefaultTimerElapse);
 ~DEV_Timer();
 void m_vAddHandler(CWndDerivedClass *p_oOwner, tdfn_vTimerVector fn_vVectorHandler);
 BOOL m_bRemoveHandler(tdfn_vTimerVector fn_vVectorHandler);
 void m_vRestartTimer(unsigned long ulMilliseconds = C_ulDefaultTimerElapse);
 void m_vSuspendTimer();
 void m_vOnTimer(CWndDerivedClass *p_oOwner);

protected:

private:
 clVectorNode<CWndDerivedClass> *m_p_oGetVectorHead() { return m_p_oVectorHead; };
 unsigned int m_uiTimerId;
 clVectorNode<CWndDerivedClass> *m_p_oVectorHead;
 CWndDerivedClass *m_p_oOwner;
};

/*===========================================================================
  constructor / destructor
=========================================================================*/

template <class CWndDerivedClass>
DEV_Timer<CWndDerivedClass>::DEV_Timer<CWndDerivedClass>(CWndDerivedClass *p_oOwner, unsigned long ulMilliseconds)
{
 if ( m_p_oOwner = p_oOwner )
 {
  m_p_oVectorHead = NULL;
  // no timer yet
  m_uiTimerId = 0;
  // start it
  m_vRestartTimer(ulMilliseconds);
 }
}

/*===========================================================================*/

template <class CWndDerivedClass>
DEV_Timer<CWndDerivedClass>::~DEV_Timer<CWndDerivedClass>()
{
 if ( m_p_oOwner )
 {
  // stop the timer event queue processing
  m_vSuspendTimer();

  // remove all the handler nodes
  while ( m_p_oVectorHead && m_bRemoveHandler(m_p_oVectorHead->m_fn_vVectorHandler));
 }
}

/*===========================================================================
  stop the timer
=========================================================================*/

template <class CWndDerivedClass>
void DEV_Timer<CWndDerivedClass>::m_vSuspendTimer()
{
 if ( m_p_oOwner )
 {
  if ( m_uiTimerId && m_p_oOwner->KillTimer(m_uiTimerId) )
   m_uiTimerId = 0;
 }
}

/*===========================================================================
  start the timer
=========================================================================*/

template <class CWndDerivedClass>
void DEV_Timer<CWndDerivedClass>::m_vRestartTimer(unsigned long ulMilliseconds)
{
 if ( m_p_oOwner )
 {
  // stop the timer before restarting in case this is just a timeout change
  m_vSuspendTimer();
  if ( ulMilliseconds )
   m_uiTimerId = m_p_oOwner->SetTimer(1, ulMilliseconds, NULL);
 }
}


/*===========================================================================
  add a handler to the list of handlers
=========================================================================*/

template <class CWndDerivedClass>
void DEV_Timer<CWndDerivedClass>::m_vAddHandler(CWndDerivedClass *p_oOwner, tdfn_vTimerVector fn_vVectorHandler)
{
 //TODO-BBB: I hope that no OnTimer scanning method is currently scanning the list...
 if ( m_p_oOwner && m_p_oOwner == p_oOwner )
 {
  // remove the handler if it already existed
  m_bRemoveHandler(fn_vVectorHandler);
  if ( fn_vVectorHandler )
  {
   //allocate a new node
   clVectorNode<CWndDerivedClass> *p_oVectorNode = new clVectorNode<CWndDerivedClass>;
   if ( p_oVectorNode )
   {
    clVectorNode<CWndDerivedClass> *p_oOldHead = m_p_oVectorHead;

    // set it
    p_oVectorNode->m_fn_vVectorHandler = fn_vVectorHandler;
    p_oVectorNode->m_p_oOwner = p_oOwner;
    // insert it in the list
    m_p_oVectorHead = p_oVectorNode;
    m_p_oVectorHead->m_p_oNext = p_oOldHead;
   }
  }
 }
}

/*===========================================================================
  remove a handler from the queue if it exists,
  returns TRUE if a handler is removed, else FALSE
=========================================================================*/

template <class CWndDerivedClass>
BOOL DEV_Timer<CWndDerivedClass>::m_bRemoveHandler(tdfn_vTimerVector fn_vVectorHandler)
{
 BOOL bRetVal = FALSE;

 if ( m_p_oOwner && m_p_oVectorHead )
 {
  clVectorNode<CWndDerivedClass> *p_oCurrentNode = m_p_oVectorHead;
  clVectorNode<CWndDerivedClass> *p_oPrevNode = m_p_oVectorHead;

  // stalk the list, stop when we find a handler that is ours
  while ( p_oCurrentNode )
  if ( p_oCurrentNode->m_fn_vVectorHandler == fn_vVectorHandler )
  {
   if ( p_oPrevNode == p_oCurrentNode ) // if we remove the head
    m_p_oVectorHead = p_oCurrentNode->m_p_oNext; // make the head point to the next item
   else
    p_oPrevNode->m_p_oNext = p_oCurrentNode->m_p_oNext; // make the list skip the found node
   // destroy the node
   delete p_oCurrentNode;
   p_oCurrentNode = NULL;
   bRetVal = TRUE;
  }
  else
  {
   p_oPrevNode = p_oCurrentNode;
   p_oCurrentNode = p_oCurrentNode->m_p_oNext;
  }
 }
 return bRetVal;
}

template <class CWndDerivedClass>
void DEV_Timer<CWndDerivedClass>::m_vOnTimer(CWndDerivedClass *p_oOwner)
{
 clVectorNode<CWndDerivedClass> *p_oCurrentNode = m_p_oGetVectorHead();

 // if the world, the event editor and the current editor agree to it
 if
 (
     p_oCurrentNode
  && M_VGetParentMultiDevice3DOf(p_oOwner)->GetWorld()->fn_bAcceptToRunTimerEngine()
  && M_VGetParentMultiDevice3DOf(p_oOwner)->GetInterface()->GetMainWorld()->GetCurrentEditor()->fn_bAcceptToRunTimerEngine()
  && M_VGetParentMultiDevice3DOf(p_oOwner)->GetInterface()->GetEditor()->fn_bAcceptToRunTimerEngine()
 )
 // call all the handlers is sequence
 while( p_oCurrentNode )
 {
  p_oCurrentNode->m_vCallVector();
  p_oCurrentNode = p_oCurrentNode->m_p_oGetNext();
 }
}

#undef TIMERCLASS_TEMPLATE

#endif //__DEVTIMER_HPP__
#endif // ACTIVE_EDITOR


