// ============================================================================
// $Id: Timer.cc,v 1.2 2005-12-21 13:58:55 habbecke Exp $
// ----------------------------------------------------------------------------
#ifndef DOXY_IGNORE_THIS
// ----------------------------------------------------------------------------
#include <OpenMesh/Core/System/config.hh>
#if defined(OM_CC_MIPS)
#  include <math.h>
#  include <stdio.h>
#else
#  include <cmath>
#  include <cstdio>
#endif
#include "Timer.hh"
// ----------------------------------------------------------------------------

// ------------------------------------------------------------- namespace ----

namespace OpenMesh {
namespace Utils {


// ----------------------------------------------------------------------------

using namespace std;

// -------------------------------------------------------------- TimerImpl ----
// just a base class for the implementation
class TimerImpl
{
protected:
public:
  TimerImpl()  { ; }
  virtual ~TimerImpl() { ; }

  virtual void   reset(void) = 0;
  virtual void   start(void) = 0;
  virtual void   stop(void)  = 0;
  virtual void   cont(void)  = 0;
  virtual double seconds(void) const = 0;
};

// compiler and os dependent implementation

// ------------------------------------------------------------- windows 32 ----
#if defined(WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER))

#include <windows.h>

class TimerImplWin32 : public TimerImpl
{
protected:
   LARGE_INTEGER freq_;
   LARGE_INTEGER count_;
   LARGE_INTEGER start_;

public:
   TimerImplWin32(void);
   ~TimerImplWin32(void) { ; }

   virtual void   reset(void);
   virtual void   start(void);
   virtual void   stop(void);
   virtual void   cont(void);
   virtual double seconds(void) const;
};

TimerImplWin32::TimerImplWin32(void)
{
   if (QueryPerformanceFrequency(&freq_)==FALSE)
     throw std::runtime_error("Performance counter of of stock!");
   reset();
}

void TimerImplWin32::reset(void)
{
   memset(&count_,0,sizeof(count_));
   memset(&start_,0,sizeof(count_));
}

void TimerImplWin32::start(void)
{
   reset();
   QueryPerformanceCounter(&start_);
}

void TimerImplWin32::stop(void)
{
   LARGE_INTEGER stop_;

   QueryPerformanceCounter(&stop_);
   count_.QuadPart += stop_.QuadPart - start_.QuadPart;
}

void TimerImplWin32::cont(void)
{
   QueryPerformanceCounter(&start_);
}

double TimerImplWin32::seconds(void) const
{
   return (double)count_.QuadPart/(double)freq_.QuadPart;
}

// ------------------------------------------------------------- posix time ----
#elif defined(__GNUC__) && defined(__POSIX__) 

#  include <time.h>

template <clockid_t N>
class TimerImplPosix : public TimerImpl
{
public:
    TimerImplPosix() : id_(N), seconds_(0.0)
    { }

    ~TimerImplPosix()
    { }

    virtual void reset(void) { seconds_ = 0.0; }

    virtual void start(void) { seconds_ = 0.0; clock_gettime( id_, &start_ ); }
    virtual void stop(void)
    {
        timespec stop;
        clock_gettime( id_, &stop );
        seconds_ += ( stop.tv_sec - start_.tv_sec );
        seconds_ += ( (double(stop.tv_nsec-start_.tv_nsec)*1e-9) );
    }

    virtual void cont(void)  { clock_gettime( id_, &start_ ); }

    virtual double seconds() const { return seconds_; }

protected:
   clockid_t id_;
   double    seconds_;
   timespec  start_;
};

// ----------------------------------------------------------- gettimeofday ----
#elif defined(__GNUC__) || (defined(__INTEL_COMPILER) && !defined(WIN32))

#  include <sys/time.h>
#  include <sys/resource.h>
#  include <unistd.h>

class TimerImplGToD: public TimerImpl
{
public:
    TimerImplGToD() : seconds_(0.0)
    { }

    ~TimerImplGToD()
    { }

    virtual void reset(void) { seconds_ = 0.0; }
    virtual void start(void) { seconds_ = 0.0; gettimeofday( &start_, &tz_ ); }

    virtual void stop(void)
    {
      gettimeofday( &stop_, &tz_ );

      seconds_ +=  (double)(stop_.tv_sec - start_.tv_sec);
      seconds_ +=  (double)(stop_.tv_usec- start_.tv_usec)*1e-6;
    }

    virtual void cont(void)  { gettimeofday( &start_, &tz_); }

    virtual double seconds() const { return seconds_; }

private:
  
  struct timeval start_, stop_;
  struct timezone tz_;

  double seconds_;
};


#else // ---------------------------------------- standard implementation ----

#include <time.h>

static const unsigned long clockticks = CLOCKS_PER_SEC;

class TimerImplStd : public TimerImpl
{
public:
   TimerImplStd() : freq_(clockticks) { reset(); }
   ~TimerImplStd() { ; }

