#ifndef CTICK2_H
#define CTICK2_H

#include "GdCPP_Exports.h"
#include <stdint.h>
#include <Windows.h>
#include <iostream>

/// @brief 在不同的时间点（电脑开机，程序启动，实例启动等）、精度单位(s, ms, us等）下计算时间差。
class GDCPP_API CTic
{   
	// ------ 静态变量和静态成员函数 ------ 
public:
	/// 初始化获取时钟频率和起始值，只需要在App或Dll初始化时调用一次。
	static bool Init();

	// ------ 静态变量，必须调用一次Init()确定 -----

	/// init()初始化时检测到的系统高精度定时器的频率
	static int64_t Freq;		//1Hz
	// 以下是由Freq转换出来的不同单位的频率，供计算不同单位的延迟时选用。
	// 减少一个乘法，目的是减少运算量并防止乘法溢出。
	static int64_t Freq_1k;		//1kHz，用于计算1ms的时间差
	static int64_t Freq_10k;	//10kHz, 用于计算100us的时间差
	static int64_t Freq_100k;	//100kHz, 用于计算10us以下的时间差。目前假设时钟是100k的整数倍
	// 以下用于将时钟数转换成频率。
	static int64_t Freq_M10;	//0.1Hz
	static int64_t Freq_M100;	//0.2Hz

	/// 记录Init()时的时间戳，当做启动软件时的时间
	static int64_t AppStartTick;

