#ifndef __ACCURATE_TIMER_H__
#define __ACCURATE_TIMER_H__

#pragma warning ( disable : 4786 )

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <deque>
#include <process.h>
#include "mmsystem.h"

using namespace std;

//-------------------------------------------------------------------
// this timer class was designed to give high precision < 2% average
// error and it was designed to work best with multi-threaded clients
// waiting on the single instance of this timer
//
class AccurateTimer
{
public:
   static AccurateTimer* Instance()
   {
      return &instance;
   };
   virtual ~AccurateTimer()
   {
      timeKillEvent(timer), timer = 0;
      DeleteCriticalSection(&crit);
   }

   ///////////////////////////////////////////////////////////////
   // Function name   : Wait
   // Description     : Timer wait method
   //                 :
   // Return type     : void  :
   // Argument        : int timeout :
   ///////////////////////////////////////////////////////////////
   void Wait(int timeout)
   {
//#define TIMING_DEBUG
#ifdef TIMING_DEBUG
      LARGE_INTEGER s[2];
      QueryPerformanceCounter(&s[0]);     // timing starts

#endif
      if ( timeout > 0 )   // anything to wait on ?
      {
         HANDLE tHandle = 0;
         HANDLE pHandle = GetCurrentProcess();
         DuplicateHandle(pHandle, GetCurrentThread(), pHandle,
                         &tHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
         HANDLE WaitEvent;
         WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
         ResetEvent(WaitEvent);
         CTrigger Trg;
         Trg.InsertTrigger(WaitEvent);

         // wrap pool data access
         EnterCriticalSection(&AccurateTimer::Instance()->crit);
         waitPool.push_back(WaitData(tHandle, WaitEvent, timeout));
         LeaveCriticalSection(&AccurateTimer::Instance()->crit);
         //SuspendThread(tHandle);        // do the wait
         Trg.Wait();
         CloseHandle(tHandle);
      }
#ifdef TIMING_DEBUG
      QueryPerformanceCounter(&s[1]);   // wake up call and timing ends
      double diff = ((double)s[1].QuadPart - (double)s[0].QuadPart)/(double)freq.QuadPart;
      double percent = /*Absolute*/(diff-timeout)*100.0/(timeout?timeout:1);

      TRACE("intended to sleep for: %3ld. slept for: %8.4f. error = %8.4f\n",
             timeout, diff, percent);

      error += percent;
      ++count;
#endif
   }
   inline double        Absolute(double val) { return val > 0 ? val : -val; }
   inline LARGE_INTEGER GetFrequency()       { return freq; };

   ///////////////////////////////////////////////////////////////
   // Function name   : CALLBACK TimerFunc
   // Description     : Media callback timer method
   //                 :
   // Return type     : static void  :
   // Argument        : UINT uID :
   // Argument        : UINT uMsg :
   // Argument        : DWORD dwUser :
   // Argument        : DWORD dw1 :
   // Argument        : DWORD dw2 :
   ///////////////////////////////////////////////////////////////
   static void CALLBACK TimerFunc(UINT /*uID*/, UINT /*uMsg*/, DWORD dwUser, DWORD /*dw1*/, DWORD /*dw2*/)
   {
      static LARGE_INTEGER s[2];
      static bool init = true;

      if ( !init )
      {
         init = true;
         HANDLE tHandle = 0;
         HANDLE pHandle = GetCurrentProcess();
         DuplicateHandle(pHandle, GetCurrentThread(), pHandle,
                         &tHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
         SetThreadPriority(tHandle, THREAD_PRIORITY_TIME_CRITICAL);
         HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _getpid());
         if (!SetPriorityClass(h, REALTIME_PRIORITY_CLASS))
         {
            DWORD err = GetLastError();
            TRACE("SetPriorityClass Error: %d.\n", err);
         }
         CloseHandle(h);
         CloseHandle(tHandle);
      }

      AccurateTimer* pThis = reinterpret_cast<AccurateTimer*>(dwUser);

      QueryPerformanceCounter(&s[1]);
      //double diff = (((double)s[1].QuadPart - (double)s[0].QuadPart)/(double)pThis->freq.QuadPart);
      __int64 now = s[1].QuadPart/*+pThis->halfMsec.QuadPart*/;

      EnterCriticalSection(&pThis->crit);
      deque<WaitData>::iterator it = pThis->waitPool.begin();
      while ( it != pThis->waitPool.end() )
      {
         if ( now >= it->timeout.QuadPart )
         {
            //ResumeThread(it->h);
            SetEvent(it->eventHandle);
            it = pThis->waitPool.erase(it);
            continue;
         }
         ++it;
      }
      LeaveCriticalSection(&pThis->crit);
      s[0] = s[1];
   }

   volatile static double  error;
   volatile static int     count;

private:
   AccurateTimer()
   {
      error = 0;
      count = 0;
      QueryPerformanceFrequency(&freq);
      halfMsec.QuadPart = freq.QuadPart / 2000;
      freq.QuadPart /= 1000;   // convert to msecs
      InitializeCriticalSection(&crit);
      timer = timeSetEvent(1, 0, TimerFunc, (DWORD)this, TIME_PERIODIC);
   }

   struct WaitData
   {
      WaitData(HANDLE _h, HANDLE _evtH, int _t) : h(_h), eventHandle(_evtH)
      {
         LARGE_INTEGER now;
         QueryPerformanceCounter(&now);
         timeout.QuadPart = _t * AccurateTimer::Instance()->freq.QuadPart + now.QuadPart;
      };

      HANDLE h;
      HANDLE eventHandle;
      LARGE_INTEGER timeout;
   };
   friend struct WaitData;

   static AccurateTimer    instance;

   CRITICAL_SECTION        crit;
   deque<WaitData>         waitPool;
   MMRESULT                timer;
   LARGE_INTEGER           freq;
   LARGE_INTEGER           halfMsec;
};

#endif  // __ACCURATE_TIMER_H__
