//////////////////////////////////////////
// ToolBoxDebug - Debugging assistance  //
//////////////////////////////////////////

#ifndef TOOL_BOX_DEBUG_HEADER
#define TOOL_BOX_DEBUG_HEADER


#ifdef TOOL_BOX_HEADER
#error If ToolBox is being used, it must be #include-ed after ToolBoxDebug so that new and delete will be properly memory-tracked
#endif // TOOL_BOX_HEADER

// Valid tracing levels
/*
TBDOL_TRACE - mainly used for debugging/tracing purposes (usually not enabled for release builds)
TBDOL_FUNC - logs function information; enter, exit, execution time ie. function profiling
TBDOL_INFORMATION - used for logging data values - usually included in rel builds
TBDOL_ERROR - logs errors
*/
enum TBD_OUTPUT_LEVEL { TBDOL_TRACE = 0, TBDOL_INFORMATION, TBDOL_FUNC, TBDOL_ERROR, TBDOL_NONE };
//enum TBD_OUTPUT_LEVEL { TBDOL_TRACE = 0, TBDOL_FUNC, TBDOL_INFORMATION, TBDOL_ERROR, TBDOL_NONE };

// Class-type used to display output regardless of other settings
#define TBDCT_ALWAYS TBDCT_LAST

// Include some enums that need to be present even when TBD_DEBUG is not defined
#include "ToolBoxW4Settings.h"
#include "Windows_W3.h"
#include <TCHAR.h>
#include <stdio.h>
#include "ToolBoxDebugSettings.h"
#ifdef UNDER_CE
	#include "..\SecureCRTFuncs.h"
#endif

#define LARGE_STRING_SIZE	10240

// WinCE's SHIP_BUILD define overrides TBD_DEBUG
#if defined(SHIP_BUILD)
#if defined(TBD_DEBUG)
#undef TBD_DEBUG
#endif // defined(TBD_DEBUG)
#endif // defined(SHIP_BUILD)

#define LogEvent 0
// End of Dummy Watcher variables/functions



// Defining TBD_DEBUG enables ToolBoxDebug support
#if defined(TBD_DEBUG)


// Macro to simplify determining the number of elements in an array (do *not* use this macro for pointers)
#ifndef ARRAY_LENGTH
#define ARRAY_LENGTH(x) (sizeof(x)/sizeof((x)[0]))
#endif // ARRAY_LENGTH

// Defines to simplify global changes
#define TBD_REGISTRY_ROOT TEXT("Software\\ToolBoxDebug")

#define TBD_REGISTRY_SWI_SOFTWARE_ROOT TEXT("Software\\Sierra Wireless Inc")
#define TBD_REGISTRY_ENABLE_LOG_ENCRYPT TEXT("enable encrypt")
#define TBD_LOGFILE_BACKUP TEXT("_BACKUP")


// Enumerations to clarify variable-meanings
enum TBD_OUTPUT_LEVEL_DESTINATION { TBD_OUTPUT_LEVEL_DESTINATION_ALL, TBD_OUTPUT_LEVEL_DESTINATION_FILE, TBD_OUTPUT_LEVEL_DESTINATION_DEBUG };

