//
// 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 OR INDEMNITIES.
//
/*++


Module Name:

    scerror.cxx

Abstract:

    Small client error processing


--*/

#include <sc.hxx>

//
//    Private routines
//
static int scerror_GetFileNameAndSize (WCHAR *szFileName, int ccFileName, DWORD *pdwSize) {
    SVSUTIL_ASSERT (ccFileName > 0);
    SVSUTIL_ASSERT_PREFAST (szFileName);

    int fError = FALSE;

    for ( ; ; ) {
        HKEY hKey;
        LONG hr = RegOpenKeyEx (HKEY_LOCAL_MACHINE, MSMQ_SC_REGISTRY_KEY, 0, KEY_READ | KEY_WRITE, &hKey);

        if (hr != ERROR_SUCCESS) {
            fError = TRUE;
            break;
        }

        DWORD dwType = REG_DWORD;
        DWORD dwValue = 0;
        DWORD dwSize = sizeof(dwValue);

        if ((RegQueryValueEx (hKey, L"LogSize", NULL, &dwType, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwValue)))
            *pdwSize = dwValue * 1024;

        dwSize = sizeof(WCHAR) * ccFileName;
        dwType = REG_SZ;
        szFileName[0] = L'\0';

        hr = RegQueryValueEx (hKey, L"BaseDir", NULL, &dwType, (LPBYTE)szFileName, &dwSize);

        RegCloseKey (hKey);

        if ((hr != ERROR_SUCCESS) || (dwType != REG_SZ) || (szFileName[0] == L'\0'))
            fError = TRUE;

        break;
    }

    if (fError)
        GetTempPath (ccFileName, szFileName);

    szFileName[ccFileName-1] = L'\0';
    WCHAR *p = szFileName + wcslen(szFileName);

    SVSUTIL_ASSERT (p != szFileName);

    if (*(p - 1) == L'\\')
        --p;

    if ((int)(p - szFileName + SVSUTIL_CONSTSTRLEN(MSMQ_SC_LOGFILE) + 2) > ccFileName)
        return FALSE;

    *p++ = L'\\';

    memcpy (p, MSMQ_SC_LOGFILE, sizeof (MSMQ_SC_LOGFILE));

    return TRUE;
}

