/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    LogWrappers.h

Abstract:

    Helper classes for logging APIs

Author:

    Hakki T. Bostanci (hakkib) 06-Apr-2000

Revision History:

--*/

#ifndef LOGWRAPPERS_H
#define LOGWRAPPERS_H

#include <map>

#include "LogWindow.h"

//////////////////////////////////////////////////////////////////////////
//
//
//

#define TLS_NOCONSOLE 0x04000000L

//////////////////////////////////////////////////////////////////////////
//
//
//

#define LOG_LEVELS    0x0000FFFFL    // These are used to mask out the
#define LOG_STYLES    0xFFFF0000L    // styles or levels from log object.

#define TLS_LOGALL    0x0000FFFFL    // Log output.  Logs all the time.
#define TLS_LOG       0x00000000L    // Log output.  Logs all the time.
#define TLS_INFO      0x00002000L    // Log information.
#define TLS_ABORT     0x00000001L    // Log Abort, then kill process.
#define TLS_SEV1      0x00000002L    // Log at Severity 1 level
#define TLS_SEV2      0x00000004L    // Log at Severity 2 level
#define TLS_SEV3      0x00000008L    // Log at Severity 3 level
#define TLS_WARN      0x00000010L    // Log at Warn level
#define TLS_PASS      0x00000020L    // Log at Pass level
#define TLS_BLOCK     0x00000400L    // Block the variation.
#define TLS_BREAK     0x00000800L    // Debugger break;
#define TLS_CALLTREE  0x00000040L    // Log call-tree (function tracking).
#define TLS_SYSTEM    0x00000080L    // Log System debug.
#define TLS_TESTDEBUG 0x00001000L    // Debug level.
#define TLS_TEST      0x00000100L    // Log Test information (user).
#define TLS_VARIATION 0x00000200L    // Log testcase level.

#define TLS_REFRESH   0x00010000L    // Create new file || trunc to zero.
#define TLS_SORT      0x00020000L    // Sort file output by instance.
#define TLS_DEBUG     0x00040000L    // Output to debug (com) monitor).
#define TLS_MONITOR   0x00080000L    // Output to 2nd screen.
#define TLS_PROLOG    0x00200000L    // Prolog line information.
#define TLS_WINDOW    0x00400000L    // Log to windows.
#define TLS_ACCESSON  0x00800000L    // Keep log-file open.
#define TLS_DIFFABLE  0x01000000L    // make log file windiff'able (no dates..)
#define TLS_NOHEADER  0x02000000L    // suppress headers so it is more diffable

#define TL_LOG       TLS_LOG      ,_T(__FILE__),(int)__LINE__
#define TL_ABORT     TLS_ABORT    ,_T(__FILE__),(int)__LINE__
#define TL_SEV1      TLS_SEV1     ,_T(__FILE__),(int)__LINE__
#define TL_SEV2      TLS_SEV2     ,_T(__FILE__),(int)__LINE__
#define TL_SEV3      TLS_SEV3     ,_T(__FILE__),(int)__LINE__
#define TL_WARN      TLS_WARN     ,_T(__FILE__),(int)__LINE__
#define TL_PASS      TLS_PASS     ,_T(__FILE__),(int)__LINE__
#define TL_BLOCK     TLS_BLOCK    ,_T(__FILE__),(int)__LINE__
#define TL_INFO      TLS_INFO     ,_T(__FILE__),(int)__LINE__
#define TL_BREAK     TLS_BREAK    ,_T(__FILE__),(int)__LINE__
#define TL_CALLTREE  TLS_CALLTREE ,_T(__FILE__),(int)__LINE__
#define TL_SYSTEM    TLS_SYSTEM   ,_T(__FILE__),(int)__LINE__
#define TL_TESTDEBUG TLS_TESTDEBUG,_T(__FILE__),(int)__LINE__
#define TL_TEST      TLS_TEST     ,_T(__FILE__),(int)__LINE__
#define TL_VARIATION TLS_VARIATION,_T(__FILE__),(int)__LINE__

//////////////////////////////////////////////////////////////////////////
//
//
//

class CLog
{
protected:
    struct CResults
    {
        UINT   nTotal;
        UINT   nPassed;
        UINT   nWarned;
        UINT   nFailedSev3;
        UINT   nFailedSev2;
        UINT   nFailedSev1;
        UINT   nBlocked;
        UINT   nAborted;
        DWORD  dwStartTime;

