/////////////////////////////////////////////////////////////////////
// This open-source software sample is offered free of charge and without guarantee.
// Polhemus accepts no responsibility or liability for any use of this code.
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// Polhemus Inc., www.polhemus.com
// © 2002 Polhemus Inc., All Rights Reserved
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//
//  Filename:           $Workfile: Debug.h $
//
//  Project Name:       Polhemus Tracker Command Interface API 
//
//  Original Author:    S. Gagnon
//
//  Description:        DOUT defs - replacement for OutputDebugString calls
//
//  VSS $Header: /Viper/VPlibusb.root/VPlibusb/VPlibusb/Debug.h 9     3/11/20 6:40p Suzanne $  
//
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
///@cond
#ifndef _DEBUG_H_
#define _DEBUG_H_

/////////////////////////////////////////////////////////////////////
//#ifndef VPCMDIF_STATIC
//#if defined (PDEBUG_EXPORT)
//#define PDEBUG_API __declspec(dllexport)
//#else
//#define PDEBUG_API __declspec(dllimport)
//#endif
//#else
#define PDEBUG_API
//#endif
/////////////////////////////////////////////////////////////////////
#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
#include <windows.h>
/*#if defined(interface)
#undef interface
#endif
*/
#if !defined(__CYGWIN__)
#include <winsock.h>
#endif
#endif
/////////////////////////////////////////////////////////////////////

//#include "PTTypeDefs.h"
//#include "LogFile.h"

#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdlib.h>
#include <cerrno>
#include <thread>
#include <cstring>

#ifndef _WINDOWS_
typedef const char * LPCSTR;
typedef const wchar_t * LPCWSTR;
typedef char * LPSTR;
typedef wchar_t * LPWSTR;
typedef char CHAR;
typedef wchar_t WCHAR;
#define MAX_PATH 260
#define sprintf_s snprintf
#define GetCurrentThreadId std::this_thread::get_id()
#define _countof(a) (sizeof(a)/sizeof(*(a)))
//template < typename T, size_t N >
//size_t _countof(T(&arr)[N]) { return std::extent< T[N] >::value; }
#define strlen std::strlen
#define errno_t errno
#endif


/////////////////////////////////////////////////////////////////////
//using namespace iodbg;
/////////////////////////////////////////////////////////////////////
#define PI_DEBUG_BUFFER_SIZE 0x0800
#define DOUT  PiDebug::PIDOUT
#define DOUT0 PiDebug::PIDOUT0
#define DOUT1 PiDebug::PIDOUT1
#define DOUT3 PiDebug::PIDOUT3
#define DOUT5 PiDebug::PIDOUT5
#define DOUT7 PiDebug::PIDOUT7
#define DOUT0_ALT PiDebug::PIDOUT0_ALT
#define DOUT3_ALT PiDebug::PIDOUT3_ALT
#define DOUT5_ALT PiDebug::PIDOUT5_ALT

#define PI_DEBUGOUT( lev, str ) PiDebug::PIDebugOut( lev, str )
#define	DOUTHEXBUF	PiDebug::PIDOUTHEXBUF
#define	DOUTHEXBUF0	PiDebug::PIDOUTHEXBUF0
#define	DOUTHEXBUF_ALT	PiDebug::PIDOUTHEXBUF_ALT
#define	DOUTASCBUF	PiDebug::PIDOUTASCBUF

#define DOUT_ERR PiDebug::PIDOUT_ERR0
#define DOUT_ERR1 PiDebug::PIDOUT_ERR1
#define DOUT_ERR3 PiDebug::PIDOUT_ERR3
#define DOUT_ERR5 PiDebug::PIDOUT_ERR5
#define DOUT_ERR7 PiDebug::PIDOUT_ERR7

#define PID_COMPACT PiDebug::Compact()

#define PID_COMPACT_LEVEL 2

//#define LOGOUT		PiDebug::LogOut//PiDebug::m_LogFile.Write
//#define LOGOUT_NFL	PiDebug::PIDOUT_ERR0//PiDebug::m_LogFile.WriteNoFlush
//#define LOGHEXBUF	PiDebug::PILOGHEXBUF

#ifndef CODETRACE
#define CODETRACE 	DOUT3("%s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__)
#define CODETRACEEX DOUT3( "\t >>>>leaving %s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__ )
#define CODETRACE5 	DOUT5("%s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__)
#define CODETRACEEX5 DOUT5( "\t >>>>leaving %s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__ )
#define CODETRACE7 	DOUT7("%s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__)
#define CODETRACEEX7 DOUT7( "\t >>>>leaving %s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__ )
#define CODETRACE1  DOUT1("%s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__)
#define CODETRACEEX1 DOUT1( "\t >>>>leaving %s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__ )

#endif

#define CODETRACE_ALT DOUT3_ALT("%s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__)
#define CODETRACEEX_ALT DOUT3_ALT( "\t >>>>leaving %s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__ )

