//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
//------------------------------------------------------------------------------
// 
//      Bluetooth DEBUG
// 
// 
// Module Name:
// 
//      btdebug.cxx
// 
// Abstract:
// 
//      This file implements Bluetooth Debug Subsystem
// 
// 
//------------------------------------------------------------------------------
#include <windows.h>

#if defined (DEBUG) || defined (_DEBUG) || defined (RETAILLOG)
#include <stdio.h>

#if ! defined (SDK_BUILD)
#define BT_USE_CELOG    1
#endif

#include <bt_debug.h>
#include <svsutil.hxx>
#include <bt_os.h>

static CRITICAL_SECTION gcs_debug;

static unsigned int        gMask;
static int                gOutputMode;
static int                gOutputCnt;
static FILE             *gfpOutputFile;

#define BPR        8

// Debug Zones.
#if defined (DEBUG)
  DBGPARAM dpCurSettings = {
    TEXT("BTD"), {
    TEXT("Output"),TEXT("Shell"),TEXT("HCI"),
    TEXT("L2CAP"),TEXT("RFCOMM"),TEXT("SDP"),TEXT("AVDTP"),
    TEXT("Packets"),TEXT("Verbose"),TEXT("Dump"),TEXT("Init"),TEXT("Callback"),
    TEXT("Trace"),TEXT("Transport"),TEXT("Warn"),TEXT("Error") },
    0xc001
  };

unsigned long gulZoneMask;

#endif

