//
//  Copyright © 2003, 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:   
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      
//

#if !defined(AFX_7KSONARIF_H__8F1818D5_540C_4E4D_BE9D_C7C964DDBA4B__INCLUDED_)
#define AFX_7KSONARIF_H__8F1818D5_540C_4E4D_BE9D_C7C964DDBA4B__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "..\..\..\Utils\NetUtils\Critical.h"
#include "..\..\..\Utils\NetUtils\BaseThread.h"

///////////////////////////////////////////////////////////////////////////////
// C7kSonarIF class definition.

#pragma warning( push, 3 )                                                          // Microsoft's STL is dirty so suppress necessary warnings.
#pragma warning( disable : 4018 )
#include <list>
#include <algorithm>
#pragma warning( default : 4018 )
#pragma warning( pop )

template <typename tSonarInterface>
class C7kSonarIF : public tSonarInterface, protected CBaseThread                    // Threaded sonar interface object to handle data IO.
{
public:

    ///////////////
    // Definitions.

    enum ETHREADEVENT                                                               // Significant thread events.
    {
        threadEventTerminate                    = 0,
        threadEventInBoundDataAvailable,                                            // Inbound data is available.

        // ... add new enumerations here.

        threadEventMaxEvents                                                        // Keep as the last item to ensure an accurate count.
    };

    ///////////////
    // Services.

                            C7kSonarIF              (   void );
    virtual                ~C7kSonarIF              (   void );

    bool                    Connect                 (   void );

    bool                    Disconnect              (   void );

protected:

    ///////////////
    // Services.
    
    // Overrides from CBaseThread base class.

    bool                    InitializeThreadObjects (   void );
    bool                    TerminateThreadObjects  (   void );
    void                    WatchCycle              (   void );
    BOOL                    ProcessMessage          (   MSG                *psMsg );

    // Overrides from bases class(es).

    bool                    Handle7kRecordFromSonar (   const BYTE                 *pby7kRecord,
                                                        const unsigned long        &rulBytes,
                                                        const unsigned long        &rulTimeStamp );
private:

    ///////////////
    // Attributes.

    const unsigned long                             m_ulThreadTerminateTimeout;
    const unsigned long                             m_ulMaxInBoundBufferSize;

    BYTE                                           *m_pbyInBoundBytes;
    unsigned long                                   m_ulBytesInRecord;
    HANDLE                                          m_hWaitForConnection;
    HANDLE                                          m_hTerminateEvent;

    ///////////////
    // Services.

    void                    ProcessEvents           (   const HANDLE               *phEvents,
                                                        const DWORD                &rdwEventIndex );

    bool                    ValidateEventList       (   const HANDLE               *phEventList,
                                                        const DWORD                &rdwEventCount )                 const;

    bool                    WaitForConnection       (   void );

    // Standard services that are not implemented.

                            C7kSonarIF              (   const C7kSonarIF           &rRhs );         // Not implemented thus private to prevent use.
    C7kSonarIF &            operator =              (   const C7kSonarIF           &rRhs );         // Not implemented thus private to prevent use.

};

//////////////////////////////////////////////////////////////////////
// C7kSonarIF class implementation.

#pragma CompileMessage_m( "TODO: REINSTATE FINITE TIMEOUT PROCESSING ONCE DEBUGGED (1000ms?)" )