// Initializes the debugging code
// //param tchFilename	- FILENAME; will be created in the current path of the calling app or
//						- FULLPATH; will be created in specified path eg TEXT("c:\\windows\\system\\psa.txt")
bool TBD_Initialize(TCHAR *tchFilename, TCHAR *tchAppName, const int nMaxLogSize, BOOL bCreateNew);
#define TBD_INITIALIZE TBD_Initialize
// Deinitializes the debugging code
void TBD_Deinitialize();
#define TBD_DEINITIALIZE() TBD_Deinitialize()
// Configure TBD settings from the registry
void TBD_ImportRegistrySettings();
#define TBD_IMPORT_REGISTRY_SETTINGS() TBD_ImportRegistrySettings()
// Sets the output level (trace, informational, etc.)
void TBD_SetLevel(const TBD_OUTPUT_LEVEL tbdoLevelNew, const TBD_OUTPUT_LEVEL_DESTINATION tbdolDestinationNew);
#define TBD_SET_LEVEL(l) TBD_SetLevel(l, TBD_OUTPUT_LEVEL_DESTINATION_ALL)
#define TBD_SET_LEVEL_ALL(l) TBD_SetLevel(l, TBD_OUTPUT_LEVEL_DESTINATION_ALL)
#define TBD_SET_LEVEL_FILE(l) TBD_SetLevel(l, TBD_OUTPUT_LEVEL_DESTINATION_FILE)
#define TBD_SET_LEVEL_DEBUG(l) TBD_SetLevel(l, TBD_OUTPUT_LEVEL_DESTINATION_DEBUG)
// Enables/disables output for the different TBD_CLASS_TYPEs
void TBD_Enable(const TBD_CLASS_TYPE tbtcType, const bool bEnable);
#define TBD_ENABLE_CLASS(t, b) TBD_Enable(t, b)
// Displays the current TBD settings
void TBD_DisplaySettings();
#define TBD_DISPLAY_SETTINGS() TBD_DisplaySettings()
// Breaks into the debugger if a condition is not true
bool TBD_Assert(bool bExpression, const char* const psExpression, const char* const psFileName, const int iLineNumber);
#ifdef _PREFIX_
// To avoid scenarios where PREfix incorrectly reports a problem that's actually prevented
// by TBD_ASSERT (i.e., TBD_ASSERT(0 != pi); int i = *pi;), change the way TBD_ASSERT works
// when compiling under PREfix.  Because PREfix builds will never be run, the recommended
// fix is to call ExitThread(0) if the TBD_ASSERT would otherwise have been triggered.
#define TBD_ASSERT(e) (void)(((bool)(e)) || (ExitThread(0), true))
#else // _PREFIX_
#define TBD_ASSERT(e) (void)(((bool)(e)) || TBD_Assert(e, #e, __FILE__, __LINE__))
#endif // _PREFIX_
// Outputs a message to the user iff the user has elected to see messages for the specified TBD_CLASS_TYPE and output level

//TBD see TBD_Assert(e, #e, __FILE__, __LINE__))
//void TBD_Output(const TBD_CLASS_TYPE tbdcType, const TBD_OUTPUT_LEVEL tbdoLevelOutput, const char* const psFileName, const int iLineNumber, const TCHAR* const psFormatString, ...);

void TBD_Output(const TBD_CLASS_TYPE tbdcType, const TBD_OUTPUT_LEVEL tbdoLevelOutput, const TCHAR* const psFormatString, ...);
#define TBD_OUTPUT TBD_Output

#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)

/*
//TBD - macro Variable Number of Arguments
//#define SWI_TBD_OUTPUT(n, ...) TBD_OUTPUT(TBDCT_ALL_FUNCTIONS, TBDOL_TRACE, TEXT("%s-%i: ") TEXT(#n), __WFILE__, __LINE__, ## __VA_ARGS__);
#define SWI_TBD_OUTPUT(n, p) TBD_OUTPUT(TBDCT_ALL_FUNCTIONS, TBDOL_TRACE, TEXT("%s-%i: ") TEXT(#n), __WFILE__, __LINE__, ##p);
*/

void TBD_OutputLarge(const TBD_CLASS_TYPE tbdcType, const TBD_OUTPUT_LEVEL tbdoLevelOutput, const TCHAR* const psFormatString, ...);
// a replacement for WriteFile that can do simple encryption of output
BOOL TBD_WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);
#define TBD_OUTPUTLARGE TBD_OutputLarge

