//
// 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 Console
// 
// 
// Module Name:
// 
//      btconsole.cxx
// 
// Abstract:
// 
//      This file implements Bluetooth Console
// 
// 
//------------------------------------------------------------------------------
#include <windows.h>
#include <stdio.h>

#include <svsutil.hxx>

#include <bt_debug.h>

#include <bt_buffer.h>
#include <bt_ddi.h>
#include <bt_hcip.h>

#if defined (BTH_CONSOLE)

static HANDLE hConsole = NULL;
static CRITICAL_SECTION cs;

static WCHAR *skipwhite (WCHAR *p) {
    while ((*p != '\0') && iswspace (*p))
        ++p;

    return *p == '\0' ? NULL : p;
}

static DWORD WINAPI bth_ConsoleThread (LPVOID pParm) {
    for ( ; ; ) {
        WCHAR szBuffer[500];
        if (! fgetws (szBuffer, sizeof(szBuffer) / sizeof(szBuffer[0]), stdin))
            return 0;

        WCHAR *p = wcschr (szBuffer, L'\n');
        if (p)
            *p = L'\0';

        int iRes = ERROR_SUCCESS;

        if (wcsicmp (szBuffer, L"exit") == 0)
            break;
        else if (wcsicmp (szBuffer, L"break") == 0) {
            DebugBreak ();
            continue;
        } else if (wcsnicmp (szBuffer, L"debug 0x", 8) == 0) {
            p = szBuffer + 8;
            unsigned int mask = 0;
            for (int i = 0 ; i < 8 ; ++i, ++p) {
                int c = *p;
                if (c >= 'a')
                    c -= 'a' - 'A';

                if ((c < '0') || (c > 'F') || ((c > '9') && (c < 'A')))
                    break;

                if (c >= 'A')
                    c -= 'A' - 10;
                else
                    c -= '0';

                mask = (mask << 4) | c;
            }

            if (i == 8) {
                DebugSetMask (mask); 
                DebugOut (DEBUG_OUTPUT, L"Set mask to 0x%08x\n", mask);
            } else
                DebugOut (DEBUG_OUTPUT, L"Command format is : debug 0xXXXXXXXX\n");
            continue;
        } else if (wcsicmp (szBuffer, L"stop card") == 0) {
            HCI_StopHardware ();
            continue;
        } else if (wcsicmp (szBuffer, L"start card") == 0) {
            HCI_StartHardware ();
            continue;
        } else if (wcsnicmp (szBuffer, L"tdi", 3) == 0) {
            if (p = skipwhite (szBuffer + 3))
                iRes = tdiR_ProcessConsoleCommand (p);
            else
                iRes = tdiR_ProcessConsoleCommand (L"help");
        } else if (wcsnicmp (szBuffer, L"portemu", 7) == 0) {
            if (p = skipwhite (szBuffer + 7))
                iRes = portemu_ProcessConsoleCommand (p);
            else
                iRes = portemu_ProcessConsoleCommand (L"help");
        } else if (wcsnicmp (szBuffer, L"rfcomm", 6) == 0) {
            if (p = skipwhite (szBuffer + 6))
                iRes = rfcomm_ProcessConsoleCommand (p);
            else
                iRes = rfcomm_ProcessConsoleCommand (L"help");
        } else if (wcsnicmp( szBuffer, L"sdp", 3) == 0) {
            if (p = skipwhite (szBuffer + 3))
                iRes = sdp_ProcessConsoleCommand (p);
            else
                iRes = sdp_ProcessConsoleCommand (L"help");
        } else if (wcsnicmp(szBuffer, L"l2cap", 5) == 0) {
            if (p = skipwhite (szBuffer + 5))
                iRes = l2cap_ProcessConsoleCommand (p);
            else
                iRes = l2cap_ProcessConsoleCommand (L"help");
        } else if (wcsnicmp(szBuffer, L"hci", 3) == 0) {
            if (p = skipwhite (szBuffer + 3))
                iRes = hci_ProcessConsoleCommand (p);
            else
                iRes = hci_ProcessConsoleCommand (L"help");
        } else if (wcsnicmp (szBuffer, L"help", 4) == 0) {
            p = skipwhite (szBuffer + 4);
            if (p) {
                if (wcsicmp (p, L"rfcomm") == 0)
                    iRes = rfcomm_ProcessConsoleCommand (L"help");
                else if (wcsicmp (p, L"portemu") == 0)
                    iRes = portemu_ProcessConsoleCommand (L"help");
                else if (wcsicmp (p, L"tdi") == 0)
                    iRes = tdiR_ProcessConsoleCommand (L"help");
                else if (wcsicmp (p, L"sdp") == 0)
                    iRes = sdp_ProcessConsoleCommand (L"help");
                else if (wcsicmp (p, L"sdp") == 0)
                    iRes = sdp_ProcessConsoleCommand (L"help");
                else if (wcsicmp (p, L"l2cap") == 0)
                    iRes = l2cap_ProcessConsoleCommand (L"help");
                else if (wcsicmp (p, L"hci") == 0)
                    iRes = hci_ProcessConsoleCommand (L"help");
                else {
                    tdiR_ProcessConsoleCommand (L"help");
                    portemu_ProcessConsoleCommand (L"help");
                    rfcomm_ProcessConsoleCommand (L"help");
                    sdp_ProcessConsoleCommand (L"help");
                    l2cap_ProcessConsoleCommand (L"help");
                    hci_ProcessConsoleCommand (L"help");
                }
            } else {
                tdiR_ProcessConsoleCommand (L"help");
                portemu_ProcessConsoleCommand (L"help");
                rfcomm_ProcessConsoleCommand (L"help");
                sdp_ProcessConsoleCommand (L"help");
                l2cap_ProcessConsoleCommand (L"help");
                hci_ProcessConsoleCommand (L"help");
            }
        }

        if (iRes != ERROR_SUCCESS)
            DebugOut (DEBUG_OUTPUT, L"Command not understood\n");
    }
    return 0;
}

int bth_StartConsole (void) {
    if (hConsole)
        return ERROR_ALREADY_INITIALIZED;

    InitializeCriticalSection (&cs);

    hConsole = CreateThread (NULL, 0, bth_ConsoleThread, NULL, 0, NULL);
    if (! hConsole)
        return GetLastError ();

    return ERROR_SUCCESS;
}

int bth_EndConsole (void) {
    HANDLE h = (HANDLE)InterlockedExchange ((LONG *)&hConsole, (LONG)NULL);

    if (! h)
        return ERROR_SERVICE_NOT_ACTIVE;

    EnterCriticalSection (&cs);

    // To make console code work properly again we need to find a graceful way
    // to shut down the Console thread.  We cannot terminate thread inside the 
    // kernel.
    SVSUTIL_ASSERT(0);

    CloseHandle (h);
    LeaveCriticalSection (&cs);
    DeleteCriticalSection (&cs);

    return ERROR_SUCCESS;
}

#else

int bth_StartConsole (void) { return ERROR_SUCCESS; }

int bth_EndConsole (void) { return ERROR_SUCCESS; }

#endif