void DebugOut (unsigned int cMask, WCHAR *lpszFormat, ...) {
#if defined (DEBUG)
    if (gulZoneMask != dpCurSettings.ulZoneMask) {
        EnterCriticalSection (&gcs_debug);
        if (gulZoneMask != dpCurSettings.ulZoneMask) {
            gulZoneMask = dpCurSettings.ulZoneMask;
            gMask = 0;
            if (gulZoneMask & 0x0001)
                gMask |= DEBUG_OUTPUT;
            if (gulZoneMask & 0x0002)
                gMask |= DEBUG_SHELL_ALL;
            if (gulZoneMask & 0x0004)
                gMask |= DEBUG_HCI_ALL;
            if (gulZoneMask & 0x0008)
                gMask |= DEBUG_L2CAP_ALL;
            if (gulZoneMask & 0x0010)
                gMask |= DEBUG_RFCOMM_ALL;
            if (gulZoneMask & 0x0020)
                gMask |= DEBUG_SDP_ALL;
            if (gulZoneMask & 0x0040)
                gMask |= DEBUG_AVDTP_ALL;
            if (gulZoneMask & 0x0080)
                gMask |= (DEBUG_HCI_PACKETS | DEBUG_L2CAP_PACKETS | DEBUG_RFCOMM_PACKETS | DEBUG_SDP_PACKETS | DEBUG_AVDTP_PACKETS | DEBUG_PAN_PACKETS);
            if (gulZoneMask & 0x0100)
                gMask |= DEBUG_VERBOSE;
            if (gulZoneMask & 0x0200)
                gMask |= DEBUG_HCI_DUMP;
            if (gulZoneMask & 0x0400)
                gMask |= (DEBUG_SHELL_INIT | DEBUG_HCI_INIT | DEBUG_L2CAP_INIT | DEBUG_RFCOMM_INIT | DEBUG_SDP_INIT | DEBUG_AVDTP_INIT | DEBUG_PAN_INIT);
            if (gulZoneMask & 0x0800)
                gMask |= (DEBUG_HCI_CALLBACK | DEBUG_L2CAP_CALLBACK | DEBUG_RFCOMM_CALLBACK | DEBUG_SDP_CALLBACK | DEBUG_AVDTP_CALLBACK | DEBUG_PAN_CALLBACK);
            if (gulZoneMask & 0x1000)
                gMask |= (DEBUG_HCI_TRACE | DEBUG_L2CAP_TRACE | DEBUG_RFCOMM_TRACE | DEBUG_SDP_TRACE | DEBUG_AVDTP_TRACE | DEBUG_PAN_TRACE);
            if (gulZoneMask & 0x2000)
                gMask |= DEBUG_HCI_TRANSPORT;
            if (gulZoneMask & 0x4000)
                gMask |= DEBUG_WARN;
            if (gulZoneMask & 0x8000)
                gMask |= DEBUG_ERROR;
        }
        LeaveCriticalSection (&gcs_debug);
    }

#endif
    if (cMask && (! (cMask & gMask)))
        return;

    EnterCriticalSection (&gcs_debug);

    static WCHAR szBigBuffer[2000];
    va_list arglist;
    va_start (arglist, lpszFormat);
    wvsprintf (szBigBuffer, lpszFormat, arglist);
    va_end (arglist);

    WCHAR *pszWhat = L"[MULTI] ";
    if (cMask & DEBUG_ERROR)
        pszWhat = L"[ERR] ";
    else if (cMask & DEBUG_WARN)
        pszWhat = L"[WARN] ";
    else if (cMask & DEBUG_OUTPUT)
        pszWhat = L"";
    else if ((cMask & DEBUG_SHELL_ALL) == cMask)
        pszWhat = L"[SHELL] ";
    else if ((cMask & DEBUG_HCI_ALL) == cMask)
        pszWhat = L"[HCI] ";
    else if ((cMask & DEBUG_L2CAP_ALL) == cMask)
        pszWhat = L"[L2CAP] ";
    else if ((cMask & DEBUG_RFCOMM_ALL) == cMask)
        pszWhat = L"[RFCOMM] ";
    else if ((cMask & DEBUG_SDP_ALL) == cMask)
        pszWhat = L"[SDP] ";
    else if ((cMask & DEBUG_AVDTP_ALL) == cMask)
        pszWhat = L"[AVDTP] ";
    else if ((cMask & DEBUG_PAN_ALL) == cMask)
        pszWhat = L"[PAN] ";
    else if ((cMask & DEBUG_VERBOSE) == cMask)
        pszWhat = L"";
    else if ((cMask & DEBUG_HCI_TRANSPORT_ALL) == cMask)
        pszWhat = L"[TRP] ";

    if (gOutputMode & OUTPUT_MODE_REOPEN) {
        if (gfpOutputFile) {
            fclose (gfpOutputFile);
            gfpOutputFile = NULL;
        }

        gOutputMode &= ~OUTPUT_MODE_REOPEN;
    }

    if ((gOutputMode != OUTPUT_MODE_FILE) && gfpOutputFile) {
        fclose (gfpOutputFile);
        gfpOutputFile = NULL;
    }

    if ((gOutputMode == OUTPUT_MODE_FILE) && (! gfpOutputFile)) {
        WCHAR szFileName[50];
        wsprintf (szFileName, L"\\temp\\btd%d.txt", (gOutputCnt++) & 1);
        gfpOutputFile = _wfopen (szFileName, L"w");
    }

    if (gOutputMode == OUTPUT_MODE_FILE) {
        if (gfpOutputFile) {
            fwprintf (gfpOutputFile, L"%s%s", pszWhat, szBigBuffer);

            if (ftell (gfpOutputFile) > OUTPUT_FILE_MAX)
                gOutputMode |= OUTPUT_MODE_REOPEN;
        }
    } else if (gOutputMode == OUTPUT_MODE_CONSOLE) {
#if ! defined (UNDER_CE)
        HANDLE hStdout = GetStdHandle (STD_OUTPUT_HANDLE);
        if (hStdout != INVALID_HANDLE_VALUE) {
            if (cMask & DEBUG_ERROR)
                SetConsoleTextAttribute (hStdout, FOREGROUND_RED | FOREGROUND_INTENSITY);
            else if (cMask & DEBUG_WARN)
                SetConsoleTextAttribute (hStdout, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY);
        }
#endif
        wprintf (L"%s%s", pszWhat, szBigBuffer);
#if ! defined (UNDER_CE)
        if ((hStdout != INVALID_HANDLE_VALUE) && (cMask & (DEBUG_ERROR | DEBUG_WARN)))
            SetConsoleTextAttribute (hStdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);    
#endif
    } else if (gOutputMode == OUTPUT_MODE_CELOG) {
#if defined (BT_USE_CELOG)
        RETAILCELOGMSG(1, (L"%s%s", pszWhat, szBigBuffer));
#endif
    } else {
        OutputDebugString (pszWhat);
        OutputDebugString (szBigBuffer);
    }

    LeaveCriticalSection (&gcs_debug);
}

void DumpBuff (unsigned int cMask, unsigned char *lpBuffer, unsigned int cBuffer) {
    if (cMask && (! (cMask & gMask)))
        return;

    EnterCriticalSection (&gcs_debug);

    WCHAR szLine[5 + 7 + 2 + 4 * BPR];

    for (int i = 0 ; i < (int)cBuffer ; i += BPR) {
        int bpr = cBuffer - i;
        if (bpr > BPR)
            bpr = BPR;

        wsprintf (szLine, L"%04x ", i);
        WCHAR *p = szLine + wcslen (szLine);

        for (int j = 0 ; j < bpr ; ++j) {
            WCHAR c = (lpBuffer[i + j] >> 4) & 0xf;
            if (c > 9) c += L'a' - 10; else c += L'0';
            *p++ = c;
            c = lpBuffer[i + j] & 0xf;
            if (c > 9) c += L'a' - 10; else c += L'0';
            *p++ = c;
            *p++ = L' ';
        }

        for ( ; j < BPR ; ++j) {
            *p++ = L' ';
            *p++ = L' ';
            *p++ = L' ';
        }

        *p++ = L' ';
        *p++ = L' ';
        *p++ = L' ';
        *p++ = L'|';
        *p++ = L' ';
        *p++ = L' ';
        *p++ = L' ';

        for (j = 0 ; j < bpr ; ++j) {
            WCHAR c = lpBuffer[i + j];
            if ((c < L' ') || (c >= 127))
                c = L'.';

            *p++ = c;
        }

        for ( ; j < BPR ; ++j) {
            *p++ = L' ';
        }

        *p++ = L'\n';
        *p++ = L'\0';

        SVSUTIL_ASSERT (p == szLine + sizeof(szLine)/sizeof(szLine[0]));

        DebugOut (cMask, L"%s", szLine);
    }

    LeaveCriticalSection (&gcs_debug);
}