template <typename tSonarInterface>
C7kSonarIF<tSonarInterface>::C7kSonarIF         ( void )

                     :tSonarInterface           (),                                 // Explicitly invoke the default base-class constructor.

                      CBaseThread               (   INFINITE, //1000UL,             // Timeout check period in milliseconds; set to INFINITE to disable.
                                                    THREAD_PRIORITY_ABOVE_NORMAL ), //THREAD_PRIORITY_NORMAL ),       // Normal thread priority, for now.

                      m_ulThreadTerminateTimeout(   4000 ),                         // Wait upto 4s before terminating the worker thread, once signalled.
                      m_ulMaxInBoundBufferSize  (   8UL * 1024UL * 1024UL )         // Max 7k record size is 8MBytes.
{
    // Internal buffering support members.

    __TRY
    {
        m_pbyInBoundBytes = NULL;
        m_ulBytesInRecord = 0UL;

        if ( ( m_hTerminateEvent = CreateEvent( NULL, TRUE, FALSE, NULL ) ) == NULL )
        {
            TRACE( _T( "C7kSonarIF<tSonarInterface>::C7kSonarIF(), CreateEvent() failed.\n" ) );
        }

        if ( ( m_pbyInBoundBytes = new BYTE [ m_ulMaxInBoundBufferSize ] ) == NULL )
        {
            TRACE( _T( "C7kSonarIF::C7kSonarIF(), Can't allocate inbound data buffer.\n" ) );
        }

        if ( ( m_hWaitForConnection = ::CreateEvent( NULL, TRUE, FALSE, NULL ) ) == NULL )            // Manual reset event initially not signalled.
        {
            TRACE( _T( "C7kSonarIF::C7kSonarIF(), Can't create a connection event\n" ) );
        }
    }
    __FINALLY
    {
        if ( ResumeThread() == m_dwResumeFailCode )
        {
            TRACE( _T( "C7kSonarIF::C7kSonarIF(), ResumeThread() failed.\n" ) );
        }
    }
    __ENDFINALLY
}

template <typename tSonarInterface>
C7kSonarIF<tSonarInterface>::~C7kSonarIF( void )
{
    __TRY
    {
        if ( m_hWaitForConnection != NULL )
        {
            CloseHandle( m_hWaitForConnection );
            m_hWaitForConnection = NULL;
        }

        // Signal the worker thread to self-terminate but force the issue after the specified
        // timeout period if necessary.

        if ( m_hTerminateEvent != NULL )
        {
            SetEvent( m_hTerminateEvent );
        }

        if ( ! CBaseThread::WaitForThreadTermination( m_ulThreadTerminateTimeout ) )
        {
            TRACE( _T( "C7kSonarIF::~C7kSonarIF(), WaitForThreadToTerminate() timed out or failed\n." ) );
        }

        // Ensure we have disconnected but client of this class may have already done so and the base class
        // should be prepared to handle this scenario.

        if ( m_hTerminateEvent != NULL )
        {
            CloseHandle( m_hTerminateEvent );
            m_hTerminateEvent = NULL;
        }

        if ( ! Disconnect() )
        {
            TRACE( _T( "C7kSonarIF::~C7kSonarIF, Disconnect() returned false\n" ) );
        }
    }
    __FINALLY
    {
        // Finally delete the inbound data buffer.

        if ( m_pbyInBoundBytes != NULL )
        {
            delete [] m_pbyInBoundBytes;
            m_pbyInBoundBytes = NULL;
        }
    }
    __ENDFINALLY
}

template <typename tSonarInterface>
bool C7kSonarIF<tSonarInterface>::Connect( void )
{
    bool bSuccess = tSonarInterface::Connect();

    if ( m_hWaitForConnection != NULL )
    {
        SetEvent( m_hWaitForConnection );
    }

    return bSuccess;
}

template <typename tSonarInterface>
bool C7kSonarIF<tSonarInterface>::Disconnect( void )
{
    return tSonarInterface::Disconnect();
}

//////////////////////////////////////////////////////////////////////
// Private or overridden protected services.

template <typename tSonarInterface>
inline bool C7kSonarIF<tSonarInterface>::InitializeThreadObjects( void )
{
    // Add thread specific initialization here as needed.

    return true;
}

template <typename tSonarInterface>
inline bool C7kSonarIF<tSonarInterface>::TerminateThreadObjects( void )
{
    // Add thread specific clean up here as needed.

    return true;
}