        CResults()
        {
            ZeroMemory(this, sizeof(*this));
            dwStartTime = GetTickCount();
        }

        void Update(DWORD dwLogLevel)
        {
            if (dwLogLevel & TLS_PASS) ++nPassed;
            if (dwLogLevel & TLS_WARN) ++nWarned;
            if (dwLogLevel & TLS_SEV3) ++nFailedSev3;
            if (dwLogLevel & TLS_SEV2) ++nFailedSev2;
            if (dwLogLevel & TLS_SEV1) ++nFailedSev1;
            if (dwLogLevel & TLS_BLOCK) ++nBlocked;
            if (dwLogLevel & TLS_ABORT) ++nAborted;
        }

        PCTSTR Report(PTSTR pBuffer, SIZE_T nBufferLen) const
        {
            DWORD dwUpTime = GetTickCount() - dwStartTime;

            _sntprintf(
                pBuffer, 
                nBufferLen, 
                _T("%d total, ")
                _T("%d passed, ")
                _T("%d warned, ")
                _T("%d failed sev1, ")
                _T("%d failed sev2, ")
                _T("%d failed sev3, ")
                _T("%d blocked, ")
                _T("%d aborted tests in ")
                _T("%d hours, ")
                _T("%d mins, ")
                _T("%d secs"),
                nTotal,
                nPassed,
                nWarned,
                nFailedSev3,
                nFailedSev2,
                nFailedSev1,
                nBlocked,
                nAborted,
                dwUpTime / (1000 * 60 * 60),
                (dwUpTime / (1000 * 60)) % 60,
                (dwUpTime / 1000) % 60
            );

            return pBuffer;
        }

        PCTSTR Summary() const
        {
            if (nAborted)
            {
                return _T("TIMEOUT");
            }

            if (nBlocked)
            {
                return _T("NOCONFIG");
            }

            if (nFailedSev3 || nFailedSev2 || nFailedSev1)
            {
                return _T("FAIL");
            }

            return _T("PASS");
        }
    };

public:
	CLog()
	{
        m_pLogWindow = 0;
        m_bMute      = false;
	}

    virtual ~CLog()
    {
    }

    VOID
    SetLogWindow(
        CLogWindow *pLogWindow
    )
    {
        m_pLogWindow = pLogWindow;
    }

    VOID
    Mute(bool bMute = true)
    {
        m_bMute = bMute;
    }

	PCTSTR
    __cdecl
	Log(
		DWORD  dwLogLevel,
		PCTSTR pFile,
		int    nLine,
		PCTSTR pFormat,
        ...
	)
	{
        va_list arglist;
        va_start(arglist, pFormat);

        return LogV(dwLogLevel, pFile, nLine, pFormat, arglist);
	}

    PCTSTR 
	LogV(
		DWORD   dwLogLevel,
		PCTSTR  pFile,
		int     nLine,
		PCTSTR  pFormat,
        va_list arglist
	)
	{
        CCppMem<TCHAR> pszStr(bufvprintf(pFormat, arglist));

        PCTSTR pszComment = _T("");

        if (!m_bMute) 
        {
            if (dwLogLevel & TLS_TEST)
            {
                m_TestResults.Update(dwLogLevel);
            }

            if (dwLogLevel & TLS_VARIATION)
            {
                if (dwLogLevel & (TLS_WARN | TLS_SEV1 | TLS_SEV2 | TLS_SEV3 | TLS_ABORT))
                {
                    DWORD &rResult = m_ThreadResult[GetCurrentThreadId()];

                    if (rResult < dwLogLevel)
                    {
                        rResult = dwLogLevel;
                    }
                }
            }

		    if (dwLogLevel & TLS_NOCONSOLE) 
            {
                dwLogLevel &= ~TLS_NOCONSOLE;
            } 
            else 
            {
                if (dwLogLevel & TLS_DEBUG)
                {
                    if (pFile)
                    {
                        OutputDebugString(pFile);
                        OutputDebugString(_T(" ")); 
                    }

                    OutputDebugString(pszStr);
                    OutputDebugString(_T("\n")); //bugbug: talk about inefficiency...

                    dwLogLevel &= ~TLS_DEBUG;
                }

                if (m_pLogWindow) 
                {
                    TCHAR Buffer[1024];

                    pszComment = m_pLogWindow->Log(
                        dwLogLevel, 
                        pFile, 
                        nLine, 
                        pszStr,
                        m_VarResults.Report(Buffer, COUNTOF(Buffer))
                    );
                } 
                else 
                {
                    _ftprintf(
                        dwLogLevel & TLS_INFO ? stdout : stderr, 
                        _T("%s\n"), 
                        pszStr
                    );
                }
		    }

	        LogStr(
		        dwLogLevel,
		        pFile,
		        nLine,
		        pszStr,
		        pszComment
	        );
        }

        return pszComment;
	}