void DumpBuffPfx (unsigned int cMask, WCHAR *lpszLinePrefix, unsigned char *lpBuffer, unsigned int cBuffer) {
    if (cMask && (! (cMask & gMask)))
        return;

    EnterCriticalSection (&gcs_debug);

    WCHAR szLine[5 + 7 + 2 + 4 * BPR];

    for (int i = 0 ; i < (int)cBuffer ; i += BPR) {
        int bpr = cBuffer - i;
        if (bpr > BPR)
            bpr = BPR;

        wsprintf (szLine, L"%04x ", i);
        WCHAR *p = szLine + wcslen (szLine);

        for (int j = 0 ; j < bpr ; ++j) {
            WCHAR c = (lpBuffer[i + j] >> 4) & 0xf;
            if (c > 9) c += L'a' - 10; else c += L'0';
            *p++ = c;
            c = lpBuffer[i + j] & 0xf;
            if (c > 9) c += L'a' - 10; else c += L'0';
            *p++ = c;
            *p++ = L' ';
        }

        for ( ; j < BPR ; ++j) {
            *p++ = L' ';
            *p++ = L' ';
            *p++ = L' ';
        }

        *p++ = L' ';
        *p++ = L' ';
        *p++ = L' ';
        *p++ = L'|';
        *p++ = L' ';
        *p++ = L' ';
        *p++ = L' ';

        for (j = 0 ; j < bpr ; ++j) {
            WCHAR c = lpBuffer[i + j];
            if ((c < L' ') || (c >= 127))
                c = L'.';

            *p++ = c;
        }

        for ( ; j < BPR ; ++j) {
            *p++ = L' ';
        }

        *p++ = L'\n';
        *p++ = L'\0';

        SVSUTIL_ASSERT (p == szLine + sizeof(szLine)/sizeof(szLine[0]));

        DebugOut (cMask, L"%s %s", lpszLinePrefix, szLine);
    }

    LeaveCriticalSection (&gcs_debug);
}

void DebugInitialize (void) {
    static int s_fInit = 0;

    gOutputCnt = 0;

    DeleteFile (L"\\temp\\btd0.txt");
    DeleteFile (L"\\temp\\btd1.txt");

#if defined (UNDER_CE)
#if defined (SDK_BUILD)
    gOutputMode = OUTPUT_MODE_FILE;
#else
    gOutputMode = OUTPUT_MODE_DEBUG;
#endif
#else
    gOutputMode = OUTPUT_MODE_CONSOLE;
#endif

    if (!s_fInit) {
        HKEY hk;

        gMask = 0;
        s_fInit = TRUE;
        InitializeCriticalSection (&gcs_debug);
        
        if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_BASE, L"software\\Microsoft\\bluetooth\\Debug", 0, KEY_READ, &hk))
            return;

        DWORD dwSize = sizeof(gMask);
        DWORD dwType;
        RegQueryValueEx (hk, L"Mask", 0, &dwType, (LPBYTE)&gMask, &dwSize);

        dwSize = sizeof(gMask);
        RegQueryValueEx (hk, L"Console", 0, &dwType, (LPBYTE)&gOutputMode, &dwSize);

        RegCloseKey (hk);
    #if defined (DEBUG)
        dpCurSettings.ulZoneMask = 0;
        gulZoneMask = 0;
    #endif
    }
    
}

void DebugUninitialize (void) {
    gMask = 0;
    gOutputMode = OUTPUT_MODE_DEBUG;
    DeleteCriticalSection (&gcs_debug);
}

void DebugSetMask (unsigned int aMask) {
    gMask = aMask;
}

void DebugSetOutput (unsigned int fConsole) {
    if (fConsole > OUTPUT_MODE_MAX)
        gOutputMode |= fConsole;
    else
        gOutputMode = fConsole;
}

#else
void DebugOut (unsigned int cMask, WCHAR *lpszFormat, ...) {
}

void DumpBuff (unsigned int cMask, unsigned char *lpBuffer, unsigned int cBuffer) {
}

void DebugInitialize (void) {
}

void DebugUninitialize (void) {
}

void DebugSetMask (unsigned int aMask) {
}

void DebugSetOutput (unsigned int fConsole) {
}

#endif    // DEBUG || _DEBUG
