//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  Copyright (c) 1992 - 1997  Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------;


// For every module and executable we store a debugging level and flags
// for the types of output that are desired. Constants for the types are
// defined in WXDEBUG.H and more can be added.
// The keys are stored in the registry under the
// HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\Type and
// HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\Level key values
//
// There are also global values under SOFTWARE\Debug\Global which are loaded
// after the module-specific values. The Types specified there are OR'ed with
// the module specific types and m_dwLevel is set to the greater of the global
// and the module specific settings.

#include <stdarg.h>
#include <stdio.h>

#include "extdll.h"
#include "util.h"
#include "wxdebug.h"

#include <tchar.h>

#ifdef _DEBUG

void WINAPI DbgInitModuleName(void);
void WINAPI DbgInitModuleSettings(void);
void WINAPI DbgInitGlobalSettings(void);
void WINAPI DbgInitLogTo(HKEY hKey);
void WINAPI DbgInitKeyLevels(HKEY hKey, DWORD *pdwTypes, DWORD *pdwLevel);



const INT iDEBUGINFO = 512;                 // Used to format strings

HINSTANCE m_hInst;                          // Module instance handle
TCHAR m_ModuleName[iDEBUGINFO];             // Cut down module name
//CRITICAL_SECTION m_CSDebug;                 // Controls access to list
BOOL m_bInit = FALSE;                       // Have we been initialised
HANDLE m_hOutput = INVALID_HANDLE_VALUE;    // Optional output written here
DWORD m_dwTypes = 0;
DWORD m_dwLevel = 0;

const TCHAR *m_pBaseKey = TEXT("SOFTWARE\\Debug");
const TCHAR *m_pGlobalKey = TEXT("GLOBAL");
TCHAR *pKeyNames[] =
{
    TEXT("Types"),
    TEXT("Level")
};


// DbgInitialize
// This sets the instance handle that the debug library uses to find
// the module's file name from the Win32 GetModuleFileName function
void WINAPI DbgInitialise(HINSTANCE hInst)
{
    if (!m_bInit)
    {
        //InitializeCriticalSection(&m_CSDebug);
        m_bInit = TRUE;
        m_hInst = hInst;
        DbgInitModuleName();
        DbgInitModuleSettings();
        DbgInitGlobalSettings();
    }
}


// DbgTerminate
// This is called to clear up any resources the debug library uses - at the
// moment we delete our critical section and the handle of the output file.
void WINAPI DbgTerminate()
{
    if (m_bInit)
    {
        if (m_hOutput != INVALID_HANDLE_VALUE)
        {
            DBGASSERTEXECUTE(CloseHandle(m_hOutput));
            m_hOutput = INVALID_HANDLE_VALUE;
        }
        //DeleteCriticalSection(&m_CSDebug);
        m_bInit = FALSE;
    }
}


// DbgInitModuleName
// Initialise the module file name
void WINAPI DbgInitModuleName()
{
    TCHAR FullName[iDEBUGINFO];     // Load the full path and module name
    TCHAR *pName;                   // Searches from the end for a backslash

    GetModuleFileName(m_hInst,FullName,iDEBUGINFO);
    pName = _tcsrchr(FullName,'\\');
    if (pName == NULL)
    {
        pName = FullName;
    }
    else
    {
        pName++;
    }
    lstrcpy(m_ModuleName,pName);
}


// DbgInitModuleSettings
// Retrieve the module-specific settings
void WINAPI DbgInitModuleSettings()
{
    LONG lReturn;               // Create key return value
    TCHAR szInfo[iDEBUGINFO];   // Constructs key names
    HKEY hModuleKey;            // Module key handle

    // Construct the base key name
    wsprintf(szInfo,TEXT("%s\\%s"),m_pBaseKey,m_ModuleName);

    // Create or open the key for this module
    lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key
                             szInfo,               // Address of subkey name
                             (DWORD)0,             // Reserved value
                             NULL,                 // Address of class name
                             (DWORD)0,             // Special options flags
                             KEY_ALL_ACCESS,       // Desired security access
                             NULL,                 // Key security descriptor
                             &hModuleKey,          // Opened handle buffer
                             NULL);                // What really happened

    if (lReturn != ERROR_SUCCESS)
    {
        DbgLogInfo(LOG_ERROR, 0, TEXT("Could not access module key"));
        return;
    }

    DbgInitLogTo(hModuleKey);
    DbgInitKeyLevels(hModuleKey, &m_dwTypes, &m_dwLevel);
    RegCloseKey(hModuleKey);
}


