/////////////////////////////////////////////////////////////////////////
// Copyright  Microsoft Corporation. All rights reserved.
// 
//  This file may contain preliminary information or inaccuracies, 
//  and may not correctly represent any associated Microsoft 
//  Product as commercially released. All Materials are provided entirely 
//  AS IS. To the extent permitted by law, MICROSOFT MAKES NO 
//  WARRANTY OF ANY KIND, DISCLAIMS ALL EXPRESS, IMPLIED AND STATUTORY 
//  WARRANTIES, AND ASSUMES NO LIABILITY TO YOU FOR ANY DAMAGES OF 
//  ANY TYPE IN CONNECTION WITH THESE MATERIALS OR ANY INTELLECTUAL PROPERTY IN THEM. 
// 


#pragma once


/////////////////////////////////////////////////////////////////////////
//  utility macros
//



#define GEN_EVAL(X) X
#define GEN_STRINGIZE_ARG(X) #X
#define GEN_STRINGIZE(X) GEN_EVAL(GEN_STRINGIZE_ARG(X))
#define GEN_MERGE(A, B) A##B
#define GEN_MAKE_W(A) GEN_MERGE(L, A)

#define GEN_WSTRINGIZE(X) GEN_MAKE_W(GEN_STRINGIZE_ARG(X))

#define __WFILE__ GEN_MAKE_W(GEN_EVAL(__FILE__))
#define __WFUNCTION__ GEN_MAKE_W(GEN_EVAL(__FUNCTION__))




// Helper macros to print a GUID using printf-style formatting
#define WSTR_GUID_FMT  L"{%.8x-%.4x-%.4x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}"

#define GUID_PRINTF_ARG( X )                                \
    (X).Data1,                                              \
    (X).Data2,                                              \
    (X).Data3,                                              \
    (X).Data4[0], (X).Data4[1], (X).Data4[2], (X).Data4[3], \
    (X).Data4[4], (X).Data4[5], (X).Data4[6], (X).Data4[7]


// Helper macro for quick treatment of case statements for error codes
#define CHECK_CASE_FOR_CONSTANT(value)                      \
    case value: return wstring(GEN_MAKE_W(#value));


#define BOOL2TXT(b) ((b)? L"TRUE": L"FALSE")




// Max buffer size for logging/tracing 
const int MAX_VPRINTF_BUFFER_SIZE = 4096;


// Generic macro for variable parameter expansion
// Safely truncate the expansion if the buffer is not big enough
#define VPRINTF_VAR_PARAMS(buffer,param)            \
{                                                   \
    buffer.resize(MAX_VPRINTF_BUFFER_SIZE, L'\0');  \
    va_list marker;                                 \
    va_start( marker, param );                      \
    HRESULT hr = StringCchVPrintfW(                 \
        WString2Buffer(buffer),                     \
        buffer.length(),                            \
        param.c_str(),                              \
        marker );                                   \
    if (FAILED(hr)                                  \
        && (hr != STRSAFE_E_INSUFFICIENT_BUFFER))   \
        throw(hr);                                  \
    va_end( marker );                               \
}


// Macro that express the position in the source file (provided for better tracing)
#define DBG_INFO        __WFILE__ , __LINE__, __WFUNCTION__



//
//  Very simple ASSERT definition
//

#ifdef _DEBUG
    #define _ASSERTE(x) {                               \
        if (!(x))                                       \
        {                                           \
            wprintf(L"\nASSERTION FAILED: %S\n", #x);       \
            wprintf(L"- File: %s\n- Line: %d\n- Function: %s\n", DBG_INFO); \
            wprintf(L"\nPress <ENTER> to continue...");     \
            int gc = getchar();                              \
        }                                           \
    }
#else
    #define _ASSERTE(x)
#endif



/////////////////////////////////////////////////////////////////////////////
//  Error-treatment macros
//

//
//  Macro to execute a COM API and to test its result. 
//  - The macro will execute the call given in its parameter
//  - The macro will examine the returned HRESULT
//  - If an failed HRESULT is returned, an explanatory text is written to the console 
//  and an error is thrown. VSHADOW.EXE will then terminate
//
//  Example 
//  - API: HRESULT CoCreateInstance()
//  - Sample Usage: CHECK_COM( CoCreateInstance() )
//
#define CHECK_COM( Call ) CHECK_COM_ERROR( Call, #Call )


#define CHECK_COM_ERROR( ErrorCode, Text )                                                  \
{                                                                                           \
    ft.Trace(DBG_INFO, L"Executing COM call '%s'", GEN_WSTRINGIZE(Text));                   \
    HRESULT hrInternal = ErrorCode;                                                         \
    if (FAILED(hrInternal))                                                                 \
    {                                                                                       \
        ft.WriteLine(L"\nERROR: COM call %s failed.", GEN_WSTRINGIZE(Text));                \
        ft.WriteLine(L"- Returned HRESULT = 0x%08lx", hrInternal);                          \
        ft.WriteLine(L"- Error text: %s", FunctionTracer::HResult2String(hrInternal).c_str());      \
        ft.WriteLine(L"- Please re-run VSHADOW.EXE with the /tracing option to get more details");\
        throw(hrInternal);                                                                  \
    }                                                                                       \
}


//
//  Macro to execute a Win32 API and to test its result. 
//  - The macro will execute the call given in its parameter
//  - The macro will examine the returned BOOL (we assume that the API returns a BOOL. 
//  - If FALSE is returned, an explanatory text is written to the console 
//  and an error is thrown. VSHADOW.EXE will then terminate.
//
#define CHECK_WIN32( Call )                                                                 \
{                                                                                           \
    BOOL bRes = Call;                                                                       \
    if (!bRes)                                                                              \
        CHECK_WIN32_ERROR(GetLastError(), #Call);                                           \
}


#define CHECK_WIN32_ERROR( ErrorCode, Text )                                                \
{                                                                                           \
    ft.Trace(DBG_INFO, L"Executing Win32 call '%s'", GEN_WSTRINGIZE(Text));             \
    DWORD dwLastError = ErrorCode;                                                          \
    HRESULT hrInternal = HRESULT_FROM_WIN32(dwLastError);                                   \
    if (dwLastError != NOERROR)                                                             \
    {                                                                                       \
        ft.WriteLine(L"\nERROR: Win32 call %s failed.", GEN_WSTRINGIZE(Text));          \
        ft.WriteLine(L"- GetLastError() == %ld", dwLastError);                              \
        ft.WriteLine(L"- Error text: %s", FunctionTracer::HResult2String(hrInternal).c_str());      \
        ft.WriteLine(L"- Please re-run VSHADOW.EXE with the /tracing option to get more details");\
        throw(hrInternal);                                                                  \
    }                                                                                       \
}