#define CODETRACE_ALT5 DOUT5_ALT("%s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__)
#define CODETRACEEX_ALT5 DOUT5_ALT( "\t >>>>leaving %s : %s : %d\r\n", __FILE__, __FUNCTION__, __LINE__ )


#define DOUT_MIN DOUT0
#define DOUT_NORM DOUT1
#define DOUT_CODE DOUT3
#define DOUT_VERB DOUT5
// Trace Levels used in Code
// DOUT         = trace that cannot be filtered out by trace level 
// DOUT0        = minimal trace + error  : user has set trace level to 1
// DOUT1        = minimal trace + error + normal I/O : user has set trace level to 2 or 3
// DOUT3        = minimal trace + error + normal I/O + code trace : user has set level to 5
// DOUT5        = minimal trace + error + normal I/O + code trace + other : user has set level to 7 (verbose)
// DOUT7        = verbose plus.  User has set level to 8 or higher.  Undocumented.

#ifndef CLAMP
#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
#endif

//Debug level selections
//
// USER		PIDEBUG CODE
//   0			Trace is off / DOUT prints anyway
//   1			0 minimal trace + errors, code uses DOUT0
//   2			1 COMPACT normal I/O trace + errors code uses  DOUT1
//   3			2 Normal I/O trace + errors  code uses  DOUT1
//   4,5		3 Code Trace + I/O + errors  code uses  DOUT3
//   6,7		5 Verbose code uses  DOUT5
namespace iodbg
{
	/////////////////////////////////////////////////////////////////////
	// CLASS PiDebug
	/////////////////////////////////////////////////////////////////////
	class PDEBUG_API PiDebug
	{
	public:
		PiDebug();
		// user debug level selections
		enum
		{
			PID_NODEBUG
			, PID_1_MIN
			, PID_2_COMPACT
			, PID_3_NORMALIO
			, PID_5_CODETRACE = 5
			, PID_7_VERBOSE = 7
		};
		static void PIDSetLevel(int32_t level)
		{
			m_usDebugLevel = CLAMP(level, PID_NODEBUG, PID_7_VERBOSE);
			m_bDebugCompact = (level == PID_COMPACT_LEVEL);
		}

		static void PIDSetCompact(bool b)
		{
			m_bDebugCompact = b;
		}
		//static void PIDLogASCII	( bool b ) { g_bLogAscii = b; }
		//static void PIDFlushIO	( bool b ) { g_bFlushIO = b; }
		//static bool PIDFlushIO	() { return g_bFlushIO; }

		static void PIDOUT(LPCSTR szFormat, ...);
		static void PIDOUTL(LPCSTR szFormat, va_list);
		static void PIDOUTLv(LPCSTR szFormat, va_list);
		static void PIDOUTL_ALT(LPCSTR szFormat, va_list);

		static void PIDebugOut(int32_t level, LPCSTR szFormat, ...);
		static void PIDebugOutL(int32_t level, LPCSTR szFormat, va_list);
		static void PIDebugOutL_ALT(int32_t level, LPCSTR szFormat, va_list);
		static void PIDOUT0(LPCSTR szFormat, ...);
		static void PIDOUT0_ALT(LPCSTR szFormat, ...);
		static void PIDOUT1(LPCSTR szFormat, ...);
		static void PIDOUT3(LPCSTR szFormat, ...);
		static void PIDOUT3_ALT(LPCSTR szFormat, ...);
		static void PIDOUT5(LPCSTR szFormat, ...);
		static void PIDOUT5_ALT(LPCSTR szFormat, ...);
		static void PIDOUT7(LPCSTR szFormat, ...);

		static void PIDOUT_ERR(int32_t nLev, LPCSTR szFormat, ...);
		static void PIDOUT_ERR0(LPCSTR szFormat, ...);
		static void PIDOUT_ERR1(LPCSTR szFormat, ...);
		static void PIDOUT_ERR3(LPCSTR szFormat, ...);
		static void PIDOUT_ERR5(LPCSTR szFormat, ...);
		static void PIDOUT_ERR7(LPCSTR szFormat, ...);

		static bool Compact() { return m_bDebugCompact; }

		static void pidoutTraceFBuf(void * pBuf, int32_t iCount, bool bCR = true, const char *szPre = 0);
		static void PIDOUTHEXBUF(uint8_t * pBuf, int32_t iCount, bool bCR, const char *szPre, uint16_t uLev = PID_3_NORMALIO);
		static void PIDOUTHEXBUF0(uint8_t * pBuf, int32_t iCount, bool bCR, const char *szPre);
		static void PIDOUTHEXBUF_ALT(uint8_t * pBuf, int32_t iCount, bool bCR, const char *szPre);
		/*static void PIDOUTHEXBUF1( LPCTSTR szTitle, PBYTE pBuf, DWORD dwCount );
		static void PIDOUTHEXBUF( LPCTSTR szTitle, PBYTE pBuf, DWORD dwCount );
		static void PIDOUTASCBUF( LPCTSTR szTitle, PBYTE pBuf, DWORD dwCount );
		static void PILOGHEXBUF( LPCTSTR szTitle, PBYTE pBuf, DWORD dwCount );


		static void PILOGERR( LPCSTR szFormat, ... );*/