// Tracks entry to a function - TBD_FUNCTION provides greater functionality
#define TBD_FUNCTION_ENTER(n) TBD_OUTPUT(TBDCT_ALL_FUNCTIONS, TBDOL_TRACE, TEXT(#n) TEXT("+")); DWORD dwTBDFunctionEnterTime = GetTickCount()
// Tracks exit of a function - TBD_FUNCTION provides greater functionality
#define TBD_FUNCTION_EXIT(n) TBD_OUTPUT(TBDCT_ALL_FUNCTIONS, TBDOL_TRACE, TEXT(#n) TEXT("- (%dms elapsed)"), GetTickCount()-dwTBDFunctionEnterTime)
// Tracks entry and exit of a function
class TBD_FUNCTION_Implementation
	{
	public:
	TBD_FUNCTION_Implementation(const TCHAR* const psFileName, LONG& rlTotalFunctionTime, LONG& rlNumberFunctionCalls) : m_rlTotalFunctionTime(rlTotalFunctionTime), m_rlNumberFunctionCalls(rlNumberFunctionCalls)
		{
		_tcsncpy_s(m_szFunctionName, MAX_PATH, psFileName, min(_tcslen(psFileName)+1, ARRAY_LENGTH(m_szFunctionName)-1));
		m_szFunctionName[ARRAY_LENGTH(m_szFunctionName)-1] = TEXT('\0');
		TBD_OUTPUT(TBDCT_ALL_FUNCTIONS, TBDOL_FUNC, TEXT("+ %s"), m_szFunctionName); 
		m_dwTBDFunctionEnterTime = GetTickCount();
		}
	~TBD_FUNCTION_Implementation()
		{
		const DWORD dwFunctionTime = GetTickCount()-m_dwTBDFunctionEnterTime;
		InterlockedIncrement(&m_rlNumberFunctionCalls);
#if defined(UNDER_CE)
		InterlockedExchange(&m_rlTotalFunctionTime, m_rlTotalFunctionTime+dwFunctionTime);
#else // defined(UNDER_CE)
		InterlockedExchangeAdd(&m_rlTotalFunctionTime, dwFunctionTime);
#endif // defined(UNDER_CE)
		TBD_OUTPUT(TBDCT_ALL_FUNCTIONS, TBDOL_FUNC, TEXT("- %s (Duration = %dms - Total time = %dms - Total calls = %d)"),
			m_szFunctionName, dwFunctionTime, m_rlTotalFunctionTime, m_rlNumberFunctionCalls);
		}
	private:
	TCHAR m_szFunctionName[MAX_PATH];
	DWORD m_dwTBDFunctionEnterTime;
	LONG& m_rlTotalFunctionTime;
	LONG& m_rlNumberFunctionCalls;
	};
#define TBD_FUNCTION(n) static LONG lTBD_FUNCTION_Number_Function_Calls = 0; static LONG lTBD_FUNCTION_Total_Function_Time = 0; TBD_FUNCTION_Implementation TBD_Func(TEXT(#n), lTBD_FUNCTION_Total_Function_Time, lTBD_FUNCTION_Number_Function_Calls)

// Makes dealing with HRESULTs easier
#define HRESULTtoTBDOL(hr) (SUCCEEDED(hr) ? TBDOL_TRACE : TBDOL_ERROR)


#else // defined(TBD_DEBUG)


// Avoid warning C4127: conditional expression is constant when TBD_DEBUG is not set
//bool TBD_Initialize();
#define HexString
#define TBD_INITIALIZE()	// TBD_Initialize()
#define TBD_DEINITIALIZE()
#define TBD_IMPORT_REGISTRY_SETTINGS()
#define TBD_SET_LEVEL(l)
#define TBD_ENABLE_CLASS(t, b)
#define TBD_DISPLAY_SETTINGS()
#define TBD_ASSERT(e)
#define TBD_OUTPUT __noop
#define TBD_OUTPUTLARGE __noop
#define TBD_FUNCTION_ENTER(n)
#define TBD_FUNCTION_EXIT(n)
#define TBD_FUNCTION(n)
#define HRESULTtoTBDOL(hr) (TBDOL_NONE)


#endif // defined(TBD_DEBUG)


// Memory-tracking functionality
#if defined(TBD_DEBUG) && defined(TBD_DEBUG_MEMORY)


