// Time.h: interface for the CTime class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(__HPTIME_H)
#define __HPTIME_H

//
// (c) Johannes Lampel a.k.a. @$3.1415rin
// This code is part of JoeBOT XP ( http://joebot.bots-united.com )
//
// Feel free to use this code in your own projects.
//

//
// The base of the cpu frequency measurement stuff is copied from somewhere, I guess it was from somewhere like
// codeproject.com, unfortunately I dunno anymore who was the original author of that pieces of code ... *runningaway*
//
// So first, thanks to the unkown.
// Additional thanks to William van der Sterren, who put me on the track towards rdtsc
//

//
// I recently started to port this code for gcc, so it may be again buggy.
// I especially havent tested yet the CTime::CCPUSpeed::GetCyclesDifference function where are some bitshifting
// operations which are used to be buggy, at least when I code such stuff without having much time /@$3.1415rin 01/2004
//

typedef	void (*DELAY_FUNC)(unsigned int uiMS);
#ifdef __GNUC__
typedef long long _hptime64;
#else
typedef  __int64 _hptime64;
#endif

#define RDTSC_INSTRUCTION		_asm _emit 0x0f _asm _emit 0x31
#define	CPUSPEED_I32TO64(x, y)		(((_hptime64) x << 32) + y)


/**
 * this class provides high performance time measurements based on an 
 * internal counter, if available and accessible, of the CPU which is 
 * increased every cycle.
 * The counter is of 64 bit size, but the information can also be received 
 * as double float values.
 * Note that you might need to filter the data you get from this class, 
 * especially on multithreading/multitasking systems, since the time spent 
 * in other applications is not substracted here.
 * For the initial measurement of the CPU clock, OS dependent routines 
 * are used, for windows for example the HighPerformance timebase based 
 * on the system timer running usually @ 1193182 Hz.
 *
 * @author Johannes Lampel <johannes@lampel.net>
 */
class CTime  {
  /**
   * this class measures the CPU clock frequency
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  class CCPUSpeed {
  public:
    /**
     * The measurements are already done inside this constructor and stored
     * in the member variable m_i64CPUSpeed
     *
     * @author Johannes Lampel <johannes@lampel.net>
     */
    CCPUSpeed ();

    ~CCPUSpeed ();

    // Variables.
    _hptime64 m_i64CPUSpeed;

    /**
    * this function calls a function with a given parameter and measures
    * how long this function runs.
    *
    * @param DelayFunction defining the function to be called
    * @param uiParameter defining the parameter with which the function should 
    *                    be called
    * @return the time difference in cpu clock cycles.
    *
    * @author Johannes Lampel <johannes@lampel.net>
    */
    _hptime64 GetCyclesDifference(DELAY_FUNC DelayFunction, 
                                  unsigned int uiParameter);

  private:
    /**
     * delay function delaying further executing for param ms
     *
     * @param uiMS delay time in milliseconds
     *
     * @author Johannes Lampel <johannes@lampel.net>
     */
    static void Delay(unsigned int uiMS);

    /**
     * Delay function delaying further executing for 0 ms to measure the 
     * overhead of Delay().
     *
     * @param uiMS delay time
     *
     * @author Johannes Lampel <johannes@lampel.net>
     */
    static void DelayOverhead (unsigned int uiMS);		
  };
public:
  /**
   * constructor - sets the marker to the current time
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  CTime();
  
  /**
   * destructor
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  virtual ~CTime();

  /**
   * returns the number of CPU cycles since startup as 64 bit integer
   *
   * @return number of CPU cycles since startup as 64 bit integer
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  _hptime64 getTime(void);

  /**
   * returns the number of seconds since startup as double float
   *
   * @return number of seconds since startup as double float
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  double getTime_d(void);
  void markTick(void);

  /**
   * returns the number of CPU cycles since last call of markTick()
   * as 64 bit integer
   *
   * @return number of CPU cycles since last call of markTick()
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  _hptime64 getElapsed(void);

  /**
   * returns the number of seconds since last call of markTick() as 
   * double float
   *
   * @return number of seconds since last call of markTick()
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  double getElapsed_d(void);

  /**
   * returns the number of CPU cycles between the last 2 calls of markTick()
   *
   * @return number of CPU cycles between the last 2 calls of markTick()
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  _hptime64 getMarkerDiff(void);

  /**
   * returns the number of seconds between the last 2 calls of markTick()
   *
   * @return number of seconds between the last 2 calls of markTick()
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  double getMarkerDiff_d(void);

  /**
   * returns the frequency at which the CPU is running
   *
   * @return frequency at which the CPU is running
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  _hptime64 getFrequency(void)
  {
    //return m_i64Frequency;
    return m_CPUSpeed.m_i64CPUSpeed;
  }

  /**
   * returns time of creation of the instance of this class
   *
   * @return time of creation of the instance of this class
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  _hptime64 getTimeOfCreation(void)
  {
    //return m_i64Frequency;
    return m_i64Creation;
  }
protected:
  /**
   * a function to measure the time without having to rely on anything cpu 
   * specific
   *
   * @return time
   *
   * @author Johannes Lampel <johannes@lampel.net>
   */
  double getExactTimewoCPU(void);

  //_hptime64 m_i64Frequency;

  /**
   * time at which the marker was set last time by markTick() or if this
   * function wasn't called yet, the time at which this instance of this class
   * was created
   */
  _hptime64 m_i64Marker;

  /**
   * time difference from marked time to next call of markTick() or if this
   * function wasn't called yet, 0
   */
  _hptime64 m_i64MarkerDiff;

  /**
   * time at which this timer was instantiated
   */
  _hptime64 m_i64Creation;

  /**
   * instance of CCPUSpeed 
   */
  CCPUSpeed m_CPUSpeed;
#ifdef _WIN32
  /**
   * result of QueryPerformanceFrequency (#ifdef _WIN32)
   */
  double m_dStdFrequency;
#endif
};

extern CTime g_HPTime;

#endif // !defined(__HPTIME_H)

