
#ifndef DKUTIL_OUTPUT_H
#define DKUTIL_OUTPUT_H

#include <string>
#include <dkutil/macro.hpp>
#include <dkutil/bit.hpp>
#include <dkutil/synchronized.hpp>

//#include <dkutil/output/logger.hpp>
#include <dkutil/parser/radix_convert.hpp>

namespace dkutil{
	
//extern bool ulltoa(ULONGLONG,char *,size_t);


/*!
できればReleaseビルド時は使わないようにしてください。内部で面倒な処理が入りますので。<br>
*/
///OutputDebugStringの引数拡張OKバージョン(Debugビルドのみしか機能しません。)
inline bool dOutputDebugString(char *str,...){
	char s[1024 * 2];
	SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),str);
	OutputDebugString(s);
	//fprintf(stderr,s);
	return true;
}

#ifdef dODS
#	pragma message("dODS defined... undef dODS")
#	undef dODS
#endif

#if 0
/// なんかC++のクラス + 可変引数って相性悪いみたい・・・
inline void dODS_base(parm_string base,...){
	char s[2048]="";
	const char *str = base.c_str();
	SET_VA_LIST_INSERT_YEN_N(s,sizeof(s) - 1,str);
	dOutputDebugString(s);

}


#else
inline void dODS_base(const char *str,...){
	char s[1024]="";
	SET_VA_LIST_INSERT_YEN_N(s,sizeof(s) - 1,str);
	dOutputDebugString(s);
	/*char s[1024];
	SET_VA_LIST_INSERT_YEN_N(s,1024,str);
	OutputDebugString(s);*/
}
inline void dODS_base(const std::string &str){
	dODS_base((const char *)str.c_str());
}
#endif
inline void dODSNoConst(char *str,...){
	char s[1024]="";
	SET_VA_LIST_INSERT_YEN_N(s,1024,str);
	dOutputDebugString(s);
}
inline void dODSNoNewLine(const char *str,...){
	char s[1024]="";
	SET_VA_LIST(s,1024,str);
	OutputDebugString(s);
	//fprintf(stderr,s);
}
#define dODS dkutil::dODS_base

#if defined(DEBUG) || defined(_DEBUG)
#define dODSDebug dODS
#else
#define dODSDebug
#endif
/*!
@note
ULONGLONG型でしか表現できない変数は上手く出力できない模様
*/
inline bool SuperODS(const char *str,...){
	char s[2048]="";
	SET_VA_LIST(s,sizeof(s),str);
	dOutputDebugString(s);
	printf(s);
	return true;
}

inline bool ULONGLONG_ODS(ULONGLONG ull){
	char buff[32]="";
	ulltoa(ull,buff,sizeof(buff));
	::OutputDebugString(buff);
	return true;
}


inline void BitODS_base(ULONGLONG b,size_t bit){
	char buff[64 + 1];
	BitIsVisualized(buff,sizeof(buff),b);
	buff[bit]='\0';
	dODS(buff);
}
inline void BitODS(BYTE b){
	BitODS_base(b,8);
}
inline void BitODS(USHORT b){
	BitODS_base(b,16);
}
inline void BitODS(UINT b){
	BitODS_base(b,32);
}
inline void BitODS(ULONG b){
	BitODS_base(b,32);
}
inline void BitODS(ULONGLONG b){
	BitODS_base(b,64);
}
	
