#include "etimer.h"
#include "logger.h"
#include "estrhashof.h"
#include "elongarray.h"

#ifdef __APPLE__
#include <mach/clock.h>
#include <mach/mach.h>

#include "ebasicarray.h"

clock_serv_t cclock=0x00;

long egettimestamp()
{
  if (cclock==0x00)
    host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
  mach_timespec_t mts;
  clock_get_time(cclock, &mts);
//  mach_port_deallocate(mach_task_self(), cclock);
  return((long)mts.tv_sec*1000l + (long)mts.tv_nsec/1000000l);
}
#elif _WIN32
#include <Windows.h>

long egettimestamp()
{
  _LARGE_INTEGER tptime;
  QueryPerformanceCounter(&tptime);
  return(tptime.LowPart);
}
#else
#include <time.h>

long egettimestamp()
{
  struct timespec tp;
  lerrorif(clock_gettime(CLOCK_MONOTONIC, &tp)!=0,"getting clock time");
  return((long)tp.tv_sec*1000l + (long)tp.tv_nsec/1000000l);
}
#endif

estrhashof<eprofiler> profilers;

eprofiler& getProfiler(const estr& name)
{
  if (!profilers.exists(name))
    profilers.addref(name,new eprofiler(name));
  return(profilers.values(name));
}


#ifdef _WIN32
double pifrq = 0.0l;
#endif

etimer::etimer()
{
#ifdef _WIN32
  if (pifrq==0.0l){
    _LARGE_INTEGER fTime;
    QueryPerformanceFrequency(&fTime);
    pifrq = (double)1.0f/(double)fTime.LowPart;
  }
#endif
}

void etimer::reset()
{
#ifdef _WIN32
  QueryPerformanceCounter(&counter);  
#else
  gettimeofday(&counter,0);
#endif
}

#undef check

double etimer::check()
{
#ifdef _WIN32
  _LARGE_INTEGER tptime;
  QueryPerformanceCounter(&tptime);
  return((tptime.LowPart - counter.LowPart)*pifrq*1.0E3);
#else
  timeval tmptv;
  gettimeofday(&tmptv,0);
  return((tmptv.tv_sec-counter.tv_sec)*1.0E3+(tmptv.tv_usec-counter.tv_usec)*1.0E-3);
#endif
}

double etimer::lap()
{
#ifdef _WIN32
  _LARGE_INTEGER tptime;
  double  tmp;
  QueryPerformanceCounter(&tptime);
  tmp = (tptime.LowPart - counter.LowPart)*pifrq*1.0E3;
  counter = tptime;
  return(tmp);
#else
  timeval tmptv;
  double tmp;
  gettimeofday(&tmptv,0);
  tmp = (tmptv.tv_sec-counter.tv_sec)*1.0E3+(tmptv.tv_usec-counter.tv_usec)*1.0E-3;
  counter = tmptv;
  return(tmp);
#endif
}

eprofile::eprofile(const estr& pname): p(getProfiler(pname)) { p.start(); }
eprofile::eprofile(eprofiler& _p): p(_p) { p.start(); }
eprofile::~eprofile() { p.stop(); }

eprofiler::eprofiler(const estr& _name): name(_name),tcount(0l) {}
eprofiler::~eprofiler()
{
  cout << "profiler: " << name << " maxdepth: " << acc.size() << endl;
  for (int i=0; i<acc.size(); ++i)
    cout << " depth " << i << ": acc: " << acc[i] << " count: " << count[i] << " avg: " << (count[i]>0?acc[i]/count[i]:0.0l) << endl;
}

void eprofiler::reset()
{
  tcount=0l;
  acc.clear();
  count.clear();
  while (tstamps.size())
    tstamps.pop();
}

void eprofiler::start()
{
#ifdef _WIN32
  _LARGE_INTEGER tstamp;
  QueryPerformanceCounter(&tstamp);
#else
  timeval tstamp;
  gettimeofday(&tstamp,0);
#endif
  tstamps.push(tstamp);
  ldieif(tstamps.size()>100000,"maximum stacksize reached in eprofiler::start: 100000");
}

void eprofiler::stop()
{
  ldieif(tstamps.size()==0l,"profiler::stop without calling start first");
  ++tcount;
#ifdef _WIN32
  _LARGE_INTEGER tptime;
  QueryPerformanceCounter(&tptime);
  _LARGE_INTEGER tstamp=tstamps.top();
  tstamps.pop();
  while (acc.size()<=tstamps.size()){
    acc.add(0.0l);
    count.add(0l);
  }
  acc[tstamps.size()]+=(tptime.LowPart - tstamp.LowPart)*pifrq*1.0E3;
  ++count[tstamps.size()];
#else
  timeval tmptv;
  gettimeofday(&tmptv,0);
  timeval tstamp=tstamps.top();
  tstamps.pop();
  while (acc.size()<=tstamps.size()){
    acc.add(0.0l);
    count.add(0l);
  }
  acc[tstamps.size()]+=(tmptv.tv_sec-tstamp.tv_sec)*1.0E3+(tmptv.tv_usec-tstamp.tv_usec)*1.0E-3;
  ++count[tstamps.size()];
#endif
}


