
/*!
@auther d金魚
@since 2003/x/x
@note
rewrite:2003/11/18
*/
#ifndef _dkutilTimer__
#define _dkutilTimer__

#include "dkutilCompileOption.h"
#include <boost/progress.hpp>
#if DKUTIL_COMPILE_OPTION_MSVC
#	pragma comment(lib,"winmm")
#	include "dkutilCPU.h"
#endif


namespace dkutil{

namespace policy{

struct ReportBase{
	enum{
		enuCharBuffSize = 1024,
	};
};

struct ConsoleReport : public ReportBase
{
  static void Output(double t,const char *f,char *str,...) {
		char s[enuCharBuffSize]="";
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),str);
		printf(s); 
	}
};
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,char *str,...) {
		char s[enuCharBuffSize]="";
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),str);
		if(f){
			dkutil::dAddLog(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];
public:
	explicit	progress_base(char *filename="debug.txt"){
		SetFileName(filename);
		restart();
	}
	virtual ~progress_base(){
		double time = elapsed();
		mPolicy.Output(time,GetFileName(),GetOutputSignature(),time);
	}
	OutputPolicy &GetOutputPolicy(){return mPolicy;}
	const char *GetFileName()const {return mFileName;}
	bool SetFileName(char *s){
		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   ← の環境のとき
		}
	}



	/*
	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(calc_clock_(mStartTime));
	}
	const char *GetOutputSignature(){
		static const char *ptr="RealCPUClock:%f\n";
		return ptr;
	}
};//timer

#pragma warning(default:4035)


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



class MilliSecondClock  : private boost::noncopyable{
	DWORD mStartTime;
public:
	MilliSecondClock(){
		mStartTime = 0;
		restart();
	}
	void restart() { 
		mStartTime = ::timeGetTime();
	}
  double elapsed() const {  
		return (double)::timeGetTime() - mStartTime;
	}
	const char *GetOutputSignature(){
		static const char *ptr="MilliSecondClock:%f\n";
		return ptr;
	}
};

struct BoostTimerImpl : public boost::timer{
	const char *GetOutputSignature(){
		static const char *ptr="boost::timer(std::clock()):%f\n";
		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(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(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();
	}
};


#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