/*
class Win32LastError{
	DKConfigUtility mUtil;
	static std::string mbuf;
protected:
	void MTLockToString(std::string &mbuf,DWORD ErrorID,bool flag)
	{
		DKUTIL_LOCKTHREAD(mUitl);
		ToString(mbuf,ErrorID,flag);
		DKUTIL_UNLOCKTHREAD(mUitl);
	}
public:
	Win32LastError(){}
	virtual ~Win32LastError(){}
	const char *operator()(){
		MTLockToString(mbuf,0,false);
		return mbuf.c_str();
	}
	const char *operator()(DWORD ErrorID){
		MTLockToString(mbuf,ErrorID,true);
		return mbuf.c_str();
	}
	void ToString(std::string &buf,DWORD ErrorID,bool flag=false)
	{
		mbuf.clear();
		LPVOID lpMsgBuf;
		if(flag){
			::FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, 
				ErrorID, 
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL 
				);
		}else{
			::FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, 
				GetLastError(), 
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL 
				);
		}
		mbuf = "ERROR Message:";
		mbuf += (LPCTSTR)lpMsgBuf;
		LocalFree( lpMsgBuf );
	}

	DWORD GetLastError(){
		DWORD ErrorID;
		DKUTIL_MULTITHREAD_SYNDROME_LOCK(mUitl);
		ErrorID = ::GetLastError();
		DKUTIL_MULTITHREAD_SYNDROME_UNLOCK(mUitl);
		return ErrorID;
	}
};


std::string Win32LastError::mbuf;
*/
class Win32LastError{
	std::string mbuf;
protected:
	void MTLockToString(std::string &mbuf,DWORD ErrorID,bool flag)
	{
		//last error値はthread localなので別にロックしなくてOKだったらしい。
		//synchronized swimming;
		ToString(mbuf,ErrorID,flag);
	}
public:
	Win32LastError(){}
	virtual ~Win32LastError(){}
	const char *operator()(){
		MTLockToString(mbuf,0,false);
		return mbuf.c_str();
	}
	const char *operator()(DWORD ErrorID){
		MTLockToString(mbuf,ErrorID,true);
		return mbuf.c_str();
	}
	/*!
	@param buf[out] 文字を格納するstd::stringのバッファ
	@param ErrorID[in] GetLastError()で獲得したID 次の引数flagがTRUEだったら別にどんな値を渡しても構わない。
	@param trueだと、内部でGetLastError()を呼び出す。
	*/
	void ToString(std::string &buf,DWORD ErrorID,bool flag=false)
	{
		mbuf.clear();
		LPVOID lpMsgBuf;
		if(flag){
			::FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, 
				ErrorID, 
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL 
				);
		}else{
			::FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, 
				GetLastError(), 
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL 
				);
		}
		mbuf = "ERROR Message:";
		mbuf += (LPCTSTR)lpMsgBuf;
		LocalFree( lpMsgBuf );
	}

	DWORD GetLastError(){
		synchronized swimming;
		DWORD ErrorID;
		//DKUTIL_MULTITHREAD_SYNDROME_LOCK(mUtil);
		ErrorID = ::GetLastError();
		//DKUTIL_MULTITHREAD_SYNDROME_UNLOCK(mUtil);
		return ErrorID;
	}
};

inline std::string GetWin32LastErrorString(){
	synchronized swimming;
	Win32LastError err;
	return std::string(err());
}
inline void OutputWin32LastError(){
	SuperODS(GetWin32LastErrorString().c_str());
}
inline int Win32LastErrorBox(){
	return MessageBox(NULL,GetWin32LastErrorString().c_str(),"Win32LastError()",MB_OK);
}