    virtual
    VOID   
	LogStr(
		DWORD  dwLogLevel,
		PCTSTR pFile,
		int    nLine,
		PCTSTR pStr,
		PCTSTR pComment
	)
    {
    }

	virtual
    VOID 
	StartVariation()
	{
        ++m_VarResults.nTotal;
        m_ThreadResult[GetCurrentThreadId()] = TLS_PASS;
	}

	virtual
	DWORD 
	EndVariation()
	{
        DWORD dwResult = m_ThreadResult[GetCurrentThreadId()];
        m_VarResults.Update(dwResult);
		return dwResult;
	}

    virtual
    VOID
    InitThread()
    {
    }

    virtual
    VOID
    DoneThread()
    {
    }

    virtual
	VOID
	StartBlock(PCTSTR pBlockName, BOOL bStamp = TRUE)
    {
    }

    virtual
	VOID
	EndBlock(PCTSTR pBlockName, BOOL bStamp = TRUE)
    {
    }

private:
    CLogWindow  *m_pLogWindow;
    bool         m_bMute;
    
protected:
    CResults     m_TestResults;
    CResults     m_VarResults;

    std::map<DWORD, DWORD> m_ThreadResult;
};

//////////////////////////////////////////////////////////////////////////
//
//
//

class CNtLog : public CHandle<HANDLE, CNtLog>, public CLog
{
	typedef CHandle<HANDLE, CNtLog> handle_type;

public:
    CNtLog()
    {
    }

	CNtLog(
		PCTSTR     pszLogFile, 
		DWORD      dwLogInfo,
		LPSECURITY_ATTRIBUTES lpSecAttrs = 0
    ) :
        handle_type((*m_Dll.CreateLogEx)(
		    pszLogFile, 
		    dwLogInfo, 
		    lpSecAttrs
		))
	{
		InitThread();
	}

	void Destroy()
	{
		(*m_Dll.ReportStats)(*this);
		(*m_Dll.RemoveParticipant)(*this);
		(*m_Dll.DestroyLog)(*this);
	}

	bool IsValid()
	{
		return (HANDLE) *this != INVALID_HANDLE_VALUE;
	}

    VOID
    InitThread()
    {
        AddParticipant();
    }

    VOID
    DoneThread()
    {
        RemoveParticipant();
    }

	VOID
	AddParticipant(
		DWORD dwLevels = 0,
		int nMachineID = 0
	) const
	{
	    (*m_Dll.AddParticipant)(
			*this, 
			dwLevels, 
			nMachineID
		);
	}

	VOID
	RemoveParticipant() const
	{
        (*m_Dll.RemoveParticipant)(*this);
	}

	static
	DWORD  
	ParseCmdLine(
		PCTSTR pszCmdLine
	)
	{
        return (*m_Dll.ParseCmdLine)(pszCmdLine);
	}

	int    
	GetLogFileName(
		PTSTR pszFileName
	) const
	{
		return (*m_Dll.GetLogFileName)(*this, pszFileName);
	}

	VOID    
	SetLogFileName(
		PTSTR pszFileName
	) const
	{
		(*m_Dll.SetLogFileName)(*this, pszFileName);
	}

	DWORD  
	GetLogInfo() const
	{
		return (*m_Dll.GetLogInfo)(*this);
	}

	DWORD  
	SetLogInfo(
		DWORD dwInfo
	) const
	{
		return (*m_Dll.SetLogInfo)(*this, dwInfo);
	}

	VOID
	PromptLog(
		HWND hWnd = 0
	) const
	{
		(*m_Dll.PromptLog)(hWnd, *this);
	}

