
#define __USE_POSIX199309

#include <sys/time.h>
#include <sys/vfs.h>
#include <time.h>

#include <unistd.h>

#include <stdio.h>
#include <stdint.h>
#include <string.h>

// --- cTimeMs ---------------------------------------------------------------

class cTimeMs 
{
   private:

      uint64_t begin;

   public:

      cTimeMs(int Ms = 0);
      static uint64_t Now(void);
      void Set(int Ms = 0);
      bool TimedOut(void);
      uint64_t Elapsed(void);
};

cTimeMs::cTimeMs(int Ms)
{
   Set(Ms);
}

uint64_t cTimeMs::Now(void)
{
#if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
#define MIN_RESOLUTION 5 // ms
   static bool initialized = false;
   static bool monotonic = false;
   struct timespec tp;

   if (!initialized) 
   {
      // check if monotonic timer is available and provides enough accurate resolution:
      if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) 
      {
         long Resolution = tp.tv_nsec;
         // require a minimum resolution:
         if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000)
         {
            if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) 
            {
               printf("cTimeMs: using monotonic clock (resolution is %ld ns)\n", Resolution);
               monotonic = true;
            }
            else
               printf("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed\n");
         }
         else
            printf("cTimeMs: not using monotonic clock - resolution is too bad (%ld s %ld ns)\n", 
                   tp.tv_sec, tp.tv_nsec);
      }
      else
         printf("cTimeMs: clock_getres(CLOCK_MONOTONIC) failed\n");
      initialized = true;
   }
   if (monotonic) 
   {
      if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
         return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000;
      printf("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed\n");
      monotonic = false;
      // fall back to gettimeofday()
   }
#else
#  warning Posix monotonic clock not available
#endif
   struct timeval t;

   if (gettimeofday(&t, NULL) == 0)
      return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;

   return 0;
}

void cTimeMs::Set(int Ms)
{
  begin = Now() + Ms;
}

bool cTimeMs::TimedOut(void)
{
  return Now() >= begin;
}

uint64_t cTimeMs::Elapsed(void)
{
  return Now() - begin;
}

const char* TimeToString(time_t t)
{
  static char buffer[32];

  if (ctime_r(&t, buffer)) 
  {
     buffer[strlen(buffer) - 1] = 0; // strip trailing newline
     return buffer;
  }

  return "???";
}

//***************************************************************************
// tescht
//***************************************************************************

int main(int argc, char** argv)
{
   uint64_t nextForce;

   nextForce = cTimeMs::Now(); //  + 4 * 1000;

   const char* date = TimeToString(nextForce/1000);

   printf("shedule force for '%s'; nextForce (%llu)\n",
          date, nextForce);

   return 0;
}

