
/*!
@author d金魚
@since 2003/x/x
@note
rewrite:2004/10/18
rewrite:2003/11/18

@section Report Policy
例：
@code
struct ConsoleReport : public ReportBase
{
  static void Output(double t,const char *f,const char *str,...) {
		char s[enuCharBuffSize]="";
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),str);
		printf(s); 
	}
};
@endcode
こんな感じ。
ちなみに第一引数のdouble tは仕様変更により今後、使われることは無い･･･。
fはファイル名
strは出力したい文字列
...はstrと一緒にprintfみたいな使い方で＾＾；
@todo 
Report Policyの仕様は変えるかもしれない。
*/
#ifndef DKUTIL_TIMER_HPP
#define DKUTIL_TIMER_HPP

#include <helper/boost_assert.hpp>
#include <helper/windows.h>
#include <dkutil/boost/progress.hpp>
#include <dkutil/boost/format.hpp>
#include <dkutil/boost/utility.hpp>

#include <dkutil/s34/list_sort.h>
#include <dkutil/output/logger.hpp>
#include <dkutil/boost/parm_string.hpp>

#ifdef WIN32
#	include <mmsystem.h>
#endif
#ifdef _MSC_VER
#	pragma comment(lib,"winmm")
#	include <dkutil/cpu.hpp>
#endif


namespace dkutil{

namespace policy{

///ProgressRealCPUClockTiemrやProgressClockTimerクラスに入れるOutputポリシーのベースクラス。
struct ReportBase{
	BOOST_STATIC_CONSTANT(size_t,	enuCharBuffSize = 1024);
};
///printfでリポート
struct ConsoleReport : public ReportBase
{
  static void Output(double t,const char *f,const char *str,...) {
		char s[enuCharBuffSize]="";
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),str);
		printf(s); 
	}
};
///fprintf(stderr,... でリポート
struct ConsoleErrorReport : public ReportBase
{
  static void Output(double t,const char *f,const char *str,...) {
		char s[enuCharBuffSize]="";
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),str);
		fprintf(stderr,s); 
	}
};
///デバッグWindowでリポート
struct OutputDebugStringReport : public ReportBase{
	static void Output(double t,const char *f,const char *str,...) { 
		char s[enuCharBuffSize]="";
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),str);
		OutputDebugString(s);
	}
};
///ログファイルにリポート
struct LogFileReport : public ReportBase{
	static void Output(double t,const char *f,const char *str,...) {
		char s[enuCharBuffSize]="";
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),str);
		if(f){
			dkutil::dAddLog(f,s);
		}
	}
};
///リポートをしない
struct NoReport : public ReportBase{
	static void Output(double t,const char *f,const char *str,...) {
		return;
	}
};
///d金魚用デバッグリポート
struct DebugReport : public ReportBase{


	static void Output(double t,const char *f,const char *str,...) {
		char s[enuCharBuffSize]="";
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),str);
		ConsoleReport::Output(t,f,s); 
		OutputDebugStringReport::Output(t,f,s);
	}
};

}//end of policy namespace



template<class YOUR_CLASS,class OutputPolicy=policy::OutputDebugStringReport>
class progress_base : public YOUR_CLASS{
	OutputPolicy mPolicy;
	//char mFileName[MAX_PATH];
	std::string mFileName;
public:
	explicit	progress_base(const char *filename="debug.txt"){
		SetFileName(filename);
		restart();
	}
	virtual ~progress_base(){
		double time = elapsed();
		mPolicy.Output(time,GetFileName(),GetOutputSignature(true),time);
	}
	OutputPolicy &GetOutputPolicy(){return mPolicy;}
	const char *GetFileName()const {return mFileName.c_str();}
	bool SetFileName(const char *s){
		try{
			mFileName = s;
		}catch(...){
			return false;
		}
		return true;
		//	return DKUTIL_SUCCEEDED(dkstrcpy(mFileName,sizeof(mFileName),s,strlen(s)));
	}
};








#ifdef _MSC_VER


#pragma warning(disable:4035)

