/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
/**
 *  @file   vcdbgbuf.h
 *  @brief  Declaration for class %basic_CDebugStreamBuf
 */
#if !defined(__VCDBGBUF_H__)
#define __VCDBGBUF_H__
#include <windows.h>
#include <streambuf>
#include "GLWndMsg.h"
#include <windows.h>
#include <streambuf>
#include "GLWndMsg.h"

namespace VCDBG{
	//! @class basic_CDebugStreamBuf
	//!
	//! A std::basic_streambuf derived class which is used to output 
	//! a string to the debugger screen of Developer Studio with 
	//! std::basic_ostream<>::operator<<().
	//! The implementation is based on the article:
	//! [Standard] ストリームいじり : Implementing Stream Classes 
	//! in http://www.dodgson.org/lab/hat/stream.html.
	//!
	//! @code
	//! namespace XXX{
	//!     VCDBG::CDebugStreamBuf buf;
	//!     std::ostream cerr(&buf);
	//! }
	//! using XXX::cerr;
	//! @endcode
	template <class E, class Tr = char_traits<E> >
	class basic_CDebugStreamBuf : public std::basic_streambuf<E, Tr>{
	public:
		enum{ BUFSIZE = 1024 };

		//! @brief Default constructor
		//!
		//! Creates an object of class basic_CDebugStreamBuf and initializes 
		//! all its pointer member objects to null pointers.
		basic_CDebugStreamBuf(UINT target):m_outputID(target){
			memset(__buffer, 0, BUFSIZE);
			setp(__buffer, __buffer + BUFSIZE - 3);
		}

		//! @brief The destructor.
		//!
		//! Destroys an object of class basic_CDebugStreamBuf.
		virtual ~basic_CDebugStreamBuf(){}

	protected:
		/// The member functions sputc() and sputn() call this function
		/// in case that not enough room can be found in the put buffer to accommodate
		/// the argument character sequence.
		virtual int_type overflow(int_type ch = traits_type::eof()){
			do_output(__buffer);
			if(ch != traits_type::eof()){
				char_type tmp[] = {static_cast<char_type>(ch), '\0'};
				do_output(tmp);
			}
			setp(__buffer, __buffer + BUFSIZE - 3);
			return traits_type::not_eof(ch);
		}

		/// Synchronizes the controlled sequences with the internal buffer.
		/// The return value is always 0.
		virtual int sync(){
			char_type buf[BUFSIZE] = {'\0'};
			traits_type::copy(buf, pbase(), (pptr() - pbase()));
			do_output(buf);
			setp(__buffer, __buffer + BUFSIZE - 3);
			return 0;
		}

	protected:
		virtual void do_output(const char_type* buffer){}

		inline UINT GetTargetId(){ return m_outputID;}
		inline void SetTargetId(UINT target){m_outputID = target;}
	private:
		char_type __buffer[BUFSIZE]; //!< Internal buffer.
		UINT m_outputID;
	};

	/// Analogy of "typedef basic_ostream<char, char_traits<char>> ostream".

	class CDebugBufT : public basic_CDebugStreamBuf<TCHAR, std::char_traits<TCHAR> >{

	public:
		CDebugBufT(UINT target):basic_CDebugStreamBuf<TCHAR, std::char_traits<TCHAR> >(target){}
		virtual ~CDebugBufT(){}

	protected:
		virtual void do_output(const char_type* buffer)
		{
#ifdef _DEBUG
			OutputDebugString(buffer);
#endif
			::AfxGetMainWnd()->SendMessage(
				GetTargetId(), 0L, reinterpret_cast<LPARAM>(buffer));
		}
	};

	class CDebugBufA : public basic_CDebugStreamBuf<char, std::char_traits<char> >{
	public:
		CDebugBufA(UINT target):basic_CDebugStreamBuf<char, std::char_traits<char> >(target){}
		virtual ~CDebugBufA(){}
	protected:
		virtual void do_output(const char_type* buffer)
		{
#ifdef _DEBUG
			OutputDebugStringA(buffer);
#endif

#ifdef UNICODE
			wchar_t buf2[BUFSIZE];
			size_t wLen = 0;
			mbstowcs_s(&wLen, buf2, BUFSIZE, buffer, _TRUNCATE);			

			::AfxGetMainWnd()->SendMessage(
				GetTargetId(), 0L, reinterpret_cast<LPARAM>(buf2));
#else
			::AfxGetMainWnd()->SendMessage(
				GetTargetId(), 0L, reinterpret_cast<LPARAM>(buffer));
#endif
		}
	};


	class CDebugBufW : public basic_CDebugStreamBuf<wchar_t, std::char_traits<wchar_t> >{
	public:
		CDebugBufW(UINT target):basic_CDebugStreamBuf<wchar_t, std::char_traits<wchar_t> >(target){}
		virtual ~CDebugBufW(){}
	protected:
		virtual void do_output(const char_type* buffer)
		{
#ifdef _DEBUG
			OutputDebugStringW(buffer);
#endif

#ifdef UNICODE
			::AfxGetMainWnd()->SendMessage(
				GetTargetId(), 0L, reinterpret_cast<LPARAM>(buffer));
#else
			char buf2[BUFSIZE];
			size_t wLen = 0;
			wcstombs_s(&wLen, buf2, BUFSIZE, buffer, _TRUNCATE);			

			::AfxGetMainWnd()->SendMessage(
				GetTargetId(), 0L, reinterpret_cast<LPARAM>(buf2));
#endif
		}
	};
}	// namespace VCDBG

#endif // __VCDBGBUF_H__
