//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1999
//
//  File:       debug.h
//
//  Contents:   Debug routines and miscelaneous stuff.
//
//  Classes:    None.
//
//  History:    24-Mar-97   EricB   Created
//
//  Notes on Error Handling: The general rule is that errors will be reported
//  to the user as close to the detection of the error as possible. There are
//  two exceptions to this rule. First, utility routines that don't take an
//  HWND parameter (or a page object pointer) don't report errors because we
//  want a window handle so that the error dialogs will be modal. Second,
//  there are some circumstances where the error needs to be interpreted at a
//  higher level. These exceptions should be noted where they occur.
//
//----------------------------------------------------------------------------

#ifndef __DEBUG_HXX__
#define __DEBUG_HXX__

// macros

#define ByteOffset(base, offset) (((LPBYTE)base)+offset)

//+----------------------------------------------------------------------------
// Macro: LOAD_STRING
// Purpose: attempts to load a string, takes "action" if fails
//-----------------------------------------------------------------------------
#define LOAD_STRING(ids, wcs, len, action) \
    if (!LoadString(g_hInstance, ids, wcs, len - 1)) \
    { \
        DWORD dwErr = GetLastError(); \
        dspDebugOut((DEB_ERROR, "LoadString of " #ids " failed with error %lu\n", dwErr)); \
        action; \
    }

//+----------------------------------------------------------------------------
// Function: LoadStringReport
// Purpose: attempts to load a string, returns FALSE and gives error message
//          if a failure occurs.
//-----------------------------------------------------------------------------
BOOL LoadStringReport(int ids, PTSTR ptsz, int len, HWND hwnd);

//
// debugging support
//

#if DBG == 1

#ifndef APINOT
#ifdef _X86_
 #define APINOT    _stdcall
#else
 #define APINOT    _cdecl
#endif
#endif

#ifdef __cplusplus
extern "C" {
#define EXTRNC "C"
#else
#define EXTRNC
#endif

// smprintf should only be called from xxDebugOut()

   void APINOT
   smprintf(
       unsigned long ulCompMask,
       char const *pszComp,
       char const *ppszfmt,
       va_list  ArgList);

   void          APINOT
   SmAssertEx(
       char const *pszFile,
       int iLine,
       char const *pszMsg);

   int APINOT
   PopUpError(
       char const *pszMsg,
       int iLine,
       char const *pszFile);

   void APINOT
   CheckInit(char * pInfoLevelString, unsigned long * InfoLevel);

#define DSP_DEBUG_BUF_SIZE (512)

#ifdef __cplusplus
}
#endif // __cplusplus

//
// Debug print macros
//

#define DEB_ERROR               0x00000001      // exported error paths
#define DEB_WARN                0x00000002      // exported warnings
#define DEB_TRACE               0x00000004      // exported trace messages

#define DEB_DBGOUT              0x00000010      // Output to debugger
#define DEB_STDOUT              0x00000020      // Output to stdout

#define DEB_IERROR              0x00000100      // internal error paths
#define DEB_IWARN               0x00000200      // internal warnings
#define DEB_ITRACE              0x00000400      // internal trace messages

#define DEB_USER1               0x00010000      // User defined
#define DEB_USER2               0x00020000      // User defined
#define DEB_USER3               0x00040000      // User defined
#define DEB_USER4               0x00080000      // User defined
#define DEB_USER5               0x00100000      // User defined
#define DEB_USER6               0x00200000      // User defined
#define DEB_USER7               0x00400000      // User defined
#define DEB_USER8               0x00800000      // User defined
#define DEB_USER9               0x01000000      // User defined
#define DEB_USER10              0x02000000      // User defined
#define DEB_USER11              0x04000000      // User defined
#define DEB_USER12              0x08000000      // User defined
#define DEB_USER13              0x10000000      // User defined
#define DEB_USER14              0x20000000      // User defined
#define DEB_USER15              0x40000000      // User defined

#define DEB_NOCOMPNAME          0x80000000      // suppress component name

#define DEB_FORCE               0x7fffffff      // force message

#define ASSRT_MESSAGE           0x00000001      // Output a message
#define ASSRT_BREAK             0x00000002      // Int 3 on assertion
#define ASSRT_POPUP             0x00000004      // And popup message

//+----------------------------------------------------------------------
//
// DECLARE_DEBUG(comp)
// DECLARE_INFOLEVEL(comp)
//
// This macro defines xxDebugOut where xx is the component prefix
// to be defined. This declares a static variable 'xxInfoLevel', which
// can be used to control the type of xxDebugOut messages printed to
// the terminal. For example, xxInfoLevel may be set at the debug terminal.
// This will enable the user to turn debugging messages on or off, based
// on the type desired. The predefined types are defined below. Component
// specific values should use the upper 24 bits
//
// To Use:
//
// 1)   In your components main include file, include the line
//              DECLARE_DEBUG(comp)
//      where comp is your component prefix
//
// 2)   In one of your components source files, include the line
//              DECLARE_INFOLEVEL(comp)
//      where comp is your component prefix. This will define the
//      global variable that will control output.
//
// It is suggested that any component define bits be combined with
// existing bits. For example, if you had a specific error path that you
// wanted, you might define DEB_<comp>_ERRORxxx as being
//
// (0x100 | DEB_ERROR)
//
// This way, we can turn on DEB_ERROR and get the error, or just 0x100
// and get only your error.
//
//-----------------------------------------------------------------------

#ifndef DEF_INFOLEVEL
 #define DEF_INFOLEVEL (DEB_ERROR | DEB_WARN)
#endif


#define DECLARE_INFOLEVEL(comp) \
        extern EXTRNC unsigned long comp##InfoLevel = DEF_INFOLEVEL;\
        extern EXTRNC char* comp##InfoLevelString = #comp;


#ifdef __cplusplus

 #define DECLARE_DEBUG(comp) \
    extern EXTRNC unsigned long comp##InfoLevel; \
    extern EXTRNC char *comp##InfoLevelString; \
    _inline void \
    comp##InlineDebugOut(unsigned long fDebugMask, char const *pszfmt, ...) \
    { \
        CheckInit(comp##InfoLevelString, &comp##InfoLevel); \
        if (comp##InfoLevel & fDebugMask) \
        { \
            va_list va; \
            va_start (va, pszfmt); \
            smprintf(fDebugMask, comp##InfoLevelString, pszfmt, va);\
            va_end(va); \
        } \
    }     \
    \
    class comp##CDbgTrace\
    {\
    private:\
        unsigned long _ulFlags;\
        char const * const _pszName;\
    public:\
        comp##CDbgTrace(unsigned long ulFlags, char const * const pszName);\
        ~comp##CDbgTrace();\
    };\
    \
    inline comp##CDbgTrace::comp##CDbgTrace(\
            unsigned long ulFlags,\
            char const * const pszName)\
    : _ulFlags(ulFlags), _pszName(pszName)\
    {\
        comp##InlineDebugOut(_ulFlags, "Entering %s\n", _pszName);\
    }\
    \
    inline comp##CDbgTrace::~comp##CDbgTrace()\
    {\
        comp##InlineDebugOut(_ulFlags, "Exiting %s\n", _pszName);\
    }

#else  // ! __cplusplus

 #define DECLARE_DEBUG(comp) \
    extern EXTRNC unsigned long comp##InfoLevel; \
    extern EXTRNC char *comp##InfoLevelString; \
    _inline void \
    comp##InlineDebugOut(unsigned long fDebugMask, char const *pszfmt, ...) \
    { \
        CheckInit(comp##InfoLevelString, &comp##InfoLevel);
        if (comp##InfoLevel & fDebugMask) \
        { \
            va_list va; \
            va_start (va, pszfmt); \
            smprintf(fDebugMask, comp##InfoLevelString, pszfmt, va);\
            va_end(va); \
        } \
    }

#endif // ! __cplusplus

DECLARE_DEBUG(DsProp)

#define dspDebugOut(x) DsPropInlineDebugOut x
#define dspAssert(x) (void)((x) || (SmAssertEx(__FILE__, __LINE__, #x),0))

#include "dscmn.h"

#define ERR_OUT(msg, hr) \
    if (SUCCEEDED(hr)) { \
        dspDebugOut((DEB_ERROR, #msg "\n")); \
    } else { \
        dspDebugOut((DEB_ERROR, #msg " failed with error 0x%x\n", hr)); \
        ReportError(hr, 0, 0); \
    }

#define REPORT_ERROR(hr, hwnd) \
    dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> %08lx\n", \
                 __FILE__, __LINE__, hr)); \
    ReportError(hr, 0, hwnd);

#define REPORT_ERROR_FORMAT(hr, ids, hwnd) \
    dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> %08lx\n", \
                 __FILE__, __LINE__, hr)); \
    ReportError(hr, ids, hwnd);

#define ERR_MSG(id, hwnd) \
    dspDebugOut((DEB_ERROR, "**** ERROR <%s @line %d> msg string ID: %d\n", \
                 __FILE__, __LINE__, id)); \
    ErrMsg(id, hwnd);

#define CHECK_HRESULT(hr, action) \
    if (FAILED(hr)) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> %08lx\n", \
                     __FILE__, __LINE__, hr)); \
        action; \
    }

#define CHECK_HRESULT_REPORT(hr, hwnd, action) \
    if (FAILED(hr)) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> %08lx\n", \
                     __FILE__, __LINE__, hr)); \
        ReportError(hr, 0, hwnd); \
        action; \
    }

#define CHECK_NULL(ptr, action) \
    if (ptr == NULL) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> NULL ptr\n", \
                     __FILE__, __LINE__)); \
        action; \
    }

#define CHECK_NULL_REPORT(ptr, hwnd, action) \
    if (ptr == NULL) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> NULL ptr\n", \
                     __FILE__, __LINE__)); \
        ReportError(E_OUTOFMEMORY, 0, hwnd); \
        action; \
    }

#define CHECK_WIN32(err, action) \
    if (err != ERROR_SUCCESS) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> %d\n", \
                     __FILE__, __LINE__, err)); \
        action; \
    }

#define CHECK_WIN32_REPORT(err, hwnd, action) \
    if (err != ERROR_SUCCESS) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> %d\n", \
                     __FILE__, __LINE__, err)); \
        ReportError(err, 0, hwnd); \
        action; \
    }

#define CHECK_STATUS(err, action) \
    if (!NT_SUCCESS(err)) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> 0x%08x\n", \
                     __FILE__, __LINE__, err)); \
        action; \
    }

#define CHECK_STATUS_REPORT(err, hwnd, action) \
    if (!NT_SUCCESS(err)) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> 0x%08x\n", \
                     __FILE__, __LINE__, err)); \
        ReportError(RtlNtStatusToDosError(err), 0, hwnd); \
        action; \
    }

#define CHECK_LSA_STATUS(err, action) \
    if (!NT_SUCCESS(err)) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> 0x%08x\n", \
                     __FILE__, __LINE__, err)); \
        action; \
    }

#define CHECK_LSA_STATUS_REPORT(err, hwnd, action) \
    if (!NT_SUCCESS(err)) { \
        dspDebugOut((DEB_ERROR, "**** ERROR RETURN <%s @line %d> -> 0x%08x\n", \
                     __FILE__, __LINE__, err)); \
        ReportError(LsaNtStatusToWinError(err), 0, hwnd); \
        action; \
    }

#define TRACER(ClassName,MethodName) \
    dspDebugOut((DEB_TRACE, #ClassName"::"#MethodName"(0x%p)\n", this)); \
    if (DsPropInfoLevel & DEB_USER5) HeapValidate(GetProcessHeap(), 0, NULL);

#define TRACE(ClassName,MethodName) \
    dspDebugOut((DEB_USER1, #ClassName"::"#MethodName"(0x%p)\n", this)); \
    if (DsPropInfoLevel & DEB_USER5) HeapValidate(GetProcessHeap(), 0, NULL);

#define TRACE2(ClassName,MethodName) \
    dspDebugOut((DEB_USER2, #ClassName"::"#MethodName"(0x%p)\n", this)); \
    if (DsPropInfoLevel & DEB_USER5) HeapValidate(GetProcessHeap(), 0, NULL);

#define TRACE3(ClassName,MethodName) \
    dspDebugOut((DEB_USER3, #ClassName"::"#MethodName"(0x%p)\n", this)); \
    if (DsPropInfoLevel & DEB_USER5) HeapValidate(GetProcessHeap(), 0, NULL);

#define TRACE_FUNCTION(FunctionName) \
    dspDebugOut((DEB_USER1, #FunctionName"\n"));

#define TRACE_FUNCTION3(FunctionName) \
    dspDebugOut((DEB_USER3, #FunctionName"\n"));

#define DBG_OUT(String) \
    dspDebugOut((DEB_ITRACE, String "\n"));

#define DBG_OUT3(String) \
    dspDebugOut((DEB_USER3, String "\n"));

#else // DBG != 1

#define DECLARE_DEBUG(comp)
#define DECLARE_INFOLEVEL(comp)

#define dspDebugOut(x)

#define dspAssert(x) 

#include "dscmn.h"

#define ERR_OUT(msg, hr) \
        ReportError(hr, 0, 0);

#define REPORT_ERROR(hr, hwnd) ReportError(hr, 0, hwnd);

#define REPORT_ERROR_FORMAT(hr, ids, hwnd) ReportError(hr, ids, hwnd);

#define ERR_MSG(id, hwnd) \
    ErrMsg(id, hwnd);

#define CHECK_HRESULT(hr, action) \
    if (FAILED(hr)) { \
        action; \
    }

#define CHECK_HRESULT_H(hr, hwnd, action) \
    if (FAILED(hr)) { \
        action; \
    }

#define CHECK_HRESULT_REPORT(hr, hwnd, action) \
    if (FAILED(hr)) { \
        ReportError(hr, 0, hwnd); \
        action; \
    }

#define CHECK_NULL(ptr, action) \
    if (ptr == NULL) { \
        action; \
    }

#define CHECK_NULL_REPORT(ptr, hwnd, action) \
    if (ptr == NULL) { \
        ReportError(E_OUTOFMEMORY, 0, hwnd); \
        action; \
    }

#define CHECK_WIN32(err, action) \
    if (err != ERROR_SUCCESS) { \
        ReportError(err, 0); \
        action; \
    }

#define CHECK_WIN32_REPORT(err, hwnd, action) \
    if (err != ERROR_SUCCESS) { \
        ReportError(err, 0, hwnd); \
        action; \
    }

#define CHECK_STATUS(err, action) \
    if (!NT_SUCCESS(err)) { \
        action; \
    }

#define CHECK_STATUS_REPORT(err, hwnd, action) \
    if (!NT_SUCCESS(err)) { \
        ReportError(err, 0, hwnd); \
        action; \
    }

#define CHECK_LSA_STATUS(err, action) \
    if (!NT_SUCCESS(err)) { \
        action; \
    }

#define CHECK_LSA_STATUS_REPORT(err, hwnd, action) \
    if (!NT_SUCCESS(err)) { \
        ReportError(LsaNtStatusToWinError(err), 0, hwnd); \
        action; \
    }

#define TRACER(ClassName,MethodName)
#define TRACE(ClassName,MethodName)
#define TRACE2(ClassName,MethodName)
#define TRACE3(ClassName,MethodName)
#define TRACE_FUNCTION(FunctionName)
#define TRACE_FUNCTION3(FunctionName)
#define DBG_OUT(String)
#define DBG_OUT3(String)

#endif // DBG == 1

#endif // __DEBUG_HXX__