#ifndef TIME_METRICS_H
#define TIME_METRICS_H

class metric
{
public:
    typedef unsigned long long metric_time_t;
    typedef long long metric_timediff_t;
    
    class metric_data {
    public:
      explicit metric_data();

      metric_time_t min_us() const;
      metric_time_t max_us() const;
      metric_time_t last_us() const;
      metric_time_t avg_us() const;

      unsigned long samples() const;

      unsigned long dl_miss() const;

      void set_deadline(metric_time_t) const;
      metric_time_t get_deadline() const;

    private:

      explicit metric_data(metric* parent);

      metric_time_t m_last;
      metric_time_t m_min;
      metric_time_t m_max;
      metric_time_t m_sum;

      mutable metric_time_t m_deadline;

      unsigned long m_dlmiss;

      void reset();
      void add(metric_time_t t, bool restart);
      void set(metric_time_t t);

      friend class metric;

      metric* const m_parent;
    };

    metric(metric_time_t dl_time=0, metric_time_t dl_period=0);
    
    metric_time_t start(bool restart=false);

    metric_time_t stop();
    
    unsigned long samples() const;
    unsigned long incompletes() const; //samples witha start but without a stop

    const metric_data& period() const;
    const metric_data& time() const;

    void reset();
    void resetImmediate();

    static unsigned long total_incompleted();
    static unsigned long total_deadline_missed();

protected:

    metric_time_t m_start;
    metric_time_t m_stop;
    
    metric_data m_time;
    metric_data m_period;

    volatile unsigned long m_progress;
    volatile unsigned long m_samples;
    volatile unsigned long m_incompletes;    
};


class metricRegistered: public metric
{
public:
  metricRegistered(const char* const name, metricRegistered* parent=0);
  metricRegistered(const char* const name, metric_time_t dl_time, metric_time_t dl_period=0, metricRegistered* parent=0);

  const char* name() const { return m_name;}

  metricRegistered* parent() { return m_parent;}
  metricRegistered* next() { return m_next;}
  metricRegistered* sons() { return m_sons;}

  static metricRegistered* first() { return m_root;}

protected:
  const char* const m_name;
  metricRegistered* m_next;
  metricRegistered* m_parent;
  metricRegistered* m_sons;

  static metricRegistered* m_root;

  void init();

private:
  void operator=(const metricRegistered&);
  metricRegistered(const metricRegistered&);

};

class metricMeasure {
public:
    explicit metricMeasure(metric& m):
	  m_metric(m)
    {
        m.start();
    }
    
    metric& operator()()
    {
        return m_metric;
    }

    ~metricMeasure()
    {
        m_metric.stop();
    }
    
private:
    metric& m_metric;

	void operator=(const metricMeasure&);
	metricMeasure(const metricMeasure&);
};


//Inlines

inline const metric::metric_data& metric::time() const
{
  return m_time;
}

inline const metric::metric_data& metric::period() const
{
  return m_period;
}

inline unsigned long metric::samples() const
{
  return m_samples;
}

inline unsigned long metric::incompletes() const
{
  return m_incompletes;
}


#endif // TIME_METRICS_H