template <typename tSonarInterface>
void C7kSonarIF<tSonarInterface>::WatchCycle( void )
{
    try
    {
        // Create and initialize various thread objects first off.

        if ( ! InitializeThreadObjects() )
        {
            ThrowMessage_m( "InitializeThreadObjects() failed." );
        }

        if ( ! WaitForConnection() )
        {
            ThrowMessage_m( "WaitForConnection() failed." );
        }

        // Next, setup our waitable kernel object handle array.

        HANDLE  ahEventList[] = {
                                    m_hTerminateEvent,
                                    static_cast<HANDLE>( *static_cast<tSonarInterface *>( this ) )

                                    // ... add new handles here and ids to ETHREADEVENT enum at top of this file.

                                };

        const DWORD dwEventCount  = static_cast<DWORD>( DimOf_m( ahEventList ) );

        if ( ! ValidateEventList( &ahEventList[ 0 ], dwEventCount ) )
        {
            ThrowMessage_m( "ValidateEventList() failed" );
        }

        // Main thread processing loop. It loops until thread termination message 
        // signals termination by clearing the base class' run flag.

        DWORD dwSignalState;

        while ( IsActive() )
        {
            if ( m_pbyInBoundBytes == NULL )
            {
                TRACE( _T( "C7kSonarIF<tSonarInterface>::WatchCycle(), m_pbyInBoundBytes is NULL.\n" ) );
                ASSERT( false );
                break;
            }

            // Handle any available queued data in case we missed an event during the cycle.

            if ( ! HandleDataFromSonar( m_pbyInBoundBytes, m_ulBytesInRecord, m_ulMaxInBoundBufferSize, false ) )
            {
                TRACE( _T( "C7kSonarIF::WatchCycle(), HandleDataFromSonar() failed.\n" ) );
            }

            // Block the thread and wait for a significant event (i.e. a Win32 event or Windows message).

            dwSignalState = ::MsgWaitForMultipleObjects(  dwEventCount,           // Number of waitable kernel objects (events, timers etc) to wait for.
                                                          &ahEventList[ 0 ],      // 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) Win32 kernel object events.
            //  4) Timeout processing (not relevant here).

            try
            {
                if ( IsInactive() )
                {
                    break;
                }
                else if ( dwSignalState == ( WAIT_OBJECT_0 + dwEventCount ) )
                {
                    PumpMessages();
                }
                else if ( ( dwEventCount  >  0 )             &&
                          ( dwSignalState >= WAIT_OBJECT_0 ) &&
                          ( dwSignalState <= ( WAIT_OBJECT_0 + dwEventCount - 1 ) ) )
                {
                    ProcessEvents( &ahEventList[ 0 ], dwSignalState - WAIT_OBJECT_0 );
                }
                else if ( dwSignalState == WAIT_TIMEOUT )
                {
                    if ( ! HandleDataFromSonar( m_pbyInBoundBytes, m_ulBytesInRecord, m_ulMaxInBoundBufferSize, false ) )
                    {
                        TRACE( _T( "C7kSonarIF::WatchCycle(), HandleDataFromSonar() failed.\n" ) );
                    }
                }
                else
                {
                    // Must be an abandoned mutex... 

                    TRACE( _T( "C7kSonarIF::WatchCycle(), Abandoned mutex !\n" ) );
                    ASSERT( false );
                }
            }
            catch ( ... )
            {
                TRACE( _T( "C7kSonarIF::WatchCycle(), Exception trying to handle thread event" ) );
            }
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        TRACE( _T( "C7kSonarIF<tSonarInterface>::WatchCycle(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        TRACE( _T( "C7kSonarIF<tSonarInterface>::WatchCycle(), Unspecified exception caught.\n" ) );
    }

    // Ensure we always attempt thread object termination.

    if ( ! TerminateThreadObjects() )
    {
        TRACE( _T( "C7kSonarIF::WatchCycle(), TerminateThreadObjects() failed" ) );
        ASSERT( false );
    }
    
    TRACE( _T( "C7kSonarIF::WatchCycle(), Thread is terminating !\n" ) );
}

template <typename tSonarInterface>
BOOL C7kSonarIF<tSonarInterface>::ProcessMessage( MSG *psMsg )
{ 
    // No significant messages to deal with; defer to the base class handler.

    return CBaseThread::ProcessMessage( psMsg );
}

template <typename tSonarInterface>
inline void C7kSonarIF<tSonarInterface>::ProcessEvents( const HANDLE   *phEvents,
                                                        const DWORD    &rdwEventIndex )
{
    try
    {
        // Process the event or other waitable kernel object.

        const DWORD dwMaxEvents = static_cast<DWORD>( threadEventMaxEvents );

        ASSERT( rdwEventIndex < dwMaxEvents );
        ASSERT( phEvents[ rdwEventIndex ] != NULL );

        if ( ( rdwEventIndex < dwMaxEvents ) && ( phEvents[ rdwEventIndex ] != NULL ) )
        {
            switch ( rdwEventIndex )
            {
                case threadEventTerminate:

                    SetActive( FALSE );
                    break;
            
                case threadEventInBoundDataAvailable:

                    if ( ! HandleDataFromSonar( m_pbyInBoundBytes, m_ulBytesInRecord, m_ulMaxInBoundBufferSize, false ) )
                    {
                        TRACE( _T( "C7kSonarIF::ProcessEvents(), HandleDataFromSonar() failed\n" ) );
                    }

                    break;

                default:

                    TRACE( _T( "C7kSonarIF::ProcessEvents(), Signalled kernel object unhandled, index: %lu\n" ), rdwEventIndex );

                    break;
            }
        }
    }
    catch ( ... )
    {
        TRACE( _T( "C7kSonarIF::ProcessEvents(), Unspecified exception caught.\n" ) );
    }
}

template <typename tSonarInterface>
inline bool C7kSonarIF<tSonarInterface>::ValidateEventList( const HANDLE   *phEventList,
                                                            const DWORD    &rdwEventCount ) const
{
    bool bValid = false;        // Assume invalid for now.

    // Id count should match the number of handles in the array and no handle
    // should be NULL unless we're forcing rdwEventCount to zero for testing.

    if ( rdwEventCount == 0UL )
    {
        bValid = true;
    }
    else if ( rdwEventCount == static_cast<DWORD>( threadEventMaxEvents ) )
    {
        bValid = true;

        for ( DWORD dwEvent = 0UL; dwEvent < rdwEventCount; dwEvent++ )
        {
            if ( phEventList[ dwEvent ] == NULL )
            {
                bValid = false;
                TRACE( _T( "C7kSonarIF::ValidateEventList(), Handle at index %lu is NULL!\n" ), dwEvent );
            }
        }
    }
    else
    {
        bValid = false;
    }

    return bValid;
}

template <typename tSonarInterface>
inline bool C7kSonarIF<tSonarInterface>::Handle7kRecordFromSonar(   const BYTE             *pby7kRecord,
                                                                    const unsigned long    &rulBytes,
                                                                    const unsigned long    &rulTimeStamp )
{
    // For now, simply use the base class' handler to invoke the dispatcher's callback for routing to the relevant subsystem.

    return tSonarInterface::Handle7kRecordFromSonar( pby7kRecord, rulBytes, rulTimeStamp );
}

template <typename tSonarInterface>
inline bool C7kSonarIF<tSonarInterface>::WaitForConnection( void )
{
    if ( m_hWaitForConnection != NULL )
    {
        DWORD dwSignalState = ::MsgWaitForMultipleObjects(  1UL,                    // Number of waitable kernel objects (events, timers etc) to wait for.
                                                           &m_hWaitForConnection,   // Kernel object handle array.
                                                            FALSE,                  // Wait for any one of the specified event types.
                                                            INFINITE,               // 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).

        ResetEvent( m_hWaitForConnection );

        return ( dwSignalState != WAIT_TIMEOUT );
    }

    return false;
}

#endif // !defined(AFX_7KSONARIF_H__8F1818D5_540C_4E4D_BE9D_C7C964DDBA4B__INCLUDED_)