static HANDLE scerror_GetFileError (void) {
    WCHAR szFileName[_MAX_PATH];
    DWORD dwSize = MSMQ_SC_LOGSIZE;
    HRESULT hr;

    if (! scerror_GetFileNameAndSize (szFileName, _MAX_PATH, &dwSize))
        return INVALID_HANDLE_VALUE;

    if (dwSize != 0) {
        WIN32_FIND_DATA fd;
        HANDLE hSearch = FindFirstFile (szFileName, &fd);
        if (hSearch != INVALID_HANDLE_VALUE) {
            CloseHandle (hSearch);

            if (fd.nFileSizeLow > (dwSize / 2)) {
                WCHAR szFileName2[_MAX_PATH];
                hr = StringCchCopyW (szFileName2, _countof(szFileName2), szFileName);
                ASSERT(SUCCEEDED(hr));
                hr = StringCchCatW (szFileName2, _countof(szFileName2), L".old");
                ASSERT(SUCCEEDED(hr));
                
                DeleteFile (szFileName2);

                if (! MoveFile (szFileName, szFileName2))
                    DeleteFile (szFileName);
            }
        }
    }

    return CreateFile (szFileName, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ,
                        NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}

static HANDLE scerror_GetFileInform (void) {
    return scerror_GetFileError ();
}

int hFile_vfwprintf (HANDLE hFile, WCHAR *lpszFormat, va_list args) {
    WCHAR szBigLine[MSMQ_SC_BIGBUFFER];

    if (FAILED(StringCchVPrintfW (szBigLine, MSMQ_SC_BIGBUFFER, lpszFormat, args)))
        return 0;

    char szMB[MSMQ_SC_BIGBUFFER];
    DWORD cBytes = WideCharToMultiByte (CP_ACP, 0, szBigLine, -1, szMB, sizeof(szMB), NULL, NULL) - 1;

    if (cBytes <= 0)    // Empty line
        return 0;

    SetFilePointer (hFile, 0, NULL, FILE_END);
    DWORD dwWritten = 0;

    if ((! WriteFile (hFile, szMB, cBytes, &dwWritten, NULL)) ||
        (dwWritten != cBytes))
        return 0;

    return 1;
}

void hFile_fwprintf (HANDLE hFile, WCHAR *lpszFormat, ...) {
    va_list args;

    va_start (args, lpszFormat);
    hFile_vfwprintf (hFile, lpszFormat, args);
    va_end (args);
}

void scerror_timestamp (HANDLE hFile) {
    SYSTEMTIME st;
    GetLocalTime (&st);
    hFile_fwprintf (hFile, L"[%02d:%02d:%02d %d/%d/%d] ", st.wHour, st.wMinute, st.wSecond, st.wMonth, st.wDay, st.wYear);
}

//
//    Main code section
//
void scerror_Complain (WCHAR *lpszFormat, ...) {
    HANDLE hFile = scerror_GetFileError ();

    if (hFile == INVALID_HANDLE_VALUE)
        return;

    scerror_timestamp (hFile);

    va_list args;
    va_start (args, lpszFormat);
    hFile_vfwprintf (hFile, lpszFormat, args);
    hFile_fwprintf (hFile, L"\n");

    va_end (args);

    CloseHandle (hFile);
}

void scerror_Complain (int iFormat, ...) {
    HANDLE hFile = scerror_GetFileError ();

    if (hFile == INVALID_HANDLE_VALUE)
        return;

    scerror_timestamp (hFile);

    WCHAR szFormat[MSMQ_SC_BIGBUFFER];

    LoadString (
        GetModuleHandle(NULL),
        iFormat, szFormat, MSMQ_SC_BIGBUFFER);

    va_list args;
    va_start (args, iFormat);
    hFile_vfwprintf (hFile, szFormat, args);
    hFile_fwprintf (hFile, L"\n");

    va_end (args);

    CloseHandle (hFile);
}

void scerror_Inform (WCHAR *lpszFormat, ...) {
    HANDLE hFile = scerror_GetFileError ();

    if (hFile == INVALID_HANDLE_VALUE)
        return;

    scerror_timestamp (hFile);

    va_list args;
    va_start (args, lpszFormat);
    hFile_vfwprintf (hFile, lpszFormat, args);
    hFile_fwprintf (hFile, L"\n");

    va_end (args);

    CloseHandle (hFile);
}

void scerror_Inform (int iFormat, ...) {
    HANDLE hFile = scerror_GetFileError ();

    if (hFile == INVALID_HANDLE_VALUE)
        return;

    scerror_timestamp (hFile);

    WCHAR szFormat[MSMQ_SC_BIGBUFFER];

    LoadString (
        GetModuleHandle(NULL),
        iFormat, szFormat, MSMQ_SC_BIGBUFFER);

    va_list args;
    va_start (args, iFormat);
    hFile_vfwprintf (hFile, szFormat, args);
    hFile_fwprintf (hFile, L"\n");

    va_end (args);

    CloseHandle (hFile);
}

int scerror_AssertOut (void *pvParam, WCHAR *lpszFormat, ...) {
    HANDLE hFile = scerror_GetFileError ();

    if (hFile == INVALID_HANDLE_VALUE)
        return 0;

    scerror_timestamp (hFile);

    va_list args;
    va_start (args, lpszFormat);
    hFile_vfwprintf (hFile, lpszFormat, args);
    hFile_fwprintf (hFile, L"\n");

    va_end (args);

    CloseHandle (hFile);

    return 1;
}

#if defined (SC_VERBOSE)
unsigned int g_bCurrentMask    = 0;
unsigned int g_bOutputChannels = 0;

void scerror_DebugInitialize (void) {
    g_bCurrentMask    = 0;
    g_bOutputChannels = 0;

    HKEY hKey;
    LONG hr = RegOpenKeyEx (HKEY_LOCAL_MACHINE, MSMQ_SC_REGISTRY_KEY, 0, KEY_READ | KEY_WRITE, &hKey);

    if (hr != ERROR_SUCCESS)
        return;

    DWORD dwType = 0;
    DWORD dwSize = sizeof(g_bOutputChannels);

    RegQueryValueEx (hKey, L"DebugOutputChannels", NULL, &dwType, (LPBYTE)&g_bOutputChannels, &dwSize);
    RegQueryValueEx (hKey, L"DebugOutputMask", NULL, &dwType, (LPBYTE)&g_bCurrentMask, &dwSize);

    RegCloseKey (hKey);
}

void scerror_DebugOutPrintArgs(WCHAR *lpszFormat, va_list args) {
    if (! g_bOutputChannels)
        return;

    WCHAR szBigBuffer[MSMQ_SC_BIGBUFFER];
    HRESULT hr = StringCchPrintfW (szBigBuffer, _countof(szBigBuffer), lpszFormat, args);
    ASSERT(SUCCEEDED(hr));

    if (g_bOutputChannels & VERBOSE_OUTPUT_CONSOLE)
        wprintf_s (L"%s", szBigBuffer);

    if (g_bOutputChannels & VERBOSE_OUTPUT_LOGFILE) {
        HANDLE hFile = scerror_GetFileError ();

        if (hFile != INVALID_HANDLE_VALUE) {
            hFile_fwprintf (hFile, L"%s", szBigBuffer);
            CloseHandle (hFile);
        }
    }

    if (g_bOutputChannels & VERBOSE_OUTPUT_DEBUGMSG) {
        OutputDebugString (L"MSMQ : ");
        OutputDebugString (szBigBuffer);
        OutputDebugString (L"\n");
    }
}

void scerror_DebugOutPrint(WCHAR *lpszFormat, ...) {
    va_list args;
    va_start (args, lpszFormat);
    scerror_DebugOutPrintArgs(lpszFormat,args);
    va_end (args);
}

int scerror_DebugOut (unsigned int fMask, WCHAR *lpszFormat, ...) {
    if (! (fMask & g_bCurrentMask))
        return 0;

    va_list args;
    va_start (args, lpszFormat);
    scerror_DebugOutPrintArgs(lpszFormat,args);
    va_end (args);
    return 1;
}

#else

int scerror_DebugOut (unsigned int fMask, WCHAR *lpszFormat, ...) {
    return 0;
}

void scerror_DebugInitialize (void) {
}

#endif        // SC_VERBOSE
