
#include "time_metrics.h"

#include <windows.h>

#include "rtg_atomic.h"

//Reartgeting

static unsigned long long frq_us;

static unsigned long long get_frq_us()
{
  if (!frq_us)
  {
    LARGE_INTEGER frq;
    ::QueryPerformanceFrequency(&frq);
    frq_us=frq.QuadPart/1000000;
  }
  return frq_us;
}

inline metric::metric_time_t rtg_get_time()
{
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return li.QuadPart;
}

static unsigned long long T_MAX_=((unsigned long long)0)-1;

inline metric::metric_time_t rtg_time_diff(metric::metric_time_t a, metric::metric_time_t b)
{
  if (a<b) //Rollover?
    return b+(T_MAX_-a);
  else
    return a-b;
}

static unsigned long total_incompletes;
static unsigned long total_missed;

metric::metric_data::metric_data(metric *parent):
    m_parent(parent)
{
  reset();
  m_deadline=0;
}

metric::metric_data::metric_data():
    m_parent(0)
{
  reset();
  m_deadline=0;
}

void metric::metric_data::reset()
{
  m_sum=0;
  m_min=0;
  m_max=0;
  m_last=0;
  m_dlmiss=0;
}

void metric::metric_data::add(metric::metric_time_t t, bool restart)
{
  m_last=t;
  m_sum+=t;

  if (!restart)
  {
    if (t>m_max)
        m_max=t;
    if (t<m_min)
        m_min=t;


    if (m_deadline>0 && m_deadline<t)
    {
      ++m_dlmiss;
      rtg_atomic_increment((rtg_atom_t*) &total_missed);
    }
  }
}

void metric::metric_data::set(metric::metric_time_t t)
{
  m_max=t;
  m_min=t;
  m_last=t;
  m_sum=t;
  //m_sum+=t;

  //if (m_deadline>0 && m_deadline<t)
  //  ++m_dlmiss;
}

unsigned long metric::metric_data::samples() const
{
  if (m_parent)
    return m_parent->samples();
  return 0;
}

metric::metric_time_t metric::metric_data::avg_us() const
{
  unsigned long long s=samples();
  if (!s)
    return 0;
  return (m_sum/s)/get_frq_us();
}

metric::metric_time_t metric::metric_data::min_us() const
{
  return m_min/get_frq_us();
}

metric::metric_time_t metric::metric_data::max_us() const
{
  return m_max/get_frq_us();
}

metric::metric_time_t metric::metric_data::last_us() const
{
  return m_last/get_frq_us();
}

unsigned long metric::metric_data::dl_miss() const
{
  return m_dlmiss;
}

void metric::metric_data::set_deadline(metric_time_t x) const
{
  m_deadline=x*get_frq_us();
}

metric::metric_time_t metric::metric_data::get_deadline() const
{
  return m_deadline/get_frq_us();
}


metric::metric(metric_time_t dl_time, metric_time_t dl_period):
    m_time(this),
    m_period(this)
{
    m_progress=0;
    m_samples=0;
    m_incompletes=0;
    m_time.set_deadline(dl_time);
    m_period.set_deadline(dl_period);
}

void metric::resetImmediate()
{
}


metric::metric_time_t metric::start(bool restart)
{
    if (m_progress)
        {
          ++m_incompletes;
      rtg_atomic_increment((rtg_atom_t*) &total_incompletes);

        }
    //InterlockedExchangeAdd((volatile LONG*) &m_incompletes, m_progress>0);
    rtg_atomic_increment((rtg_atom_t*) &m_progress);

    metric_time_t  old=m_start;
    m_start=rtg_get_time();

    metric_time_t delta=rtg_time_diff(m_start, old);
    if (m_samples>1)
          m_period.add(delta, restart);
        else
      m_period.set(delta);

    return m_start;
}

void metric::reset()
{
  m_samples=0;
  m_incompletes=0;
  m_period.m_dlmiss=0;
  m_time.m_dlmiss=0;
}

unsigned long metric::total_incompleted()
{
  return total_incompletes;
}

unsigned long metric::total_deadline_missed()
{
  return total_missed;
}

metric::metric_time_t metric::stop()
{
    m_stop=rtg_get_time();
    metric_time_t delta=rtg_time_diff(m_stop, m_start);
    if (m_progress) //incomplete samples!!
    {
        //You should take some recovery action!!!
    }
    rtg_atomic_decrement((rtg_atom_t*) &m_progress);

        if (m_samples)
          m_time.add(delta, false);
        else
          m_time.set(delta);

    rtg_atomic_increment((rtg_atom_t*) &m_samples);

    return m_stop;
}

metricRegistered* metricRegistered::m_root;

/*
static CRITICAL_SECTION* metricMutex;

class metricReigisteredMutex
{
public:
  static metricReigisteredMutex& instance()
  {
        if (!metricMutex)
        {
          metricMutex=new CRITICAL_SECTION;
          InitializeCriticalSection(metricMutex);
        }
        return staticMutexWrapper;
  }
  void enter() { EnterCriticalSection(metricMutex);}
  void leave() { LeaveCriticalSection(metricMutex);}

  static metricReigisteredMutex staticMutexWrapper;
};

metricReigisteredMutex metricReigisteredMutex::staticMutexWrapper;
*/

metricRegistered::metricRegistered(const char* name, metricRegistered* parent):
  m_name(name),
  m_parent(parent)
{
  init();
}

metricRegistered::metricRegistered(const char* const name, metric_time_t dl_time, metric_time_t dl_period, metricRegistered* parent):
  metric(dl_time, dl_period),
  m_name(name),
  m_parent(parent)
{
  init();
}

void metricRegistered::init()
{
    metricRegistered** list=m_parent ? &m_parent->m_sons : &m_root;
    m_next=(metricRegistered*)rtg_atomic_exchange((rtg_atom_t*)list, (long)this);
}