	/// 获取高精度时钟原始值，保存到指定变量
	static inline void QueryCnt(int64_t& t) { QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&t)); }

	/// 返回高精度时钟原始值
	static inline int64_t QueryCnt() { int64_t t;  QueryCnt(t);   return t; }

	// 四舍五入除法，适合要求更高精度的场合
	//static inline int64_t _div(int64_t t, int64_t f) { return (t + f / 2) / f; };
	// 截尾除法，运算量最小
	static inline int64_t _div(int64_t t, int64_t f) { return t / f; }

	/// 延迟一小段时间。延迟过程中不挂起线程，长延迟请用Sleep()挂起线程
	static void Delay   (uint32_t ms) { int64_t start = QueryCnt();	while (((QueryCnt() - start) / Freq_1k)        < int(ms)); }
	static void Delay_us(uint32_t us) { int64_t start = QueryCnt();	while (((QueryCnt() - start) * 1000 / Freq_1k) < int(us)); }

	/// @brief 由时间差计算频率，单位分别为1Hz, 0.1Hz, 0.01Hz。
	static inline int64_t cnt2freq(int64_t t)		{ return _div(Freq, t); }
	static inline int64_t cnt2freq_p10(int64_t t)	{ return _div(Freq_M10, t); }
	static inline int64_t cnt2freq_p100(int64_t t)	{ return _div(Freq_M100, t); }

    /// 当前计数器原始值
    int64_t Cnt;

	/// 默认构造函数，获取当前高精度时钟值。
	inline CTic()	{	QueryCnt(Cnt);}

	/// @brief 带参数的构造函数，通常复制另一个时钟的值。
	/// @param t 不能是负数
	inline CTic(int64_t t)		: Cnt(t)	{	_ASSERT(t >= 0);	}
	inline CTic(const CTic &s)	: Cnt(s.Cnt){	}

	/// 更新计数器值并返回原始值
	inline int64_t	Update() { QueryCnt(Cnt); return Cnt; }

	// 时钟数转不同单位的时间
	static inline int64_t to_s    (int64_t t) { return _div(t,		Freq);     } // 时钟计数转时间，单位为s
	static inline int64_t to_ms   (int64_t t) { return _div(t,		Freq_1k);  } // 时钟计数转时间，单位为ms
	static inline int64_t to_100us(int64_t t) { return _div(t,		Freq_10k); } // 时钟计数转时间，单位为100us
	static inline int64_t to_10us (int64_t t) { return _div(t,		Freq_100k);} // 时钟计数转时间，单位为10us。测试算法到10us精度足够了
	static inline int64_t to_us   (int64_t t) { return _div(t*10,	Freq_100k);} // 时钟计数转时间，单位为us。 这一档开始比前面的运算量多一个乘法
	static inline int64_t to_ns   (int64_t t) { return _div(t*10000,Freq_100k);} // 时钟计数转时间，单位为ns。 注意仅用于短时间的计算，t太大乘法会溢出。

	static inline int64_t ms2cnt(int64_t t) { return t * Freq_1k; }					// 时间转时钟计数，单位为ms
	static inline int64_t us2cnt(int64_t t) { return _div(t * Freq_100k, 10); }		// 时间转时钟计数，单位为us
	static inline int64_t ns2cnt(int64_t t) { return _div(t * Freq_100k, 10000);; }	// 时间转时钟计数，单位为ns

	inline        int64_t to_s()	const { return to_s	    (Cnt); }
	inline        int64_t to_ms()	const { return to_ms	(Cnt); }		// 普通计算用这一档
	inline        int64_t to_100us()const { return to_100us (Cnt); }
	inline        int64_t to_10us() const { return to_10us	(Cnt); }	// 测试算法到10us精度足够了
	inline        int64_t to_us()	const { return to_us	(Cnt); }	// 这一档开始会多算一个乘法
	inline        int64_t to_ns()	const { return to_ns	(Cnt); }	// 注意t不能太大，乘法会溢出

	// 这些是最常用的函数，获取两次访问之间的时间差，同时更新时间计数。
	// 两次访问包括：构造函数、Update()、elapse()、sincePowerOn()、sinceAppRun(),以及子类Tick2::sinceStart()
	inline int64_t elapse_s()	 { auto bak = Cnt; QueryCnt(Cnt); return to_s    (Cnt - bak);  }
	inline int64_t elapse()		 { auto bak = Cnt; QueryCnt(Cnt); return to_ms   (Cnt - bak);  }
	inline int64_t elapse_100us(){ auto bak = Cnt; QueryCnt(Cnt); return to_100us(Cnt - bak);  }
	inline int64_t elapse_10us() { auto bak = Cnt; QueryCnt(Cnt); return to_10us (Cnt - bak);  }
	inline int64_t elapse_us()	 { auto bak = Cnt; QueryCnt(Cnt); return to_us   (Cnt - bak);  }
	inline int64_t elapse_ns()	 { auto bak = Cnt; QueryCnt(Cnt); return to_ns   (Cnt - bak);  }
	inline int64_t elapse_raw()  { auto bak = Cnt; QueryCnt(Cnt); return Cnt - bak; }

	/// 获取从电脑开机开始记的计时器值。
	static inline int64_t PowerOn_s()	   { return to_s	(QueryCnt()); }
	static inline int64_t PowerOn()	   { return to_ms	(QueryCnt()); }
	static inline int64_t PowerOn_100us() { return to_100us(QueryCnt()); }

	inline		 int64_t powerOn_s()	  { QueryCnt(Cnt); return to_s	(); }
	inline		 int64_t powerOn()		  { QueryCnt(Cnt); return to_ms	(); }
	inline		 int64_t powerOn_100us()  { QueryCnt(Cnt); return to_100us(); }

	/// 获取程序启动后执行Init()开始算起的时间。通常在App启动时调用Init()，因此可以等于程序启动后的时间
	static inline int64_t AppRun_s()		{ return to_s    (QueryCnt() - AppStartTick); }
	static inline int64_t AppRun()			{ return to_ms   (QueryCnt() - AppStartTick); }
	static inline int64_t AppRun_100us()	{ return to_100us(QueryCnt() - AppStartTick); }

    inline        int64_t appRun_s()		{ QueryCnt(Cnt); return to_s    (Cnt - AppStartTick); }
    inline        int64_t appRun()			{ QueryCnt(Cnt); return to_ms   (Cnt - AppStartTick); }
    inline        int64_t appRun_100us()	{ QueryCnt(Cnt); return to_100us(Cnt - AppStartTick); }

	inline int64_t since_s		(const CTic& start) { QueryCnt(Cnt); return to_s		(Cnt - start.Cnt); }
	inline int64_t since		(const CTic& start) { QueryCnt(Cnt); return to_ms		(Cnt - start.Cnt); }
	inline int64_t since_100us	(const CTic& start) { QueryCnt(Cnt); return to_100us	(Cnt - start.Cnt); }

	// 两个实例相减，得到计数值的差值
	inline int64_t operator -(const CTic& t) {return Cnt - t.Cnt;}

};

