/*!
@note
葉迩倭氏の
<a href="http://www.twin-tail.jp/">Luna3rd</a>を参考にしています。
*/

#ifndef DKUTIL_CPU_HPP
#define DKUTIL_CPU_UPP

#include <dkutil/define.hpp>
#include <dkutil/string.hpp>

namespace dkutil{

#ifndef _MSC_VER
///はっきり言って使えるかどうかはわからない･･･
inline ULONGLONG RDTSC(){
	typedef ULONGLONG (__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 = reinterpret_cast<RDTSC_TYPE>(&rdtsc);
	return t();
}
#else
///for MSVC


#pragma warning(disable:4035)
inline ULONGLONG __fastcall RDTSC(){
	_asm{
		RDTSC
	}
}
#pragma warning(default:4035)
#endif


class CPUInfo{
public:
	struct CPU_INFO{
		char CPUName[256];
		char CPUType[128];
		bool bFPU			;
		bool bTSC		;
		bool bCMOV		;
		bool bFCMOV		;
		bool bCPUID		;
		bool bMMX	;
		bool bMMX2		;
		bool bSSE		;
		bool bSSE2	;
		bool b3DNOW			;
		bool bE3DNOW		;
		long TypeID		;
		long FamilyID	;
		long ModelID;
		long SteppingID	;
		long CPUClock	;
	};
private:
	static CPU_INFO minfo;
//BOOST_PROTECTED:
	CPU_INFO &GetInfoRef(){return minfo;}
public:
	CPUInfo(){
		::memset(&minfo,0,sizeof(minfo));
		GetCPUInformation();
	}
	virtual ~CPUInfo(){}
	
	///MMXが使えるならばtrue
	bool isMMX(){return minfo.bMMX;}
	///MMX2が使えるならばtrue
	bool isMMX2(){return minfo.bMMX2;}
	///SSEが使えるならばtrue
	bool isSSE(){return minfo.bSSE;}
	///SSE2が使えるならばtrue
	bool isSSE2(){return minfo.bSSE2;}
	///3DNowが使えるならばtrue
	bool is3DNOW(){return minfo.b3DNOW;}
	///エンハンスド3DNowが使えるならばtrue
	bool isE3DNOW(){return minfo.bE3DNOW;}
	///@return CPUClockがMHz単位で帰ります。
	long CPUClock(){
		if(minfo.CPUClock==0){
			minfo.CPUClock = BenchCPUClock(minfo.bTSC);
		}
		return minfo.CPUClock;
	}
	///@return FPUが使えるならTRUE
	bool isFPU(){return minfo.bFPU;}
	///@return TSCが使えるならTRUE
	static bool isTSC(){return minfo.bTSC;}
	///@return CPUIDが使えるのならばtrue
	bool isCPUID(){return minfo.bCPUID;}
	/*!
	@param buff[in] 文字列の格納先へのポインタ 256byte以上を奨励
	@param buffsize[in] バッファのサイズ
	*/
	///CPUの名前をゲットします。
	void CopyCPUName(char *buff,size_t buffsize){
		strncpy(buff,minfo.CPUName,buffsize);
	}


	///CopyCPUName()を見てください。
	///CPUTypeをゲットします。CPUTypeが未だに何なのかは謎(爆
	void CopyCPUType(char *buff,size_t buffsize){
		strncpy(buff,minfo.CPUType,buffsize);
	}

	///CPUクロックの計測に約1秒かかるので
	///使う時と場所を良く考えてから使用してください。
	///@param bTSC[in] CPUのTSCが有効かどうか？（でも、デフォルト引数で渡しているので実際は渡さなくてOK
	///@return CPUClock(MHz単位)
	long BenchCPUClock(long bTSC=isTSC());
	///Luna3rdより
	///@param isAll[in] trueにするとBenchCPUClock()を内部で実行する。
	void GetCPUInformation( bool isAll=false );
	
	/*!
	@param a[in] 文字の格納場所(270byte以上確保してください。)
	@param buffsize[in] aの格納容量サイズ
	*/
	///CPU情報を可視化(文字列化)する。
	void CPUInfoToCharPtr(char *a,size_t buffsize);
	/*
	///@param logger[in] HTMLLogクラスを入れます。
	void LogoutHTML(HTMLLog &logger)
	{
		//--------------------------------------------------
		// チェック完了〜
		//--------------------------------------------------
		char CPUName[256]="";
		if ( (strlen(minfo.CPUType)>0) && (strlen(minfo.CPUName)>0) )
		{
			strncpy(CPUName,minfo.CPUName,256);
			strncat( CPUName, " - " ,256);
			strncat( CPUName, minfo.CPUType,256 );
		}
		else
		{
			strncpy( CPUName, "UnKnown" ,256);
		}

		// ログ
		logger.LogoutKeyword( "CPU Information" );
		logger.LogoutNextLine();

		logger.Logout( "　　　Name [ %s ]", CPUName );
		logger.LogoutNextLine();

		logger.Logout( "　　　Clock [ %uMHz ]", minfo.CPUClock );
		logger.LogoutNextLine();

		logger.Logout( "　　　Type [ %u ] Family [ %u ] Model [ %u ] Stepping [ %u ]",
			minfo.TypeID, minfo.FamilyID, minfo.ModelID, minfo.SteppingID );
		logger.LogoutNextLine();

		logger.Logout( "　　　FPU %s : CPUID %s : MMX %s\n",
			(minfo.bFPU  )?("○"):("×"),
			(minfo.bCPUID )?("○"):("×"), (minfo.bMMX )?("○"):("×") );
		logger.LogoutNextLine();
		logger.Logout( "　　　MMX2 %s : SSE %s : SSE2 %s\n",
			(minfo.bMMX2 )?("○"):("×"),
			(minfo.bSSE   )?("○"):("×"), (minfo.bSSE2)?("○"):("×") );
		logger.LogoutNextLine();
		logger.Logout( "　　　3DNow %s : E3DNow %s : CMOV %s\n",
			(minfo.b3DNOW)?("○"):("×"), (minfo.bE3DNOW)?("○"):("×"),
			(minfo.bCMOV)?("○"):("×") );
		logger.LogoutNextLine();
		logger.Logout( "　　　FCMOV %s : TSC %s\n",
			(minfo.bFCMOV)?("○"):("×"), (minfo.bTSC   )?("○"):("×") );
		logger.LogoutNextLine();

		logger.LogoutNextLine();
	}
	*/

};

}//end of dkutil namespace

#ifndef DKUTIL_DETAIL_CPU_CPP
//#	include <dkutil/detail/cpu.cpp>
#endif
	
#endif //end of include once