// Functions to track memory allocation and deallocation
void* TBD_Track(void* pMemory, const size_t stSize, const char* const psFileName, const UINT uiLineNumber);
void* TBD_UnTrack(void *pMemory);
void* TBD_ReAlloc(void* const pMemory, const size_t stSize, const UINT uiFlags, const char* const psFileName, const UINT uiLineNumber);
#define TBD_ALLOC(s) TBD_Track(LocalAlloc(LMEM_FIXED, s), s, __FILE__, __LINE__)
#define TBD_ALLOC_ZERO(s) TBD_Track(LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, s), s, __FILE__, __LINE__)
#define TBD_REALLOC(p, s) TBD_ReAlloc(p, s, LMEM_MOVEABLE, __FILE__, __LINE__)
#define TBD_REALLOC_ZERO(p, s) TBD_ReAlloc(p, s, LMEM_MOVEABLE | LMEM_ZEROINIT, __FILE__, __LINE__)
#define TBD_FREE(p) LocalFree(TBD_UnTrack(p))
#define TBD_TRACK(p, s) TBD_Track(p, s, __FILE__, __LINE__)
#define TBD_UNTRACK(p) TBD_UnTrack(p)
// Displays memory allocations that have not been deallocated
void TBD_ShowAllocedMemory();
#define TBD_SHOW_ALLOCED_MEMORY() TBD_ShowAllocedMemory()

// Overriding operators new and delete
inline void *operator new(size_t stSize) { return TBD_Track(LocalAlloc(0, stSize), stSize, "[UNKNOWN]", 0); }
inline void *operator new(size_t stSize, const char* const psFileName, const UINT uiLineNumber) { return TBD_Track(LocalAlloc(0, stSize), stSize, psFileName, uiLineNumber); }
inline void operator delete(void *pMemory) { LocalFree(TBD_UnTrack(pMemory)); }
inline void operator delete(void *pMemory, const char* const /*psFileName*/, const UINT /*uiLineNumber*/) { LocalFree(TBD_UnTrack(pMemory)); }
#define new new(__FILE__, __LINE__)


#else // defined(TBD_DEBUG) && defined(TBD_DEBUG_MEMORY)


#define TBD_ALLOC(s) LocalAlloc(LMEM_FIXED, s)
#define TBD_ALLOC_ZERO(s) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, s)
#define TBD_REALLOC(p, s) LocalReAlloc(p, s, LMEM_MOVEABLE)
#define TBD_REALLOC_ZERO(p, s) LocalReAlloc(p, s, LMEM_MOVEABLE | LMEM_ZEROINIT)
#define TBD_FREE(p) LocalFree(p)
#define TBD_TRACK(p, s)
#define TBD_UNTRACK(p)
#define TBD_SHOW_ALLOCED_MEMORY()


#endif // defined(TBD_DEBUG) && defined(TBD_DEBUG_MEMORY)

#if defined(TBD_DEBUG) && defined(TBD_DEBUG_TIMER)

// Execution timing.
//
// DESCRIPTION:
// These macros provide accurate timing of CPU usage within the calling thread. They rely on the Win32 API
// GetThreadTimes(). (GetTickCount() is not an accurate measure of execution time. It takes no account
// of CPU usage by other threads and processes during the timing interval.)
//
// USAGE:
//     TBD_TIMER_START(tag);
//     ...do some work...
//     TBD_TIMER_END(tag);
//
// Ensure TBD_DEBUG and TBD_DEBUG_TIMER are defined for the project.
//
// tag is an identifier of that timer and must be unique within the scope of the function in which the 
// timing macros are being used. The macros append the tag to the end of fixed identifiers
// to create temporary unique local variables. Bear this in mind when choosing tags. You may choose numbers 
// or any combination of letters which in their own right would form a valid C variable name. Thus tag
// may be as descriptive as desired.
//
// EXAMPLES:
//
// TBD_TIMER_START(1);
// ...do some work...
// TBD_TIMER_END(1);
//
//  Output would be:
//   "Timer 1: elapsed (ms): kernel 25, user 721"
//
// Timers may be nested:
//
// TBD_TIMER_START(outer);
// ...do some work...
// TBD_TIMER_START(inner);
// ...do some more work...
// TBD_TIMER_END(inner);
// ...do some more...
// TBD_TIMER_END(outer);
//
//  Output would be:
//   "Timer inner: elapsed (ms): kernel 10, user 301"
//   "Timer outer: elapsed (ms): kernel 12, user 931"
//
// Timers may be overlapped:
//
// TBD_TIMER_START(first);
// ...do some work...
// TBD_TIMER_START(second);
// ...do some more work...
// TBD_TIMER_END(first);
// ...do some more...
// TBD_TIMER_END(second);
//
//  Output would be:
//   "Timer first: elapsed (ms): kernel 10, user 301"
//   "Timer second: elapsed (ms): kernel 12, user 931"
//
// LIMITATIONS:
// On some WINCE platforms GetThreadTimes() returns 0 for kernel time. In that case, user time represents
// the total of kernel and user time.
//
// On desktop, for results to be accurate and meaningful, ensure hyperthreading is turned off for the 
// calling process. Use the Win32 APIs GetProcessAffinityMask() and SetProcessAffinityMask() to do this.
// I have not bothered to add that code here as we are primarily concerned with timing on WINCE only. 
//

