//
//  Copyright © 2004, 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:   WatchSentinel.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      
//

#include "StdAfx.h"
#include "WatchSentinel.h"

//////////////////////////////////////////////////////////////////////
// CWatchSentinel class implementation.

CWatchSentinel::CWatchSentinel              (   const unsigned long    &rulDefaultPollPeriod )

               :CBaseThread                 (   INFINITE,                                           // Initial thread timeout processing period (ms).
                                                THREAD_PRIORITY_NORMAL ),                           // Normal thread priority, for now.

                m_ulPollPeriodMilliseconds  (   rulDefaultPollPeriod ),                             // Use the specified poll period; default is 10s.
                m_ulTerminatePeriod         (   1000 )                                              // 1s before we force termination.
{
    ManageDllState_m();

    if ( ResumeThread() == m_dwResumeFailCode )
    {
        TRACE( _T( "CWatchSentinel::CWatchSentinel(), ResumeThread() failed.\n" ) );
    }
}

CWatchSentinel::~CWatchSentinel( void )
{
    ManageDllState_m();

    if ( ! CBaseThread::TerminateThread( m_ulTerminatePeriod ) )
    {
        TRACE( _T( "CWatchSentinel::CWatchSentinel(), ResumeThread() failed.\n" ) );
    }
}

bool CWatchSentinel::SetHealthCheckCallback(    PFN_STATUSCHECK    pfnStatusCheck,
                                                void              *pvParam )
{
    ManageDllState_m();

    if ( m_StatusChecker.Set( pfnStatusCheck, pvParam ) )
    {
        return ( PostThreadMessage( threadMessageCallbackInstalled ) != FALSE );
    }

    return false;
}

bool CWatchSentinel::ForceManualSensorPoll( void )
{
    ManageDllState_m();

    return ( PostThreadMessage( threadMessageForcePoll, static_cast<WPARAM>( -1 ) ) != FALSE );
}

void CWatchSentinel::WatchCycle( void )
{
    ManageDllState_m();

    __TRY
    {
        const   dwEventCount = 0UL;
        HANDLE  hEvents      = NULL;

        while ( IsActive() )
        {
            // Block the thread and wait for a significant event (i.e. a Win32 event or Windows message).

            DWORD dwSignalState = ::MsgWaitForMultipleObjects(    dwEventCount,             // Number of waitable kernel objects (events, timers etc) to wait for.
                                                                 &hEvents,                  // Kernel object handle array.
                                                                  FALSE,                    // Wait for any one of the specified event types.
                                                                  m_dwDwellPeriod,          // Timeout processing period in ms; specified in CBaseThread's constructor (see above).
                                                                  QS_ALLINPUT );            // Unblock on any message in the thread's queue (via normal message pump).

            // The following priority scheme is in effect:
            //
            //  1) Termination request.
            //  2) Queued thread messages.
            //  3) Timeout processing (not relevant here).

            try
            {
                if ( IsInactive() )
                {
                    break;
                }
                else if ( dwSignalState == ( WAIT_OBJECT_0 + dwEventCount ) )
                {
                    PumpMessages();
                }
                else if ( dwSignalState == WAIT_TIMEOUT )
                {
                    PollStatusCheck();
                }
                else if ( ( dwEventCount  >  0 )             &&
                          ( dwSignalState >= WAIT_OBJECT_0 ) &&
                          ( dwSignalState <= ( WAIT_OBJECT_0 + dwEventCount - 1 ) ) )
                {
                    ASSERT( false );                                                        // No events used presently...
                }
                else
                {
                    ASSERT( false );                                                        // Must be an abandoned mutex...
                }
            }
            catch ( ... )
            {
                TRACE( _T( "CWatchSentinel::WatchCycle(), Exception trying to handle thread event\n" ) );
            }
        }
    }
    __FINALLY
    {
    }
    __ENDFINALLY
    
    TRACE( _T( "CWatchSentinel::WatchCycle(), Thread is terminating !\n" ) );
}

BOOL CWatchSentinel::ProcessMessage( MSG *psMsg )
{
    ManageDllState_m();

    BOOL bMessageProcessed = FALSE;

    switch ( psMsg->message )
    {
        case threadMessageForcePoll:

            bMessageProcessed = TRUE;
            PollStatusCheck();
            break;
    
        case threadMessageCallbackInstalled:

            bMessageProcessed = TRUE;
            m_dwDwellPeriod   = m_ulPollPeriodMilliseconds;
            break;

        default:

            bMessageProcessed = CBaseThread::ProcessMessage( psMsg );
            break;
    }

    return bMessageProcessed;
}

inline
void CWatchSentinel::PollStatusCheck( void )
{
    ManageDllState_m();

    m_StatusChecker();
}

///////////////////////////////////////////////////////////////////////////////
// CWatchSentinel::CStatusCheck internal helper.

template <typename tCallback> const int CWatchSentinel::CStatusCheck<tCallback>::m_iSensorIndexUnspecified = -1;

template <typename tCallback>
inline CWatchSentinel::CStatusCheck<tCallback>::CStatusCheck( void )
{
    m_pfnCallback = NULL;
    m_pvParam     = NULL;
}

template <typename tCallback>
inline CWatchSentinel::CStatusCheck<tCallback>::~CStatusCheck(  void )
{
    m_pfnCallback = NULL;
    m_pvParam     = NULL;
}

template <typename tCallback>
inline bool CWatchSentinel::CStatusCheck<tCallback>::Set( tCallback pCallback, void * pvParam )
{
    m_pfnCallback = pCallback;
    m_pvParam     = pvParam;

    ASSERT( m_pfnCallback != NULL );

    return true;
}

template <typename tCallback>
inline void CWatchSentinel::CStatusCheck<tCallback>::operator() ( const int &riSensorIndex )
{
    try
    {
        if ( m_pfnCallback != NULL )
        {
            ( m_pfnCallback )( m_pvParam, riSensorIndex );
        }
    }
    catch ( ... )
    {
        TRACE( _T( "CWatchSentinel::CStatusCheck<tCallback>::operator(), Unspecified exception caught." ) );
    }
}

