/*++

Copyright (C) Microsoft Corporation, 1997 - 1999

Module Name:

    common.cxx

Abstract:

    This file contains some common stuff for SENS project.

Author:

    Gopal Parupudi    <GopalP>

[Notes:]

    optional-notes

Revision History:

    GopalP          10/11/1997         Start.

--*/


#include "common.hxx"
#include <windows.h>


#ifdef DBG
extern DWORD gdwDebugOutputLevel;
#endif // DBG

//
// Constants
//

#define MAX_DBGPRINT_LEN        512



#if defined(SENS_CHICAGO)

//
// Macros
//
#define UnicodeStrLen(str)      lstrlenW(str)




char *
SensUnicodeStringToAnsi(
    unsigned short * StringW
    )
/*++

Routine Description:

    Converts UNICODE string to it's ANSI equivalent.

Arguments:

    StringW - The UNICODE string.

Notes:

    a. The returned ANSI string is to be freed using the "delete" operator.

Return Value:

    The equivalent ANSI string, if successful.

    NULL, otherwise

--*/
{
    int retVal;
    short Length;
    char * StringA;

    // Simple check
    if (NULL == StringW)
        {
        return (NULL);
        }

    // Allocate Multi-byte buffer
    Length = UnicodeStrLen(StringW);
    StringA = new char [Length * 2 + 2];
    if (NULL == StringA)
        {
        return (NULL);
        }

    // Perform the conversion
    retVal = WideCharToMultiByte(
                 CP_ACP,                // Code Page
                 0,                     // Performance and mapping flags
                 (LPCWSTR) StringW,     // Wide String
                 -1,                    // Number of characters in Wide String
                 (LPSTR) StringA,       // Multi-byte string buffer
                 Length * 2 + 2,        // Size of the Multi-byte string buffer
                 NULL,                  // Default for unmappable characters
                 NULL                   // Flag when default character is used
                 );
    if (0 == retVal)
        {
        SensPrintToDebugger(SENS_DBG, ("[SENS] WideCharToMultiByte() failed with %d\n",
                            GetLastError()));
        delete StringA;
        return (NULL);
        }

    return (StringA);
}



unsigned short *
SensAnsiStringToUnicode(
    char * StringA
    )
/*++

Routine Description:

    Converts ANSI string to it's UNICODE equivalent.

Arguments:

    StringA - The ANSI string.

Notes:

    a. The returned UNICODE string is to be freed using the "delete" operator.

Return Value:

    The equivalent UNICODE string, if successful.

    NULL, otherwise

--*/
{
    int retVal;
    short Length;
    unsigned short * StringW;

    // Simple check
    if (NULL == StringA)
        {
        return (NULL);
        }

    // Allocate UNICODE buffer
    Length = strlen((const char *) StringA);
    StringW = new unsigned short [Length + 1];
    if (NULL == StringW)
        {
        return (NULL);
        }

    retVal = MultiByteToWideChar(
                 CP_ACP,            // Code page
                 0,                 // Character type options
                 (LPCSTR) StringA,  // Multi-byte string
                 -1,                // Number of characters in multi-byte string
                 (LPWSTR) StringW,  // UNICODE string buffer
                 Length * 2 + 2     // Length of the UNICODE string buffer
                 );
    if (0 == retVal)
        {
        SensPrintToDebugger(SENS_DBG, ("[SENS] MultiByteToWideChar() failed with %d\n",
                            GetLastError()));
        delete StringW;
        return (NULL);
        }

    return (StringW);
}



#endif // SENS_CHICAGO


//
// Available only on Debug builds.
//

#if DBG



ULONG _cdecl
SensDbgPrintA(
    CHAR * Format,
    ...
    )
/*++

Routine Description:

    Equivalent of NT's DbgPrint().

Arguments:

    Same as for printf()

Return Value:

    0, if successful.

--*/
{
    va_list arglist;
    CHAR OutputBuffer[MAX_DBGPRINT_LEN];
    ULONG length;
    static BeginningOfLine = 1;
    static LineCount = 0;

    // See if we are supposed to print
    if (0x0 == gdwDebugOutputLevel)
        {
        return -1;
        }

    //
    // vsprintf isn't multithreaded + we don't want to intermingle output
    // from different threads.
    //

    length = 0;

    //
    // Handle the beginning of a new line.
    //
    //

    if (BeginningOfLine)
        {
        //
        // If we're writing to the debug terminal,
        // indicate this is a SENS message.
        //
        length += (ULONG) wsprintfA(&OutputBuffer[length], "[SENS] ");
        }

    //
    // Put the information requested by the caller onto the line
    //

    va_start(arglist, Format);

    length += (ULONG) wvsprintfA(&OutputBuffer[length], Format, arglist);
    BeginningOfLine = (length > 0 && OutputBuffer[length-1] == '\n' );
    if (OutputBuffer[length-1] == '\n')
        {
        OutputBuffer[length-1] = '\r';
        OutputBuffer[length]   = '\n';
        OutputBuffer[length+1] = '\0';
        }

    va_end(arglist);

    if (length > MAX_DBGPRINT_LEN)
        {
        SensBreakPoint();
        }

    //
    //  Just output to the debug terminal
    //

#ifdef PRINT_TO_CONSOLE

    printf("%s\n", OutputBuffer);

#else // PRINT_TO_CONSOLE

#if !defined(SENS_CHICAGO)
    DbgPrint(OutputBuffer);
#else
    OutputDebugStringA(OutputBuffer);
#endif // SENS_CHICAGO

#endif // PRINT_TO_CONSOLE

    return (0);
}




ULONG _cdecl
SensDbgPrintW(
    WCHAR * Format,
    ...
    )