	int
	GetTestStat(
		DWORD dwLevel
	) const
	{
		return (*m_Dll.GetTestStat)(*this, dwLevel);
	}

	int
	GetVariationStat(
		DWORD dwLevel
	) const
	{
		return (*m_Dll.GetVariationStat)(*this, dwLevel);
	}

	VOID   
	ClearTestStats() const
	{
		(*m_Dll.ClearTestStats)(*this);
	}

	VOID   
	ClearVariationStats() const
	{
		(*m_Dll.ClearVariationStats)(*this);
	}

	VOID
	StartVariation()
	{
        CLog::StartVariation();
		(*m_Dll.StartVariation)(*this);
	}

	DWORD  
	EndVariation()
	{
        CLog::EndVariation();
		return (*m_Dll.EndVariation)(*this);
	}

	VOID   
	ReportStats() const
	{
		(*m_Dll.ReportStats)(*this);
	}

    VOID   
	LogStr(
		DWORD  dwLogLevel,
		PCTSTR pFile,
		int    nLine,
		PCTSTR pStr,
		PCTSTR pComment
	)
	{
		(*m_Dll.Log)(
			*this, 
			dwLogLevel, 
			pFile, 
			nLine,
            pComment && *pComment ? _T("%s (%s)") : _T("%s"),
			pStr,
            pComment
		);
	}

private:
    class CNtLogDLL
	{
	public:
		CNtLogDLL()
		{
		    try 
            {
			    m_NtLog = CLibrary(_T("ntlog.dll"));

			    CreateLog			 = CCreateLog          (m_NtLog, "tlCreateLog_"_AW);
			    CreateLogEx		     = CCreateLogEx        (m_NtLog, "tlCreateLogEx_"_AW);
			    ParseCmdLine		 = CParseCmdLine       (m_NtLog, "tlParseCmdLine_"_AW);
			    GetLogFileName		 = CGetLogFileName     (m_NtLog, "tlGetLogFileName_"_AW);
			    SetLogFileName		 = CSetLogFileName     (m_NtLog, "tlSetLogFileName_"_AW);
			    LogX				 = CLogX               (m_NtLog, "tlLogX_"_AW);
			    Log				     = CLog                (m_NtLog, "tlLog_"_AW);
			    DestroyLog			 = CDestroyLog         (m_NtLog, "tlDestroyLog");
			    AddParticipant		 = CAddParticipant     (m_NtLog, "tlAddParticipant");
			    RemoveParticipant	 = CRemoveParticipant  (m_NtLog, "tlRemoveParticipant");
			    GetLogInfo			 = CGetLogInfo         (m_NtLog, "tlGetLogInfo");
			    SetLogInfo			 = CSetLogInfo         (m_NtLog, "tlSetLogInfo");
			    PromptLog			 = CPromptLog          (m_NtLog, "tlPromptLog");
			    GetTestStat		     = CGetTestStat        (m_NtLog, "tlGetTestStat");
			    GetVariationStat	 = CGetVariationStat   (m_NtLog, "tlGetVariationStat");
			    ClearTestStats		 = CClearTestStats     (m_NtLog, "tlClearTestStats");
			    ClearVariationStats  = CClearVariationStats(m_NtLog, "tlClearVariationStats");
			    StartVariation		 = CStartVariation     (m_NtLog, "tlStartVariation");
			    EndVariation		 = CEndVariation       (m_NtLog, "tlEndVariation");
			    ReportStats		     = CReportStats        (m_NtLog, "tlReportStats");
		    } 
            catch (...) 
            {    
			    //_tprintf(_T("*** Cannot load NTLOG.DLL, no log file will be generated ***\n"));
		    }
        }

	private:
        CLibrary m_NtLog;

