//
//  Copyright © 2001 - 2002, RESON Inc. All Rights Reserved.
//
//  No part of this file may be reproduced or transmitted in any form or by
//  any means, electronic or mechanical, including photocopy, recording, or
//  information storage or retrieval system, without permission in writing
//  from RESON Inc.
//
//  Filename:   SurveyEventLog.cpp
//
//  Project:    6046.
//
//  Author(s):  W. Arcus
//
//  Purpose:    Implementation file for the CSurveyEventLog class.
//
//  Notes:
//

#include "StdAfx.h"
#include "SurveyEventLog.h"
#include "Alarm.h"
#include "RegistryKeys.h"

#include "Include\SurveyEventTypes.h"

#include "..\Utils\NetUtils\Critical.h"
#include "..\Utils\NetUtils\SystemTime.h"

#include <time.h>
#include <atlbase.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Internal module objects, constants etc.

namespace               // begin unnamed namespace.
{

    CCritical           g_Critical;

}                       // end unnamed namespace.

//////////////////////////////////////////////////////////////////////
// CSurveyEventLog class implementation.

using namespace N6046RegistryKeys;

CSurveyEventLog * CSurveyEventLog::m_pthis = NULL;

//////////////////////////////////////////////////////////////////////
// Public members.

CSurveyEventLog::CSurveyEventLog( void )
{
    m_hLogFile          = INVALID_HANDLE_VALUE;
    m_pthis             = this;
    m_bAllowDiagnostics = false;
    m_pAlarm            = NULL;
}

CSurveyEventLog::~CSurveyEventLog( void )
{
    __TRY
    {
        if ( m_hLogFile != INVALID_HANDLE_VALUE )
        {
            CloseHandle( m_hLogFile );
            m_hLogFile = INVALID_HANDLE_VALUE;
        }
    }
    __FINALLY
    {
        m_pthis             = NULL;
        m_bAllowDiagnostics = false;
        m_pAlarm            = false;
        m_pAlarm            = NULL;
        m_hLogFile          = INVALID_HANDLE_VALUE;
    }
    __ENDFINALLY
}

void CSurveyEventLog::SetAlarmCallback( const CAlarm *pAlarm )
{
    ASSERT( pAlarm != NULL );
    m_pAlarm = const_cast<CAlarm *>( pAlarm );
}

bool CSurveyEventLog::Start( LPCTSTR lpszPath )
{
    CString FilePath;

    ASSERT( lpszPath != NULL );

    FilePath.Format( "%s6046.log", lpszPath );

    if ( m_hLogFile == INVALID_HANDLE_VALUE )
    {
        m_hLogFile = ::CreateFile( FilePath,
                                    GENERIC_WRITE, 
                                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                                            NULL, 
                                                OPEN_ALWAYS, 
                                                    FILE_ATTRIBUTE_NORMAL,
                                                        NULL );

        if ( m_hLogFile != INVALID_HANDLE_VALUE )
        {
            ::SetFilePointer( m_hLogFile, 0, 0, FILE_END );
        }
    }

    m_bAllowDiagnostics = AreDiagnosticsEnabled();

    return ( m_hLogFile != INVALID_HANDLE_VALUE );
}

bool CSurveyEventLog::Stop( void )
{
    if ( m_hLogFile != INVALID_HANDLE_VALUE )
    {
        ::CloseHandle( m_hLogFile );
        m_hLogFile = INVALID_HANDLE_VALUE;
    }

    m_bAllowDiagnostics = false;

    return true;
}