   virtual void   reset(void) { count_ = 0; }
   virtual void   start(void) { count_ = 0; start_ = clock(); }
   virtual void   stop(void);
   virtual void   cont(void)  { start_ = clock(); }
   virtual double seconds(void) const { return (double)count_/(double)freq_; }

protected:
   unsigned long freq_;
   unsigned long count_;
   unsigned long start_;
};

void TimerImplStd::stop(void)
{
   unsigned long stop_ = clock();
   count_ += stop_-start_;
}

#endif

// ----------------------------------------------------------------- Timer ----

Timer::Timer(void)
{
#if defined(WIN32) && defined(_MSC_VER)
  impl_       = new TimerImplWin32;
#elif defined(__GNUC__) && defined(__POSIX__)
// CLOCK_REALTIME
// CLOCK_MONOTONIC     - ?
// CLOCK_REALTIME_HR   - RTlinux
// CLOCK_MONOTONIC_HR  - ?
#  if defined(CLOCK_REALTIME_HR)
  impl_      = new TimerImplPosix<CLOCK_REALTIME_HR>;
#  else
  impl_      = new TimerImplPosix<CLOCK_REALTIME>;
#  endif
#elif defined(__GNUC__) || (defined(__INTEL_COMPILER) && !defined(WIN32))
  impl_      = new TimerImplGToD;
#else
  impl_       = new TimerImplStd;
#endif
  state_      = Stopped;
}

Timer::~Timer(void)
{
   delete impl_;
   state_ = Stopped;
}

void Timer::reset(void)
{
   state_ = Stopped;
   impl_->reset();
}

void Timer::start(void)
{
   state_ = Running;
   impl_->start();
}

void Timer::stop(void)
{
   impl_->stop();
   state_ = Stopped;
}

void Timer::cont(void)
{
   impl_->cont();
   state_ = Running;
}

double Timer::seconds(void) const
{
   return state_==Stopped ? impl_->seconds() : 0.0;
}

std::string Timer::as_string(Timer::Format format)
{
   if (state_ == Running)
       return "Running";
   return as_string(impl_->seconds(),format);
}

std::string Timer::as_string(double seconds, Timer::Format format)
{
   char string[32];

   double fraction;
   double integer;
   unsigned long t;
//    double rest;
   short  hour,min,sec;
   bool   negative = false;
   
   if ( seconds < 0 )
   {
      negative = true;
      seconds *= -1;
   }
   
   fraction = modf(seconds,&integer);

   t = (unsigned long)integer;

   hour = short( t / 3600L );
   t   %= 3600L;
   min  = short( t / 60L );
   t   %= 60L;
   sec  = short( t );
//    rest = (double)t + fraction;

   char *ptr = string;
   if (negative)
      *ptr++ = '-';
   
   switch(format)
   {
   case Timer::Automatic:     
      if (hour)
         ptr += sprintf(ptr,"%02dh:",hour);

      if (min)
         ptr += sprintf(ptr,"%02dm:",min);
      else if (ptr>string && hour)
         ptr += sprintf(ptr,"00m:");

      if (sec)
         ptr += sprintf(ptr,"%02d",sec);
      else if (ptr>string && min)
         ptr += sprintf(ptr,"00");

      if (!hour && !min) // higher resolution necessary
      {
         if (ptr > string && sec)
         {
            sprintf(ptr,".%.3fs",fraction);
            ptr++;
            while(*(ptr+2))
            { 
               *ptr = *(ptr+2);
               ptr++;
            }
            *ptr = '\0';
         }
         else if ( fraction * 1e2 > 0.1)
            sprintf(ptr,"%.3fcs",fraction*1.e2);
         else if ( fraction * 1e3 > 0.1)
            sprintf(ptr,"%.3fms",fraction*1.e3);
         else if ( fraction * 1e6 > 0.1)
            sprintf(ptr,"%.1f\xb5s",fraction*1.e6);
         else if ( fraction * 1e9 > 0.1)
            sprintf(ptr,"%.1fns",fraction*1.e9);
         else
            sprintf(ptr,"%.1fps",fraction*1.e12);            
      } else // append a 's' for seconds!
      {
         ptr[0] = 's';
         ptr[1] = '\0';
      }
      break;
   case Timer::Long:
      ptr += sprintf(ptr,"%02dh:%02dm:%02d",hour,min,sec);
      sprintf(ptr,".%.12fs",fraction);
      ptr++;
      while(*(ptr+2))
      { 
         *ptr = *(ptr+2);
         ptr++;
      }
      *ptr = '\0';
      break;
   case Timer::Hours:
      sprintf(ptr,"%02dh:%02dm:%02ds",hour,min,sec); break;
   case Timer::Minutes:
      ptr += sprintf(ptr,"%02dm:%02d", min, sec);
      sprintf(ptr,".%.2fs",fraction);
      ptr++;
      while(*(ptr+2))
      { 
         *ptr = *(ptr+2);
         ptr++;
      }
      *ptr = '\0';
      break;
   case Timer::Seconds: sprintf(ptr,"%.3fs",seconds); break;
   case Timer::HSeconds: sprintf(ptr,"%.3fcs",seconds*1e2); break;
   case Timer::MSeconds:  sprintf(ptr,"%.3fms",seconds*1e3); break;
   case Timer::MicroSeconds: sprintf(ptr,"%.1f\xb5s",seconds*1e6); break;
   case Timer::NanoSeconds: sprintf(ptr,"%.1fns",seconds*1e9); break;
   }
   return string;
}

// ============================================================================
} // END_NS_UTILS
} // END_NS_OPENMESH
// ----------------------------------------------------------------------------
#endif // DOXY_IGNORE_THIS
// ============================================================================
// end of file Timer.cc
// ============================================================================