//#if 0
/*!
参考サイト：
超高速プログラミング入門受講者のページ
http://www.super-computing.org/sr8000/
*/
class RealCPUClock  : private boost::noncopyable{
	CPUInfo info;
	bool mIsTSC;
	typedef ULONGLONG VAR_TYPE;
	VAR_TYPE mStartTime;
public:

	inline VAR_TYPE __fastcall RDTSC( )const
	{
		_asm{
			rdtsc
	//   _emit 0xf    ← rdtsc でエラーが出る古いバージョン
	//   _emit 0x31   ← の環境のとき
		}
		/*
	//rdtsc または　以下のような感じ･･･？？なんかよく分からん。
　mov ebx,aa
　__emit 0x0F
　__emit 0x31
	*/
	}



	/*
	inline ULONGLONG  RDTSC()const{
		typedef VAR_TYPE __fastcall (*RDTSC_TYPE)();
		RDTSC_TYPE t;
		static const BYTE rdtsc[]={
			0x55									 //push				ebp
			,0x8B,0xEC								//mov		,0x ebp,esp
			,0x83,0xEC,0x44						 //sub				 esp,44h
			,0x53									 //push				ebx
			,0x56								 //	push				esi
			,0x57								 //	push				edi
			,0x51								//	 push				ecx
			,0x8D,0x7D,0xBC				 //		lea				 edi,[ebp-44h]
			,0xB9,0x11,0x00,0x00,0x00		//	 mov				 ecx,11h
			,0xB8,0xCC,0xCC,0xCC,0xCC		//	 mov				 eax,0CCCCCCCCh
			,0xF3,0xAB						 //	 rep stos		dword ptr [edi]
			,0x59								//	 pop				 ecx
			,0x89,0x4D,0xFC					//	 mov				 dword ptr [ebp-4],ecx
			,0x0F,0x31						 //	 rdtsc
			,0x5F								//	 pop				 edi
			,0x5E								//	 pop				 esi
			,0x5B								//	 pop				 ebx
			,0x83,0xC4,0x44					//	 add				 esp,44h
			,0x3B,0xEC						 //	 cmp				 ebp,esp
			,0xE8,0x14,0x34,0x00,0x00		//	 call				__chkesp (00405fb0)
			,0x8B,0xE5						 //	 mov				 esp,ebp
			,0x5D								//	 pop				 ebp
			,0xC3								//	 ret
		};
		t = (RDTSC_TYPE)&rdtsc;
		ULONGLONG l=t();
		return l;
	}
	*/
	inline VAR_TYPE get_clock_()const{	return RDTSC();}
	inline VAR_TYPE calc_clock_(VAR_TYPE c)const{ return RDTSC() - c;}
/*
	VAR_TYPE get_clock_()const{
		VAR_TYPE CPUClock=0;	
		{
			__asm
			{
				RDTSC
				MOV		[CPUClock],		EAX
			}
		}
		return CPUClock;
	}
	VAR_TYPE calc_clock_(VAR_TYPE c)const{
		VAR_TYPE Now;
		__asm
		{
			RDTSC
			SUB		EAX,			[Now]
			MOV		[Now],		EAX
		}
		return Now - c;
	}
*/
	RealCPUClock(){
		mIsTSC = info.isTSC();
		mStartTime = 0;
		restart();
	}
	void restart() { 
		if(mIsTSC==false){
			throw std::logic_error("CPUTimer:CPUのTSCが使えない");
		}
		mStartTime = get_clock_();
	}
  double elapsed() const {  
		return (double)((LONGLONG)calc_clock_(mStartTime));
	}
	const char *GetOutputSignature(bool flag)const{
		const char *ptr;
		if(flag){
			static const char *ptr1="RealCPUClock:%f\n";
			ptr = ptr1;
		}else{
			static const char *ptr2="RealCPUClock";
			ptr = ptr2;
		}
		return ptr;
	}
	static const char *getFormat(){
		static const char *p = "%d";
		return p;
	}
	VAR_TYPE getTime()const{
		return calc_clock_(mStartTime);
	}
};//timer

#pragma warning(default:4035)