#if 0
class Win32LastError{
	DKConfigUtility mUtil;
	std::string mbuf;
protected:
	void MTLockToString(std::string &mbuf,DWORD ErrorID,bool flag)
	{
		DKUTIL_LOCKTHREAD(mUtil);
		ToString(mbuf,ErrorID,flag);
		DKUTIL_UNLOCKTHREAD(mUtil);
	}
public:
	Win32LastError(){}
	virtual ~Win32LastError(){}
	const char *operator()(){
		MTLockToString(mbuf,0,false);
		return mbuf.c_str();
	}
	const char *operator()(DWORD ErrorID){
		MTLockToString(mbuf,ErrorID,true);
		return mbuf.c_str();
	}
	/*!
	@param buf[out] 文字を格納するstd::stringのバッファ
	@param ErrorID[in] GetLastError()で獲得したID 次の引数flagがTRUEだったら別にどんな値を渡しても構わない。
	@param trueだと、内部でGetLastError()を呼び出す。
	*/
	void ToString(std::string &buf,DWORD ErrorID,bool flag=false)
	{
		mbuf.clear();
		LPVOID lpMsgBuf;
		if(flag){
			::FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, 
				ErrorID, 
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL 
				);
		}else{
			::FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, 
				GetLastError(), 
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL 
				);
		}
		mbuf = "ERROR Message:";
		mbuf += (LPCTSTR)lpMsgBuf;
		LocalFree( lpMsgBuf );
	}

	DWORD GetLastError(){
		DWORD ErrorID;
		DKUTIL_MULTITHREAD_SYNDROME_LOCK(mUtil);
		ErrorID = ::GetLastError();
		DKUTIL_MULTITHREAD_SYNDROME_UNLOCK(mUtil);
		return ErrorID;
	}
};
#endif


#if 0

///アサート関数
inline bool dkassert(const char *expression,
										 const char *filename,size_t line,const char *message,...)
{

	char s[1024]="";
	if(message){
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),message);
	}else{
		SET_VA_LIST_INSERT_YEN_N(s,sizeof(s),"No message");
	}
	char ss[1024 * 2]="";
	_snprintf(ss,sizeof(ss),
		"DKUTIL_ASSERT(%s):\nfile:%s / \n line:%d / \n message: %s",
		expression,filename,line,s);
	dOutputDebugString(ss);
#ifdef NDEBUG
#	ifdef WIN32
	//::MessageBox(NULL,ss,"ERROR ASSERTION !!",MB_OK);
	{
		LPCTSTR name="ERROR_TEMP.txt";
		{
			//ファイルをオープン
			FILE *fp;
			if(NULL != (fp = fopen( name , "at" ))){
			
				// エラーログファイルに書き出す
				fputs( ss , fp ) ;
				fclose( fp ) ;
			}
		}
		::ShellExecute(NULL, "open",name, NULL, NULL, SW_SHOWNORMAL);
		::DeleteFile(name);
	}
	MB("このテキストをこのソフトの開発者に渡せば、バグ発見の近道なります。\n \
		開発者に知らせるときはこのテキストを転記してあげましょう。");
	if(IDYES==::MessageBox(NULL,
		"このままこのソフトを起動しつづけると、更なるエラーが発生する可能性があります。\n終了しますか？",
		"あってはいけない エラー!!!",
		MB_YESNO))
	{
		exit(edk_FAILED);
		//terminate();
	}else{
	}
#	endif	

#else
	assert(!expression);
#endif
	return true;
	
}


#ifdef NDEBUG
#	ifdef DKUTIL_DEBUG
#	define DKUTIL_NOT_ASSERT_MESSAGE(ex,mes) ((ex) && dkutil::SuperODS("%s,%s,%s,%d",#ex,mes,__FILE__,__LINE__))
#	define DKUTIL_ASSERT_MESSAGE(ex,mes) ((ex) || dkutil::SuperODS("%s,%s,%s,%d",#ex,mes,__FILE__,__LINE__))
#	define DKUTIL_NOT_ASSERT(ex) ( (ex) && dkutil::SuperODS("%s,%s,%d",#ex,__FILE__,__LINE__) )
#	define DKUTIL_ASSERT(ex) ( (ex) || dkutil::SuperODS("%s,%s,%d",#ex,__FILE__,__LINE__) )
#	else
#	define DKUTIL_NOT_ASSERT_MESSAGE(ex,mes) ((void)0)
#	define DKUTIL_ASSERT_MESSAGE(ex,mes) ((void)0)
#	define DKUTIL_NOT_ASSERT(ex) ((void)0)
#	define DKUTIL_ASSERT(ex) ((void)0)
#	endif
#else
#	define DKUTIL_NOT_ASSERT(ex) ( (ex) && ::dkcErrorMessage(#ex,__FILE__,__LINE__,NULL) )
#	define DKUTIL_ASSERT(ex) ( (ex) || ::dkcErrorMessage(#ex,__FILE__,__LINE__,NULL) )
#	define DKUTIL_NOT_ASSERT_MESSAGE(ex,mes) ( (ex) && ::dkcErrorMessage(#ex,__FILE__,__LINE__,mes) )
#	define DKUTIL_ASSERT_MESSAGE(ex,mes) ( (ex) || ::dkcErrorMessage(#ex,__FILE__,__LINE__,mes) )
#endif