int _cdecl CSurveyEventLog::ReportEvent(    int     iEventType,
                                            int     iAction,
                                            int     iId,
                                            char  * pszMsg, ... )
{
    int iStatus = 1;

    const ESURVEYEVENT eEventType  = static_cast<ESURVEYEVENT>( iEventType );
    const DWORD        dwTimestamp = CSystemTime::GetTickCount();

    ASSERT( m_pthis != NULL );

    if ( m_pthis == NULL )
    {
        TRACE( "CSurveyEventLog::ReportEvent(), static member's m_pthis pointer is NULL !\n" );
        iStatus = 0;
    }
    else
    {
        bool bReport = true;

        if ( eEventType == eventLogDiagnostic )
        {
            g_Critical.Enter();
            bReport = m_pthis->m_bAllowDiagnostics;
            g_Critical.Leave();
        }

        if ( bReport )
        {
            g_Critical.Enter();

            try
            {
                if ( ! CString( pszMsg ).IsEmpty() )
                {
                    CString     TimeString;
                    SYSTEMTIME  sSystemTime = { 0 };

                    if ( ! CSystemTime::TimeStampToSystemTime( dwTimestamp, &sSystemTime ) )
                    {
                        TRACE( _T( "CSurveyEventLog::ReportEvent(), CSystemTime::TimeStampToSystemTime() failed\n" ) );
                        ASSERT( false );
                    }

                    TimeString.Format( "%02u/%02u/%02u, %02u:%02u:%06.3f", sSystemTime.wYear,
                                                                           sSystemTime.wMonth,
                                                                           sSystemTime.wDay,
                                                                           sSystemTime.wHour,
                                                                           sSystemTime.wMinute,
                                                                           static_cast<float>( sSystemTime.wSecond ) + 
                                                                               0.001f * static_cast<float>( sSystemTime.wMilliseconds ) );
                    CString Message;
                    va_list pArg;

                    va_start( pArg, pszMsg );
                    Message.FormatV( pszMsg, pArg );
                    va_end( pArg );

                    // If it's an alarm, report it passing through the message an time stamp.

                    if ( eEventType == eventLogAlarm )
                    {
                        if ( ! m_pthis->ReportAlarm( iAction, iId, dwTimestamp, Message ) )
                        {
                            TRACE( _T( "CSurveyEventLog::ReportEvent(), Checking for alarm condition\n" ) );
                        }
                    }

                    // Prefix the time stamp.

                    CString FormattedMessage;

                    FormattedMessage.Format( "%s - %s\r\n", static_cast<LPCTSTR>( TimeString ), static_cast<LPCTSTR>( Message ) );

                    // Finally, emit the message to the debugger and disk log file.

                    TRACE( _T( "%s" ), static_cast<LPCTSTR>( FormattedMessage ) );

                    if ( ! m_pthis->WriteToLogFile( const_cast<char *>( static_cast<LPCTSTR>( FormattedMessage ) ) ) )
                    {
                        iStatus = 0;
                    }
                }
            }
            catch ( ... )
            {
                iStatus = 0;
                TRACE( _T( "CSurveyEventLog::ReportEvent(), Unspecified exception caught\n" ) );
            }

            g_Critical.Leave();
        }
    }

    return iStatus;
}

///////////////////////////////////////////////////////////////////////////////
// Private members.

inline
bool CSurveyEventLog::WriteToLogFile( char * pszMessage )
{
    bool  bStatus = false;                   // Assume failure for now.

    ASSERT( pszMessage != NULL );

    if ( pszMessage != NULL )
    {
        DWORD dwBytesToWrite = static_cast<DWORD>( ::strlen( pszMessage ) );

        if ( dwBytesToWrite > 0UL )
        {
            if ( m_hLogFile != INVALID_HANDLE_VALUE )
            {
                DWORD dwWritten = 0UL;

                bStatus = ( ::WriteFile( m_hLogFile, pszMessage, dwBytesToWrite, &dwWritten, NULL ) == TRUE );

                if ( ! bStatus )
                {
                    ASSERT( dwWritten == dwBytesToWrite );
                    TRACE( _T( "CSurveyEventLog::WriteToLogFile(), WriteFile() failed, code: %lu\n" ), GetLastError() );
                }

                ::FlushFileBuffers( m_hLogFile );
            }
        }
        else
        {
            bStatus = true;     // Nothing to do.
        }
    }

    return bStatus;
}

inline
bool CSurveyEventLog::AreDiagnosticsEnabled( void )
{
    CRegKey Key;
    bool    bDiagsAllowed   = false;
        
    Key.Attach( hKey6046_c );

    if ( Key.Open( hKey6046_c, lpsz6046RegistryKey_c ) == ERROR_SUCCESS )
    {
        DWORD dwValue = 0UL;

        if ( Key.QueryValue( dwValue, lpszDiagnosticsModeKey_c ) == ERROR_SUCCESS )
        {
            bDiagsAllowed = ( dwValue != 0UL );
        }

        Key.Close();
    }

    return bDiagsAllowed;
}

inline
bool CSurveyEventLog::ReportAlarm(  const int      &riEventAction,
                                    const int      &riEventId,
                                    const DWORD    &rdwTimestamp,
                                    const LPCTSTR   lpszMessage )
{
    // Check to see if the alarm object pointer is valid and, if so, execute the callback. The alarm 
    // object is reponsible for maintaining its own internal state and handing notification as applicable.

    bool bSuccess = false;

    try
    {
        if ( m_pAlarm != NULL )
        {
            bSuccess = m_pAlarm->Report( static_cast<EEVENTACTION>( riEventAction ),
                                            static_cast<EALARMID>( riEventId ),
                                                rdwTimestamp,
                                                    lpszMessage );
        }
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}