		//static void FlushLog()//{};
		//							{ m_LogFile.Flush(); }

		//static void StartLog(int nFlushCount = 0, bool bAppend = false, bool bTS = true)
		//{
		//	m_LogFile.Close();
		//	m_LogFile.Open(nFlushCount, bAppend, bTS);
		//	m_bLog = true;
		//}

		//static void StopLog			(  )
		//{
		//	m_LogFile.Close();
		//	m_bLog = false;
		//}
		//static LPCTSTR LogFile		(  )
		//{
		//	return m_LogFile.Filename();
		//	//return _T("");
		//}
		//static void SetLogFile( TCHAR *fp )
		//{
		//	m_LogFile.SetFilename(fp);
		//}

		static LPCSTR	SZDebug() { return m_szDebug; }
		static LPSTR WSToMB(LPCWSTR szStr);
		static LPWSTR MBToWS(LPCSTR szStr);


	protected:
		static bool m_bUseAlt;
		static bool     m_bLog;
		//static CLogFile m_LogFile;
		static char  	m_szDebug[PI_DEBUG_BUFFER_SIZE];
		static char  	m_szDebugAlt[PI_DEBUG_BUFFER_SIZE];
		static uint16_t	m_usDebugLevel;
		static bool     m_bDebugCompact;
		//static bool		g_bLogAscii;
		//static bool		g_bFlushIO;

	};

	class PDEBUG_API CPiDebugOut
	{
	public:
		CPiDebugOut(int32_t iLev = 5)
		{
			PiDebug::PIDSetLevel(iLev);
		}
		//	void CreateWin(bool bMove=false)
		//	{
		//#ifdef _WINDOWS_
		//		HWND hFore = ::GetForegroundWindow();
		//		AllocConsole();
		//		FILE *fp=0;
		//		freopen_s(&fp, "CONOUT$", "w", stdout);
		//		HWND hcon = ::GetConsoleWindow();
		//		if (hcon != NULL)
		//		{
		//			HMENU hMenu = ::GetSystemMenu(hcon, FALSE);
		//			if (hMenu != NULL)
		//				DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
		//
		//			HWND hChild = ::GetWindow(hcon, GW_CHILD);
		//			HWND hFirst = ::GetWindow(hcon, GW_HWNDFIRST);
		//			HWND hLast = ::GetWindow(hcon, GW_HWNDLAST);
		//			HWND hNext = ::GetWindow(hcon, GW_HWNDNEXT);
		//			HWND hPrev = ::GetWindow(hcon, GW_HWNDPREV);
		//			HWND HOwn = ::GetWindow(hcon, GW_OWNER);
		//			HWND hForeII = ::GetForegroundWindow();
		//
		//			int i = 0;
		//			if (hFore != 0)
		//			{
		//				RECT rf;
		//				POINT pc;
		//				bool bCanMove = false;
		//				if (GetWindowRect(hFore, &rf))
		//				{
		//					pc.x = rf.left-30;     pc.y = rf.top-30;
		//					bCanMove = true;
		//				}
		//				//::SetForegroundWindow(hFore);
		//
		//				if (bMove && bCanMove)
		//				{
		//					::SetWindowPos(hcon, hFore, pc.x, pc.y, 0, 0, SWP_NOSIZE);
		//				}
		//				else
		//				{
		//					::SetWindowPos(hcon, hFore, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
		//				}
		//			}
		//		}
		//#endif
		//	}
		~CPiDebugOut()
		{
			//#ifdef _WINDOWS_
			//		//FreeConsole();
			//#endif
		}
	};

#ifdef _WINDOWS_
	/////////////////////////////////////////////////////////////////////
	// CLASS CPiCriticalSection
	/////////////////////////////////////////////////////////////////////
	class CPiCriticalSection
	{
	public:
		CPiCriticalSection(void)
		{
			::InitializeCriticalSection(&m_cs);
		}

		void	Enter(void)
		{
			::EnterCriticalSection(&m_cs);
		}

		void	Leave(void)
		{
			::LeaveCriticalSection(&m_cs);
		}

		~CPiCriticalSection(void)
		{
			::LeaveCriticalSection(&m_cs);
			::DeleteCriticalSection(&m_cs);
		}

	protected:
		CRITICAL_SECTION m_cs;
	};


#else
	class CPiCriticalSection
	{
	public:
		CPiCriticalSection(void)
		{
		}

		void	Enter(void)
		{
		}

		void	Leave(void)
		{
		}

		~CPiCriticalSection(void)
		{
		}

	protected:
		//CRITICAL_SECTION m_cs;
	};
#endif  //_WINDOWS_

}; //namespace iodbg
/////////////////////////////////////////////////////////////////////
// END $Workfile: Debug.h $
/////////////////////////////////////////////////////////////////////
#endif // _PDEBUG_H_
///@endcond