// DbgInitGlobalSettings
// This is called by DbgInitialize to read the global debug settings for
// Level and Type from the registry. The Types are OR'ed together and m_dwLevel
// is set to the greater of the global and module-specific values.
void WINAPI DbgInitGlobalSettings()
{
    LONG lReturn;               // Create key return value
    TCHAR szInfo[iDEBUGINFO];   // Constructs key names
    HKEY hGlobalKey;            // Global override key
    DWORD dwTypes = 0;
    DWORD dwLevel = 0;

    // Construct the global base key name
    wsprintf(szInfo,TEXT("%s\\%s"),m_pBaseKey,m_pGlobalKey);

    // Create or open the key for this module
    lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key
                             szInfo,               // Address of subkey name
                             (DWORD)0,             // Reserved value
                             NULL,                 // Address of class name
                             (DWORD)0,             // Special options flags
                             KEY_ALL_ACCESS,       // Desired security access
                             NULL,                 // Key security descriptor
                             &hGlobalKey,          // Opened handle buffer
                             NULL);                // What really happened

    if (lReturn != ERROR_SUCCESS)
    {
        DbgLogInfo(LOG_ERROR, 0, TEXT("Could not access GLOBAL module key"));
        return;
    }

    DbgInitKeyLevels(hGlobalKey, &dwTypes, &dwLevel);
    RegCloseKey(hGlobalKey);

    m_dwTypes |= dwTypes;
    if (dwLevel > m_dwLevel)
        m_dwLevel = dwLevel;
}


// DbgInitLogTo
// Called by DbgInitModuleSettings to setup alternate logging destinations
void WINAPI DbgInitLogTo(HKEY hKey)
{
    LONG  lReturn;
    DWORD dwKeyType;
    DWORD dwKeySize;
    TCHAR szFile[MAX_PATH] = {0};
    static const TCHAR cszKey[] = TEXT("LogToFile");

    dwKeySize = MAX_PATH;
    lReturn = RegQueryValueEx(
        hKey,                       // Handle to an open key
        cszKey,                     // Subkey name derivation
        NULL,                       // Reserved field
        &dwKeyType,                 // Returns the field type
        (LPBYTE) szFile,            // Returns the field's value
        &dwKeySize);                // Number of bytes transferred

    // create an empty key if it does not already exist
    if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ)
       {
       dwKeySize = 1;
       lReturn = RegSetValueEx(
            hKey,                   // Handle of an open key
            cszKey,                 // Address of subkey name
            (DWORD) 0,              // Reserved field
            REG_SZ,                 // Type of the key field
            (PBYTE)szFile,          // Value for the field
            dwKeySize);            // Size of the field buffer
       }

    // if an output-to was specified.  try to open it.
    if (m_hOutput != INVALID_HANDLE_VALUE)
    {
       DBGASSERTEXECUTE(CloseHandle(m_hOutput));
       m_hOutput = INVALID_HANDLE_VALUE;
    }
    if (szFile[0] != 0)
    {
        if (!lstrcmpi(szFile, TEXT("Console")))
        {
            m_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
            if (m_hOutput == INVALID_HANDLE_VALUE)
            {
                AllocConsole();
                m_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
            }
            SetConsoleTitle (TEXT("Valve Debug Output"));
        } else if (szFile[0] &&
            lstrcmpi(szFile, TEXT("Debug")) &&
            lstrcmpi(szFile, TEXT("Debugger")) &&
            lstrcmpi(szFile, TEXT("Deb")))
        {
            m_hOutput = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            if (INVALID_HANDLE_VALUE != m_hOutput)
            {
                static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n");
                SetFilePointer (m_hOutput, 0, NULL, FILE_END);
                DbgOutString (cszBar);
            }
        }
    }
}