#define TBD_TIMER_START(tag)\
TCHAR* tagTbd ## tag = _T(# tag);\
FILETIME creationTimeTbd ## tag;\
FILETIME exitTimeTbd ## tag;\
FILETIME startKernelTimeTbd ## tag;\
FILETIME startUserTimeTbd ## tag;\
FILETIME endKernelTimeTbd ## tag;\
FILETIME endUserTimeTbd ## tag;\
LARGE_INTEGER elapsedKernelTimeTbd ## tag;\
LARGE_INTEGER elapsedUserTimeTbd ## tag;\
LARGE_INTEGER startTbd ## tag;\
LARGE_INTEGER endTbd ## tag;\
GetThreadTimes(GetCurrentThread(), &creationTimeTbd ## tag, &exitTimeTbd ## tag, &startKernelTimeTbd ## tag, &startUserTimeTbd ## tag); 

#define TBD_TIMER_END(tag)\
GetThreadTimes(GetCurrentThread(), &creationTimeTbd ## tag, &exitTimeTbd ## tag, &endKernelTimeTbd ## tag, &endUserTimeTbd ## tag);\
startTbd ## tag.HighPart = startKernelTimeTbd ## tag.dwHighDateTime;\
startTbd ## tag.LowPart = startKernelTimeTbd ## tag.dwLowDateTime;\
endTbd ## tag.HighPart = endKernelTimeTbd ## tag.dwHighDateTime;\
endTbd ## tag.LowPart = endKernelTimeTbd ## tag.dwLowDateTime;\
elapsedKernelTimeTbd ## tag.QuadPart = endTbd ## tag.QuadPart - startTbd ## tag.QuadPart;\
elapsedKernelTimeTbd ## tag.QuadPart /= 10000;\
startTbd ## tag.HighPart = startUserTimeTbd ## tag.dwHighDateTime;\
startTbd ## tag.LowPart = startUserTimeTbd ## tag.dwLowDateTime;\
endTbd ## tag.HighPart = endUserTimeTbd ## tag.dwHighDateTime;\
endTbd ## tag.LowPart = endUserTimeTbd ## tag.dwLowDateTime;\
elapsedUserTimeTbd ## tag.QuadPart = endTbd ## tag.QuadPart - startTbd ## tag.QuadPart;\
elapsedUserTimeTbd ## tag.QuadPart /= 10000;\
TBD_OUTPUT(TBDCT_ALWAYS, TBDOL_TRACE, _T("Timer %s: elapsed (ms): kernel %I64d, user %I64d"), tagTbd ## tag, elapsedKernelTimeTbd ## tag.QuadPart, elapsedUserTimeTbd ## tag.QuadPart);  

#else //defined(TBD_DEBUG) && defined(TBD_DEBUG_TIMER)

#define TBD_TIMER_START(tag)
#define TBD_TIMER_END(tag)

#endif //defined(TBD_DEBUG) && defined(TBD_DEBUG_TIMER)

//#endif
#endif // TOOL_BOX_DEBUG_HEADER