// 计数两个实例的时间差
static inline int64_t DiffTime_s	(const CTic& start, const CTic& end) { return CTic::to_s	(end.Cnt - start.Cnt); }
static inline int64_t DiffTime		(const CTic& start, const CTic& end) { return CTic::to_ms	(end.Cnt - start.Cnt); }
static inline int64_t DiffTime_100us(const CTic& start, const CTic& end) { return CTic::to_100us(end.Cnt - start.Cnt); }
static inline int64_t DiffTime_10us	(const CTic& start, const CTic& end) { return CTic::to_10us	(end.Cnt - start.Cnt); }
static inline int64_t DiffTime_us	(const CTic& start, const CTic& end) { return CTic::to_us	(end.Cnt - start.Cnt); }
static inline int64_t DiffTime_ns	(const CTic& start, const CTic& end) { return CTic::to_ns	(end.Cnt - start.Cnt); }

/// @brief 双计时器。应用场景：某个算法包含多个耗时的步骤，需要统计每一步的时间和总时间。
/// @detail 示例：
/// 1. 算法开头构造Tick2实例或Restart()现有实例
/// Tick2 t; //或t.Restart();
/// 
/// 2. 分步统计耗时
/// Step1();			// 步骤1
/// log(t.elapse());	// 记录步骤1耗时
/// Step2();			// 步骤2
/// log(t.elapse());	// 记录步骤2耗时
/// Step3();			// 步骤3
/// log(t.elapse());	// 记录步骤3耗时
/// 
/// 2. 统计总耗时
/// log(t.sinceStart());// 记录总耗时
class GDCPP_API CTic2 : public CTic
{
public:
	inline CTic2()
		: Start(Cnt) // 基类构造时用默认构造函数，本类构造Start时用直接复制Cnt值避免再获取一次
	{

	}

	inline CTic2(const CTic2 &s)
		: Start(s.Start)
	{
		Cnt = s.Cnt;
	}
	/// @brief 保存起始时间
	CTic Start;

	/// @brief 重置两个定时器的值。
	inline void Restart()	{QueryCnt(Cnt);	Start.Cnt = Cnt;}

	/// 用于最近获取过时间，快速重置起始时间，
	inline void fastRestart(){Start.Cnt = Cnt;	}

	/// 从构造实例或重置起经过了多少时间
	inline int64_t sinceStart_s()    { QueryCnt(Cnt); return to_s    (Cnt - Start.Cnt); }
	inline int64_t sinceStart()      { QueryCnt(Cnt); return to_ms   (Cnt - Start.Cnt); }
	inline int64_t sinceStart_100us(){ QueryCnt(Cnt); return to_100us(Cnt - Start.Cnt); }
	inline int64_t sinceStart_10us() { QueryCnt(Cnt); return to_10us (Cnt - Start.Cnt); }
	inline int64_t sinceStart_us()   { QueryCnt(Cnt); return to_us   (Cnt - Start.Cnt); }
	inline int64_t sinceStart_ns()   { QueryCnt(Cnt); return to_ns   (Cnt - Start.Cnt); }
};

/// @brief 在CTic基础上增加计算N个时间戳的差值的平均
class GDCPP_API CAvgDiffTick
	: public CTic
{
public:
	CAvgDiffTick(int n = 0)
		: N(n)
	{
		if (n > 0) Buf = new int64_t[n];
	}

	~CAvgDiffTick()
	{
		delete[] Buf;
	}

	void setSize(int n) {
		_ASSERT(n != 0);
		if (n != N) {
			if (Buf) delete[] Buf;
			Buf = new int64_t[n];
			N = n;
		}
		clear();
	}

	/// @brief 添加一个新值，返回差值的平均
	int64_t add(int64_t newvalue);
	inline int64_t add()
	{
		return add(Update());
	}

	void clear()
	{
		Cnt = 0;
		index = 0;
	}

	int Avg() { return _Avg; }
	// 测试代码
	static void Test1(int avgnum = 4);
	static void Test2(int avgnum = 4);
protected:
	int64_t* Buf = nullptr; // 最近N个CTic的缓冲区
	int64_t Cnt = 0;	// 已经缓冲的计数
	int index = 0;	// 新增数据的下标
	int N;			// 平均次数 = 缓冲区长度
	int _Avg = 0;		// 最近一次的平均值
};

#endif // CTICK_H