	public:
        DECL_CWINAPI(HANDLE, APIENTRY, CreateLog, (LPCTSTR, DWORD));
        DECL_CWINAPI(HANDLE, APIENTRY, CreateLogEx, (LPCTSTR, DWORD, LPSECURITY_ATTRIBUTES));
        DECL_CWINAPI(DWORD,  APIENTRY, ParseCmdLine, (LPCTSTR));
        DECL_CWINAPI(int,    APIENTRY, GetLogFileName, (HANDLE, LPTSTR));
        DECL_CWINAPI(BOOL,   APIENTRY, SetLogFileName, (HANDLE, LPCTSTR));
        DECL_CWINAPI(BOOL,   APIENTRY, LogX, (HANDLE, DWORD, LPCTSTR, int, LPCTSTR));
        DECL_CWINAPI(BOOL,   __cdecl,  Log, (HANDLE, DWORD, LPCTSTR, int, LPCTSTR, ...));
        DECL_CWINAPI(BOOL,   APIENTRY, DestroyLog, (HANDLE));
        DECL_CWINAPI(BOOL,   APIENTRY, AddParticipant, (HANDLE, DWORD, int));
        DECL_CWINAPI(BOOL,   APIENTRY, RemoveParticipant, (HANDLE));
        DECL_CWINAPI(DWORD,  APIENTRY, GetLogInfo, (HANDLE));
        DECL_CWINAPI(DWORD,  APIENTRY, SetLogInfo, (HANDLE, DWORD));
        DECL_CWINAPI(HANDLE, APIENTRY, PromptLog, (HWND, HANDLE));
        DECL_CWINAPI(int,    APIENTRY, GetTestStat, (HANDLE, DWORD));
        DECL_CWINAPI(int,    APIENTRY, GetVariationStat, (HANDLE, DWORD));
        DECL_CWINAPI(BOOL,   APIENTRY, ClearTestStats, (HANDLE));
        DECL_CWINAPI(BOOL,   APIENTRY, ClearVariationStats, (HANDLE));
        DECL_CWINAPI(BOOL,   APIENTRY, StartVariation, (HANDLE));
        DECL_CWINAPI(DWORD,  APIENTRY, EndVariation, (HANDLE));
        DECL_CWINAPI(BOOL,   APIENTRY, ReportStats, (HANDLE));
	};

private:
	static CNtLogDLL m_Dll;
};

//////////////////////////////////////////////////////////////////////////
//
//
//

// Invalid Log handle define
#define INVALID_LOGHANDLE			-1

// Log file and debug output flags
#define NO_LOG			0x000000
#define STAT_LOG		0x000001	// Local logging operation
#define SUM_LOG			0x000002
#define	ERR_LOG			0x000004
#define DBG_LOG			0x000008
#define SVR_STAT_LOG	0x010000
#define SVR_SUM_LOG		0x020000	// Lormaster Server logging operation
#define	SVR_ERR_LOG		0x040000
#define SVR_DBG_LOG		0x080000
#define SVR_SQL_LOG		0x100000	// Perform SQL logging

// LogFile Creation flags
#define LOG_NEW			0x001
#define LOG_APPEND		0x002
#define LOG_NOCREATE	0x004

// Test result type
#define RESULT_INFO		0
#define RESULT_TESTPASS	1
#define RESULT_TESTFAIL	2
#define RESULT_TESTNA	3
#define RESULT_SUMMARY	4
#define RESULT_DEBUG	5
#define RESULT_ERRINFO  6

//////////////////////////////////////////////////////////////////////////
//
//
//

class CLorLog : public CHandle<unsigned long, CLorLog>, public CLog
{
	typedef CHandle<unsigned long, CLorLog> handle_type;

public:
    CLorLog()
    {
    }

	CLorLog(
		const char *pTest_Name,
		const char *pLog_Path,
		unsigned long dwCreate_Flags,
		unsigned long dwLog_Flags,
		unsigned long dwDebug_Flags
    ) :
        handle_type((*m_Dll.RegisterTest)(
		    pTest_Name,
		    pLog_Path,
		    dwCreate_Flags,
		    dwLog_Flags,
		    dwDebug_Flags
        ))
	{
	}

	CLorLog(
		const char *pTest_Name,
		const char *pLog_Path,
		DWORD Create_Flags,
		DWORD Log_Flags,
		DWORD Debug_Flags,
		DWORD dwProcessID
    ) :
        handle_type((*m_Dll.RegisterTest_ProcID)(
		    pTest_Name,
		    pLog_Path,
		    Create_Flags,
		    Log_Flags,
		    Debug_Flags,
            dwProcessID
        ))
	{
	}

	void Destroy()
	{
        (*m_Dll.UnRegisterTest)(*this);
	}

	bool IsValid()
	{
		return *this != INVALID_LOGHANDLE;
	}