///ProcessTimerクラスのテンプレートにぶち込む奴。MilliSecondClock ( ::timeGetTime() )を使っている
template<class ReportPolicy=policy::OutputDebugStringReport>
struct ProgressRealCPUClockTimer : public progress_base< RealCPUClock,ReportPolicy>{
	typedef progress_base< RealCPUClock,ReportPolicy> BASE_TYPE;
	ProgressRealCPUClockTimer(const char *filename="debug.txt") : BASE_TYPE(filename)
	{}
	virtual ~ProgressRealCPUClockTimer(){}
};
//#endif //end of if 0
#endif //end of _MSC_VER

#ifdef WIN32

class MilliSecondClock  : private boost::noncopyable{
	DWORD mStartTime;
public:
	MilliSecondClock(){
		mStartTime = 0;
		restart();
	}
	void restart() { 
		mStartTime = ::timeGetTime();
	}
  double elapsed() const {  
		return (double)((DWORD)(::timeGetTime() - mStartTime));
	}
	const char *GetOutputSignature(bool flag)const{
		const char *ptr;
		if(flag){
			static const char *ptr1="MilliSecondClock:%f\n";
			ptr = ptr1;
		}else{
			static const char *ptr2="MilliSecondClock";
			ptr = ptr2;
		}
		return ptr;
	}
	static const char *getFormat(){
		static const char *p = "%d";
		return p;
	}
	DWORD getTime()const{
		DWORD t = ::timeGetTime();
		return t - mStartTime;
	}
};

class QueryPerformanceClock : private boost::noncopyable{
	LARGE_INTEGER mStartTime;
public:
	QueryPerformanceClock(){
		mStartTime.QuadPart = 0;
		restart();
	}
	void restart() { 
		LARGE_INTEGER t;
		if(FALSE==QueryPerformanceFrequency(&t)){
			DKUTIL_THROW_OR_NOTICE(std::runtime_error("QueryPerformanceFrequency() not support"));
			return;
		}
		QueryPerformanceCounter(&mStartTime);
	}
  double elapsed() const {  
		register LARGE_INTEGER t;
		QueryPerformanceCounter(&t);
		return (double)((LONGLONG)(t.QuadPart - mStartTime.QuadPart));
	}
	const char *GetOutputSignature(bool flag)const{
		const char *ptr;
		if(flag){
			static const char *ptr1="QueryPerformanceClock:%lf\n";
			ptr = ptr1;
		}else{
			static const char *ptr2="QueryPerformanceClock";
			ptr = ptr2;
		}
		return ptr;
	}
	static const char *getFormat(){
		static const char *p = "%d";
		return p;
	}
	LONGLONG getTime()const{
		register LARGE_INTEGER t;
		QueryPerformanceCounter(&t);
		return t.QuadPart - mStartTime.QuadPart;
	}
};

#endif

struct BoostTimerImpl : public boost::timer{
	const char *GetOutputSignature(bool flag)const{
		const char *ptr;
		if(flag){
			static const char *ptr1="boost::timer(std::clock()):%f\n";
			ptr = ptr1;
		}else{
			static const char *ptr2="boost::timer(std::clock())";
			ptr = ptr2;
		}
		return ptr;

	}
};

///ProcessTiemrクラスのテンプレートにぶち込む奴。boost::timer ( std::clock() )を使っている
template<class ReportPolicy=policy::OutputDebugStringReport>
struct ProgressClockTiemr : public progress_base<BoostTimerImpl,ReportPolicy>{
	typedef progress_base<BoostTimerImpl,ReportPolicy> BASE_TYPE;
	ProgressClockTiemr(const char *filename="debug.txt") : BASE_TYPE(filename)
	{}
};

///ProcessTiemrクラスのテンプレートにぶち込む奴。MilliSecondClock ( ::timeGetTime() )を使っている
template<class ReportPolicy=policy::OutputDebugStringReport>
struct ProgressMilliSecondTiemr : public progress_base<MilliSecondClock,ReportPolicy>{
	typedef progress_base<MilliSecondClock,ReportPolicy> BASE_TYPE;
	ProgressMilliSecondTiemr(const char *filename="debug.txt") : BASE_TYPE(filename)
	{}
	virtual ~ProgressMilliSecondTiemr(){}
};