#endif //end of if 0


#ifdef NDEBUG
#	ifdef DKUTIL_DEBUG
#	define DKUTIL_NOT_ASSERT(ex) ( (ex) && ::dkcErrorMessage(#ex,__FILE__,__LINE__,NULL) )
#	define DKUTIL_ASSERT(ex) ( (ex) || ::dkcErrorMessage(#ex,__FILE__,__LINE__,NULL) )
#	define DKUTIL_NOT_ASSERT_MESSAGE(ex,mes) ( (ex) && ::dkcErrorMessage(#ex,__FILE__,__LINE__,mes) )
#	define DKUTIL_ASSERT_MESSAGE(ex,mes) ( (ex) || ::dkcErrorMessage(#ex,__FILE__,__LINE__,mes) )
#	else
#	define DKUTIL_NOT_ASSERT_MESSAGE(ex,mes) ((void)0)
#	define DKUTIL_ASSERT_MESSAGE(ex,mes) ((void)0)
#	define DKUTIL_NOT_ASSERT(ex) ((void)0)
#	define DKUTIL_ASSERT(ex) ((void)0)
#	endif
#else
#	define DKUTIL_NOT_ASSERT(ex) ( (ex) && ::dkcErrorMessage(#ex,__FILE__,__LINE__,NULL) )
#	define DKUTIL_ASSERT(ex) ( (ex) || ::dkcErrorMessage(#ex,__FILE__,__LINE__,NULL) )
#	define DKUTIL_NOT_ASSERT_MESSAGE(ex,mes) ( (ex) && ::dkcErrorMessage(#ex,__FILE__,__LINE__,mes) )
#	define DKUTIL_ASSERT_MESSAGE(ex,mes) ( (ex) || ::dkcErrorMessage(#ex,__FILE__,__LINE__,mes) )
#endif




#	define SET_VA_LIST_DMB(a1,a2) SET_VA_LIST(a1,sizeof(a1),a2)	
inline void dMB(const char *str,...){
	char s[1024]="";
	SET_VA_LIST_DMB(s,str);
	::MessageBox(NULL,s,"Message",MB_OK);
}
inline void dMB(BYTE *str,...){
	char s[1024]="";
	char *str__ = (char *)str;
	SET_VA_LIST_DMB(s,str__);
	::MessageBox(NULL,s,"Message",MB_OK);
}


inline void dMB(HWND hWnd,const char *title,UINT uType,char *str,...){
	char s[1024];
	SET_VA_LIST_DMB(s,str);
	::MessageBox(hWnd,s,title,uType);
}
inline void dMBWithTitile(const char *title,char *str,...){
	char s[1024];
	SET_VA_LIST_DMB(s,str);
	::MessageBox(NULL,s,title,MB_OK);
}

inline void dMBWithTitile(const char *title,BYTE *str,...){
	char s[1024];
	char *str__ = (char *)str;
	SET_VA_LIST_DMB(s,str__);
	::MessageBox(NULL,s,title,MB_OK);
}

inline int dMBWithType(UINT uType,char *str,...){
	char s[1024];
	SET_VA_LIST_DMB(s,str);
	return ::MessageBox(NULL,s,"Message",uType);
}

/*inline void dMB(const char *s){
	::MessageBox(NULL,s,"Message",MB_OK);
}*/
#	undef SET_VA_LIST_DMB

	
}//end of dkutil namespace

#endif //end of include once