/*++

Routine Description:

    Equivalent of NT's DbgPrint().

Arguments:

    Same as for printf()

Return Value:

    0, if successful.

--*/
{
    va_list arglist;
    WCHAR OutputBuffer[MAX_DBGPRINT_LEN];
    ULONG length;
    static BeginningOfLine = 1;
    static LineCount = 0;

    // See if we are supposed to print
    if (0x0 == gdwDebugOutputLevel)
        {
        return -1;
        }

    //
    // vsprintf isn't multithreaded + we don't want to intermingle output
    // from different threads.
    //

    length = 0;

    //
    // Handle the beginning of a new line.
    //
    //

    if (BeginningOfLine)
        {
        //
        // If we're writing to the debug terminal,
        // indicate this is a SENS message.
        //
        length += (ULONG) wsprintfW(&OutputBuffer[length], SENS_BSTR("[SENS] "));
        }

    //
    // Put the information requested by the caller onto the line
    //

    va_start(arglist, Format);

    length += (ULONG) wvsprintfW(&OutputBuffer[length], Format, arglist);
    BeginningOfLine = (length > 0 && OutputBuffer[length-1] == (WCHAR)'\n' );
    if (OutputBuffer[length-1] == (WCHAR)'\n')
        {
        OutputBuffer[length-1] = (WCHAR)'\r';
        OutputBuffer[length]   = (WCHAR)'\n';
        OutputBuffer[length+1] = (WCHAR)'\0';
        }

    va_end(arglist);

    if (length > MAX_DBGPRINT_LEN)
        {
        SensBreakPoint();
        }

    //
    //  Just output to the debug terminal
    //

#ifdef PRINT_TO_CONSOLE
    wprintf(SENS_BSTR("%s\n"), OutputBuffer);
#else
    OutputDebugStringW(OutputBuffer);
#endif

    return (0);
}





BOOL
ValidateError(
    IN int Status,
    IN unsigned int Count,
    IN const int ErrorList[])
/*++
Routine Description

    Tests that 'Status' is one of an expected set of error codes.
    Used on debug builds as part of the VALIDATE() macro.

Example:

        VALIDATE(EventStatus)
            {
            RPC_P_CONNECTION_CLOSED,
            RPC_P_RECEIVE_FAILED,
            RPC_P_CONNECTION_SHUTDOWN
            // more error codes here
            } END_VALIDATE;

     This function is called with the RpcStatus and expected errors codes
     as parameters.  If RpcStatus is not one of the expected error
     codes and it not zero a message will be printed to the debugger
     and the function will return false.  The VALIDATE macro ASSERT's the
     return value.

Arguments:

    Status - Status code in question.
    Count - number of variable length arguments

    ... - One or more expected status codes.  Terminated with 0 (RPC_S_OK).

Return Value:

    TRUE - Status code is in the list or the status is 0.

    FALSE - Status code is not in the list.

--*/
{
    unsigned int i;

    for (i = 0; i < Count; i++)
        {
        if (ErrorList[i] == Status)
            {
            return TRUE;
            }
        }

    SensPrintToDebugger(SENS_DEB, ("[SENS] Assertion: unexpected failure %lu (0x%08x)\n",
                        (unsigned long)Status, (unsigned long)Status));

    return(FALSE);
}



void
EnableDebugOutputIfNecessary(
    void
    )
/*++

    This routine tries to set gdwDebugOuputLevel to the value defined
    in the regitry at HKLM\Software\Microsoft\Mobile\SENS\Debug value.
    All binaries using this function need to define the following
    global value:

        DWORD gdwDebugOutputLevel;

--*/
{
    HRESULT hr;
    HKEY hKeySens;
    LONG RegStatus;
    BOOL bStatus;
    DWORD dwType;
    DWORD cbData;
    LPBYTE lpbData;

    hr = S_OK;
    hKeySens = NULL;
    RegStatus = ERROR_SUCCESS;
    bStatus = FALSE;
    dwType = 0x0;
    cbData = 0x0;
    lpbData = NULL;

    RegStatus = RegOpenKeyEx(
                    HKEY_LOCAL_MACHINE, // Handle of the key
                    SENS_REGISTRY_KEY,  // String which represents the sub-key to open
                    0,                  // Reserved (MBZ)
                    KEY_QUERY_VALUE,    // Security Access mask
                    &hKeySens           // Returned HKEY
                    );
    if (RegStatus != ERROR_SUCCESS)
        {
        SensPrintToDebugger(SENS_ERR, ("[SENS] RegOpenKeyEx(Sens) returned %d.\n", RegStatus));
        goto Cleanup;
        }

    //
    // Query the Configured value
    //
    lpbData = (LPBYTE) &gdwDebugOutputLevel;
    cbData = sizeof(DWORD);

    RegStatus = RegQueryValueEx(
                    hKeySens,           // Handle of the sub-key
                    SENS_DEBUG_LEVEL,   // Name of the Value
                    NULL,               // Reserved (MBZ)
                    &dwType,            // Address of the type of the Value
                    lpbData,            // Address of the data of the Value
                    &cbData             // Address of size of data of the Value
                    );
    if (RegStatus != ERROR_SUCCESS)
        {
        SensPrintToDebugger(SENS_ERR, ("[SENS] RegQueryValueEx(SENS_DEBUG_LEVEL) failed with 0x%x\n", RegStatus));
        gdwDebugOutputLevel = 0x0;
        goto Cleanup;
        }

    SensPrintToDebugger(SENS_INFO, ("[SENS] Debug Output is turned %s. The level is 0x%x.\n",
                        gdwDebugOutputLevel ? "ON" : "OFF", gdwDebugOutputLevel));

Cleanup:
    //
    // Cleanup
    //
    if (hKeySens)
        {
        RegCloseKey(hKeySens);
        }

    return;
}
#endif // DBG