    VOID   
	LogStr(
		DWORD  dwLogLevel,
		PCTSTR pFile,
		int    nLine,
		PCTSTR pStr,
		PCTSTR pComment
	)
    {
        DWORD dwType;

        if (dwLogLevel & TLS_INFO)
        {
            dwType = RESULT_INFO;
        }
        else if (dwLogLevel & (TLS_WARN | TLS_SEV1 | TLS_SEV2 | TLS_SEV3 | TLS_ABORT))
        {
            dwType = RESULT_ERRINFO;
        }
        else
        {
            dwType = RESULT_INFO;
        }

        USES_CONVERSION;

		(*m_Dll.TestResultEx)(
			*this, 
			dwType, 
            pComment && *pComment ? "%1: %2!d!: %3 (%4)" : "%1: %2!d!: %3",
			T2A(pFile), 
			nLine,
			T2A(pStr),
            T2A(pComment)
		);
	}

private:
    class CLorLogDLL
	{
	public:
		CLorLogDLL()
		{
		    try 
            {
			    m_LorLog = CLibrary(_T("loglog32.dll"));

			    RegisterTest    	 = CRegisterTest       (m_LorLog, "RegisterTest");
			    RegisterTest_ProcID  = CRegisterTest_ProcID(m_LorLog, "RegisterTest_ProcID");
			    TestResult           = CTestResult         (m_LorLog, "TestResult");
			    TestResultEx         = CTestResultEx       (m_LorLog, "TestResultEx");
			    UnRegisterTest       = CUnRegisterTest     (m_LorLog, "UnRegisterTest");
			    QueryTestResults     = CQueryTestResults   (m_LorLog, "QueryTestResults");
		    } 
            catch (...) 
            {    
			    //_tprintf(_T("*** Cannot load LORLOG32.DLL, no log file will be generated ***\n"));
		    }
        }

	private:
        CLibrary m_LorLog;

	public:
        DECL_CWINAPI(unsigned long, WINAPI,  RegisterTest, (const char *, const char *, unsigned long, unsigned long, unsigned long));
        DECL_CWINAPI(unsigned long, WINAPI,  RegisterTest_ProcID, (const char *, const char *, DWORD, DWORD, DWORD, DWORD));
        DECL_CWINAPI(int,           WINAPI,  TestResult, (unsigned long, unsigned long, const char *));
        DECL_CWINAPI(int,           __cdecl, TestResultEx, (unsigned long, unsigned long, const char *, ...));
        DECL_CWINAPI(int,           WINAPI,  UnRegisterTest, (unsigned long));
        DECL_CWINAPI(int,           WINAPI,  QueryTestResults, (unsigned long, unsigned long *, unsigned long *, unsigned long *));
	};

private:
	static CLorLogDLL m_Dll;
};

//////////////////////////////////////////////////////////////////////////
//
//
//

#define TIME_TO_STR_FORMAT              \
    _T("%d/%d/%02d %d:%02d:%02d %cM")

#define TIME_TO_STR_ARGS(st)            \
    st.wMonth,                          \
    st.wDay,                            \
    st.wYear % 100,                     \
    st.wHour % 12,                      \
    st.wMinute,                         \
    st.wSecond,                         \
    st.wHour / 12 ? _T('P') : _T('A')   \

//////////////////////////////////////////////////////////////////////////
//
//
//

class CBvtLog : public CLog
{
public:
    struct COwners
    {
        PCTSTR pTestName;
        PCTSTR pContactName;
        PCTSTR pMgrName;
        PCTSTR pDevPrimeName;
        PCTSTR pDevAltName;
        PCTSTR pTestPrimeName;
        PCTSTR pTestAltName;
    };

public:
    CBvtLog()
    {
    }

	CBvtLog(
        COwners *pOwners,
        PCTSTR   filename, 
        PCTSTR   mode = _T("at"),
        int      shflag = _SH_DENYWR
    ) :
        m_nIntent(0),
        m_nBlock(0),
        m_pOwners(pOwners),
        m_LogFile(filename, mode, shflag)
	{
        setvbuf(m_LogFile, 0, _IONBF, 0);

        GetLocalTime(&m_StartTime);

        StartBlock(_T("TESTRESULT"), FALSE);
	}

    ~CBvtLog()
    {
        GetLocalTime(&m_EndTime);

        ReportStats();

        EndBlock(_T("TESTRESULT"), FALSE);
    }