template<class T>
struct ProcessTimer{
	//local_singleton<T> mSgtn;
	T *mPtr;
	ProcessTimer(){mPtr=NULL;}
	virtual ~ProcessTimer(){
		end();
	}
	void begin(){
		if(mPtr){
		}else{
			mPtr = new T;
		}
	
		/*
		if(mSgtn.isNull()){
			mSgtn.CheckInstance();
		}else{
			DEBUGMB("Don't call twice or more.");
		}
		*/
	}
	void end(){
		if(mPtr){
			delete mPtr;
			mPtr=NULL;
		}
		//mSgtn.Release();
	}
};

///最終的に簡単なこれに収束
template<class POLICY__1=policy::ConsoleReport,class POLICY__2=policy::OutputDebugStringReport>
class scoped_processtimer_base{
#ifdef _MSC_VER
	typedef RealCPUClock timer_type;
#else
	typedef MilliSecondClock tiemr_type;
#endif
	timer_type mT;
public:
	scoped_processtimer_base(){
		mT.restart();
	}
	~scoped_processtimer_base(){
		double time = elapsed();
		POLICY__1::Output(time,NULL,mT.GetOutputSignature(true),time);
		POLICY__2::Output(time,NULL,mT.GetOutputSignature(true),time);
		//policy::ConsoleReport::Output(time,NULL,mT.GetOutputSignature(),time);
		//policy::OutputDebugStringReport::Output(time,NULL,mT.GetOutputSignature(),time);
	}
	void restart(){
		mT.restart();
	}
	double elapsed() const {
		return mT.elapsed();
	}
};
typedef scoped_processtimer_base<> scoped_processtimer;







/**
@section 使い方
{
	ranking_timer boss;

	{
		//クリティカルセクションを使うと正確になるはず？
		scoped_criticalsection section;
		ranking_timer::scoped_timer timer("calc1",&boss);
		calc1();//計りたい処理
		//scoped_tiemrがscope out 時にbossにタイムを送る。
	}
	{
		synchronized swimming;
		ranking_timer::scoped_timer timer("calc2",&boss);
		calc2();//計りたい処理2
	}
	//ranking_timer scope out 時にランキングを表示
}

*/
namespace policy{
#ifdef _MSC_VER
	typedef RealCPUClock ranking_timer_type;
#else
	typedef MilliSecondClock ranking_timer_type;
#endif
}//end

template<class ClockPolicy = policy::ranking_timer_type,class OutputPolicy = policy::DebugReport>
class ranking_timer_base : public boost::noncopyable{
public:

	typedef typename ClockPolicy timer_type;
	typedef typename ranking_timer_base<ClockPolicy,OutputPolicy> self_type;
	typedef typename boost::noncopyable base_type;
	typedef typename self_type self_ranking_timer_type;

	typedef typename map_ex_adapter<std::map<std::string,double> > CONTAINER_TYPE;
	typedef typename CONTAINER_TYPE::DATA_TYPE DATA_TYPE;
	
	struct SORT_DATA{
	private:
		double mTime;
		std::string mName;
	public:
		const char *c_str()const{return mName.c_str();}
		double time()const{return mTime;}
		SORT_DATA(double t,const char *s) : mTime(t) , mName(s){}
		SORT_DATA(const SORT_DATA &x){
			*this = x;
		}
			
		struct less : public std::binary_function<SORT_DATA,SORT_DATA,bool> 
		{
			bool operator()(const SORT_DATA& __x, const SORT_DATA& __y) const {
				return __x.mTime < __y.mTime;; 
			}
			friend SORT_DATA;
		};
		SORT_DATA &operator =(const SORT_DATA &x){
			mTime = x.mTime;
			mName = x.mName;
			return *this;
		}
		friend bool operator <(const SORT_DATA &x,const SORT_DATA &y){//less
			return x.mTime < y.mTime;

		}
		friend bool operator ==(const SORT_DATA &x,const SORT_DATA &y){//less
			return x.mTime < y.mTime;

		}
		friend SORT_DATA::less;
	};
private:
	timer_type mT;
	CONTAINER_TYPE mC;
	///ファイル名
	std::string mFName;