// DbgInitKeyLevels
// This is called by DbgInitModuleSettings and DbgInitGlobalSettings to read 
// settings for Types and Level from the registry
void WINAPI DbgInitKeyLevels(HKEY hKey, DWORD *pdwTypes, DWORD *pdwLevel)
{
    LONG lReturn;               // Create key return value
    DWORD dwKeySize;            // Size of the key value
    DWORD dwKeyType;            // Receives it's type

    // Get the Types value
    dwKeySize = sizeof(DWORD);
    lReturn = RegQueryValueEx(
        hKey,                   // Handle to an open key
        pKeyNames[0],           // Subkey name derivation
        NULL,                   // Reserved field
        &dwKeyType,             // Returns the field type
        (LPBYTE)pdwTypes,       // Returns the field's value
        &dwKeySize );           // Number of bytes transferred

    // If either the key was not available or it was not a DWORD value
    // then we ensure only the high priority debug logging is output
    //  but we try and update the field to a zero filled DWORD value

    if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)
    {
        *pdwTypes = 0;
        lReturn = RegSetValueEx(
            hKey,               // Handle of an open key
            pKeyNames[0],       // Address of subkey name
            (DWORD)0,           // Reserved field
            REG_DWORD,          // Type of the key field
            (PBYTE)pdwTypes,    // Value for the field
            sizeof(DWORD));     // Size of the field buffer

        if (lReturn != ERROR_SUCCESS)
        {
            DbgLogInfo(LOG_ERROR, 0, TEXT("Could not create subkey %s"),pKeyNames[0]);
            *pdwTypes = 0;
        }
    }

    // Get the Level value
    dwKeySize = sizeof(DWORD);
    lReturn = RegQueryValueEx(
        hKey,                   // Handle to an open key
        pKeyNames[1],           // Subkey name derivation
        NULL,                   // Reserved field
        &dwKeyType,             // Returns the field type
        (LPBYTE)pdwLevel,       // Returns the field's value
        &dwKeySize );           // Number of bytes transferred

    // If either the key was not available or it was not a DWORD value
    // then we ensure only the high priority debug logging is output
    //  but we try and update the field to a zero filled DWORD value

    if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)
    {
        *pdwLevel = 0;
        lReturn = RegSetValueEx(
            hKey,               // Handle of an open key
            pKeyNames[1],       // Address of subkey name
            (DWORD)0,           // Reserved field
            REG_DWORD,          // Type of the key field
            (PBYTE)pdwLevel,   // Value for the field
            sizeof(DWORD));     // Size of the field buffer

        if (lReturn != ERROR_SUCCESS)
        {
            DbgLogInfo(LOG_ERROR, 0, TEXT("Could not create subkey %s"),pKeyNames[1]);
            *pdwLevel = 0;
        }
    }
}


// DbgOutString
void WINAPI DbgOutString(LPCTSTR psz)
{
    if (!m_bInit)
        return;
    if (m_hOutput != INVALID_HANDLE_VALUE) {
        UINT  cb = lstrlen(psz);
        DWORD dw;
        WriteFile (m_hOutput, psz, cb, &dw, NULL);
    } else {
        OutputDebugString (psz);
    }
}


// DbgLogInfo
// Print a formatted string to the debugger prefixed with this module's name
// Because the debug code is linked statically every module loaded will
// have its own copy of this code. It therefore helps if the module name is
// included on the output so that the offending code can be easily found
void WINAPI DbgLogInfo(DWORD Type, DWORD Level, const TCHAR *pFormat,...)
{
    if (!m_bInit)
        return;
    // Check the current level for this type combination */
    if (((Type & m_dwTypes) == 0) || (m_dwLevel < Level))
        return;

    TCHAR szInfo[2000];

    // Format the variable length parameter list

    va_list va;
    va_start(va, pFormat);

    //lstrcpy(szInfo, m_ModuleName);
    //lstrcat(szInfo, TEXT(": "));
    wvsprintf(szInfo /* + lstrlen(szInfo) */, pFormat, va);
    //lstrcat(szInfo, TEXT("\r\n"));
    DbgOutString(szInfo);

    va_end(va);
}


// DbgKernelAssert
// If we are executing as a pure kernel filter we cannot display message
// boxes to the user, this provides an alternative which puts the error
// condition on the debugger output with a suitable eye catching message
void WINAPI DbgKernelAssert(const TCHAR *pCondition, const TCHAR *pFileName, INT iLine)
{
    if (!m_bInit)
        return;
	DbgLogInfo(LOG_ERROR, 0, TEXT(m_ModuleName));
    DbgLogInfo(LOG_ERROR, 0, TEXT(": Assertion FAILED (%s) at line %d in file %s\r\n"), pCondition, iLine, pFileName);
    DebugBreak();
}

#endif // _DEBUG