	VOID   
	ReportStats() const
    {
        _ftprintf(
            m_LogFile,
            _T("\n")
            _T("%*sTEST:         %s\n")
            _T("%*sRESULT:       %s\n")
            _T("%*sBUILD:        %d\n")
            _T("%*sMACHINE:      %s\n")
            _T("%*sCONTACT:      %s\n")
            _T("%*sMGR CONTACT:  %s\n")
            _T("%*sDEV PRIME:    %s\n")
            _T("%*sDEV ALT:      %s\n")
            _T("%*sTEST PRIME:   %s\n")
            _T("%*sTEST ALT:     %s\n")
            _T("%*sSTART TIME:   ") TIME_TO_STR_FORMAT _T("\n")
            _T("%*sEND TIME:     ") TIME_TO_STR_FORMAT _T("\n")
            _T("\n"),
            m_nIntent, _T(""), m_pOwners->pTestName,
            m_nIntent, _T(""), m_VarResults.Summary(), //bugbug
            m_nIntent, _T(""), COSVersionInfo().dwBuildNumber,
            m_nIntent, _T(""), (PCTSTR) CComputerName(TRUE),
            m_nIntent, _T(""), m_pOwners->pContactName,
            m_nIntent, _T(""), m_pOwners->pMgrName,
            m_nIntent, _T(""), m_pOwners->pDevPrimeName,
            m_nIntent, _T(""), m_pOwners->pDevAltName,
            m_nIntent, _T(""), m_pOwners->pTestPrimeName,
            m_nIntent, _T(""), m_pOwners->pTestAltName,
            m_nIntent, _T(""), TIME_TO_STR_ARGS(m_StartTime),
            m_nIntent, _T(""), TIME_TO_STR_ARGS(m_EndTime)
        );
	}

    VOID   
	LogStr(
		DWORD  dwLogLevel,
		PCTSTR pFile,
		int    nLine,
		PCTSTR pStr,
		PCTSTR pComment
	) 
    {
        PCTSTR pszText;
        int    iImage;
        
        CLogWindow::FindNtLogLevel(dwLogLevel, &pszText, &iImage);

        _ftprintf(
            m_LogFile,
            pComment && *pComment ? _T("%*s%s: %s (%s)\n") : _T("%*s%s: %s\n"),
            m_nIntent, _T(""),
            pszText,
			pStr,
            pComment
		);
	}

	VOID
	StartBlock(PCTSTR pBlockName, BOOL bStamp /*= TRUE*/)
	{
        SYSTEMTIME st;
        GetLocalTime(&st);

        _ftprintf(
            m_LogFile, 
            bStamp ? _T("%*s[%s %d - ") TIME_TO_STR_FORMAT _T("]\n") : _T("%*s[%s]\n"),
            m_nIntent, _T(""), pBlockName,
            InterlockedIncrement(&m_nBlock),
            TIME_TO_STR_ARGS(st)
        );
        
        m_nIntent += 4;
    }

	VOID
	EndBlock(PCTSTR pBlockName, BOOL bStamp /*= TRUE*/)
	{
        m_nIntent -= 4;

        SYSTEMTIME st;
        GetLocalTime(&st);

        _ftprintf(
            m_LogFile, 
            bStamp ? _T("%*s[/%s - ") TIME_TO_STR_FORMAT _T("]\n\n") : _T("%*s[/%s]\n\n"),
            m_nIntent, _T(""), pBlockName,
            TIME_TO_STR_ARGS(st)
        );
    }

private:
public:
    COwners   *m_pOwners;

	CCFile     m_LogFile;
    
    SYSTEMTIME m_StartTime;
    SYSTEMTIME m_EndTime;

    int        m_nIntent; //bugbug

    LONG       m_nBlock;
};


//////////////////////////////////////////////////////////////////////////
//
//
//

extern CCppMem<CLog> g_pLog;

//////////////////////////////////////////////////////////////////////////
//
//
//

template <DWORD dwLogLevel> 
struct CLogHelper
{
	PCTSTR
    __cdecl
	operator ()(
		PCTSTR pFormat,
        ...
	)
	{
        va_list arglist;
        va_start(arglist, pFormat);

		return g_pLog->LogV(dwLogLevel, _T(""), 0, pFormat, arglist);
	}
};

#endif LOGWRAPPERS_H