	typedef std::list<SORT_DATA> SORTED_CONTAINER;

	//ソートしてやる
	void to_sorted_data(SORTED_CONTAINER &vec)const{

		{//ソート用データを入れる。
			CONTAINER_TYPE::const_iterator it = mC.begin();
		
			for(;it != mC.end();it++)
			{
				double time = (*it).second;
				vec.push_back(
					SORT_DATA(
						time,	 
						(*it).first.c_str()
					)
				);
			}
		}
		//std::less<SORT_DATA> less_functor__;
		SORT_DATA::less less_functor__;
		stx::comb_sort(vec.begin(),vec.end(),less_functor__);

	}
		
public:
	///csv形式でデータを出力
	std::string get_csv_result()const{
		SORTED_CONTAINER vec;
		to_sorted_data(vec);
		
		size_t i = 1;
		std::string ts;
		std::string re;
		SORTED_CONTAINER::const_iterator it;
		for(it=vec.begin();it != vec.end();it++,i++)
		{
			ts += boost::io::str(
				boost::format("\"%d\",\"%f\",\"%s\"") 
					% i % (*it).time() % (*it).c_str() 
			);
			ts += "\n";
		}
	
	}
	virtual void sort_output()const{
#	if defined(DEBUG) || defined(_DEBUG)
		const char *cmode = "DEBUG";
#	else
		const char *cmode = "RELEASE";
#	endif

		OutputPolicy::Output(0,mFName.c_str(),"ranking_tiemr / clock type : %s / compile mode : %s"
			,mT.GetOutputSignature(false),cmode);
		//typedef std::vector<SORT_DATA> vtype;
		SORTED_CONTAINER vec;
		to_sorted_data(vec);
		{
			size_t i = 1;
			std::string ts;
			for(SORTED_CONTAINER::const_iterator it=vec.begin();
			it != vec.end();it++,i++)
			{
				ts = boost::io::str(boost::format("%d\t/ %s\t/ %lf") % i % (*it).c_str() % (*it).time());
				OutputPolicy::Output((*it).time(),mFName.c_str(),ts.c_str());
			}
		}
		/*
		typedef std::list<SORT_DATA> vtype;
		vtype vec;

		{//ソート用データを入れる。
			CONTAINER_TYPE::const_iterator it = mC.begin();
		
			for(;it != mC.end();it++)
			{
				double time = (*it).second;
				vec.push_back(
					SORT_DATA(
						time,	 
						(*it).first.c_str()
					)
				);
			}
		}
		//std::less<SORT_DATA> less_functor__;
		SORT_DATA::less less_functor__;
		stx::comb_sort(vec.begin(),vec.end(),less_functor__);
		
		{
			size_t i = 1;
			std::string ts;
			for(vtype::const_iterator it=vec.begin();
			it != vec.end();it++,i++)
			{
				ts = boost::io::str(boost::format("%d\t/ %s\t/ %f") % i % (*it).c_str() % (*it).time());
				OutputPolicy::Output((*it).time(),mFName.c_str(),ts.c_str());
			}
		}
		*/
		/*
		dODS("%s",mT.GetOutputSignature());
		//typedef std::vector<SORT_DATA> vtype;
		typedef std::list<SORT_DATA> vtype;
		vtype vec;

		{//ソート用データを入れる。
			CONTAINER_TYPE::const_iterator it = mC.begin();
		
			for(;it != mC.end();it++)
			{
				double time = (*it).second;
				std::string str = 
					boost::io::str(boost::format("%s\t:\t%f\t") % (*it).first % (*it).second);
				vec.push_back(SORT_DATA(
					time,	 
					str.c_str()
				));

			}
		}
		std::less<SORT_DATA> less_functor__;
		stx::comb_sort(vec.begin(),vec.end(),less_functor__);
		
		{
			for(vtype::const_iterator it=vec.begin();
			it != vec.end();it++)
			{
				dODS("%s",(*it).c_str());
			}
		}
		*/
	}
	ranking_timer_base(){	}
	virtual ~ranking_timer_base(){
		sort_output();
	}
	///ranking_timer_baseクラスが使うタイマー
	struct scoped_timer{
		timer_type mT;
		self_ranking_timer_type *mPtr;
		std::string mName;
		/*!
		@param name[in] 出力する時に一緒に出力する文字列（いわいる識別子）
		@param ptr[in] ranking_timer_baseクラスへのポインタ
		@todo
		このranking_timer_baseクラスへのポインタを渡すところが
		ダサくて嫌いなのですが、これしか思いつきません＾＾；
		どちら様か、奨励できる実装がありましたら、どうか教えてください。m(_ _)m
		*/
		scoped_timer(const char *name,self_ranking_timer_type *ptr){
			dkcmFORCE_NOT_ASSERT(NULL==ptr);
			mPtr = ptr;
			mName = name;
			mT.restart();
		}
		~scoped_timer(){
			double t = mT.elapsed();
			if(false==mPtr->insert(mName.c_str(),t))
			{
				dkcmFORCE_NOT_ASSERT("ranking_timer_base::scoped_timer \
					すでに同じ名前が登録されているぞ！！！");
			}
		}
		void restart(){
			mT.restart();
		}
		double elapsed() const {
			return mT.elapsed();
		}
	};
		///ranking_timer_baseクラスが使うタイマー
	template<class RTT>
	struct scoped_timer_base{
		timer_type mT;
		RTT *mPtr;
		std::string mName;
		/*!
		@param name[in] 出力する時に一緒に出力する文字列（いわいる識別子）
		@param ptr[in] ranking_timer_baseクラスへのポインタ
		@todo
		このranking_timer_baseクラスへのポインタを渡すところが
		ダサくて嫌いなのですが、これしか思いつきません＾＾；
		どちら様か、奨励できる実装がありましたら、どうか教えてください。m(_ _)m
		*/
		scoped_timer_base(const char *name,RTT *ptr){
			dkcmFORCE_NOT_ASSERT(NULL==ptr);
			mPtr = ptr;
			mName = name;
			mT.restart();
		}
		~scoped_timer_base(){
			double t = mT.elapsed();
			if(false==mPtr->insert(mName.c_str(),t))
			{
				dkcmFORCE_NOT_ASSERT("ranking_timer_base::scoped_timer \
					すでに同じ名前が登録されているぞ！！！");
			}
		}
		void restart(){
			mT.restart();
		}
		double elapsed() const {
			return mT.elapsed();
		}
	};
	typedef typename scoped_timer_base<self_type> scoped_timer_t;
	bool insert(const char *name,double time){
		//boost::io::str( boost::format("%d") % ui);
	

		return mC.SetData(std::string(name),time);
	}
	void clear(){
		mC.clear();
	}
	bool erase(const char *name){
		return mC.DeleteDataByKey(name);
	}
	void setFilename(parm_string str){
		mFName = str.c_str();
	}
	void print(parm_string str){
		OutputPolicy::Output(0,mFName.c_str(),str.c_str());
	}
};

typedef ranking_timer_base<> ranking_timer;
#ifdef WIN32
typedef ranking_timer_base<MilliSecondClock> msec_ranking_timer;
typedef ranking_timer_base<QueryPerformanceClock> qp_ranking_timer;
#endif
//typedef ranking_timer_base<policy::DebugReport> ranking_timer;
//typedef ranking_timer_base ranking_timer;

#define BEGIN_DKINGYO_CPUCLOCK_DOUT() { \
	dkutil::progress_base<boost::timer> *p_dkingyocpu = new progress_base<boost::timer>

#define END_DKINGYO_CPUCLOCK_DOUT() delete p_dkingyocpu; }

#define BEGIN_DKINGYO_CPUCLOCK_LOG(s) { \
dkutil::progress_base<boost::timer,policy::LogFileReport> *p_dkingyocpu = \
		new dkutil::progress_base<boost::timer,policy::LogFileReport>(s)

#define END_DKINGYO_CPUCLOCK_LOG() delete p_dkingyocpu; }


}//end of dkutil namespace


#endif //end of include once