//
//  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:   Dispatcher.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    CDispatcher class implementation file.
//
//  Notes:      
//

#include "StdAfx.h"
#include "Dispatcher.h"
#include "7kSharedMemoryInterface.h"
#include "7kNetworkInterface.h"
#include "7kSonarIF.h"
#include "7kSonarMessages.h"

#include "..\..\..\Utils\NetUtils\7kProtocol.h"

#pragma warning( push, 3 )                                                                          // Microsoft's STL is dirty so suppress necessary warnings.
#pragma warning( disable : 4018 )
#include <vector>
#include <algorithm>
#pragma warning( default : 4018 )
#pragma warning( pop )

#include <string.h>

///////////////////////////////////////////////////////////////////////////////
// Internal definitions etc.

namespace                                                                                           // Begin anonymous namespace to ensure internal linkage.
{

    typedef C7kSonarIF<C7kSharedMemoryInterface>    LocalSonar_t;                                   // Local:  uses the sonar's shared memory interface (memory mapped file).
    typedef C7kSonarIF<C7kNetworkInterface>         RemoteSonar_t;                                  // Remote: uses a TCP/IP connection (sonar is the socket server).


    const unsigned int                              uiMaxReceiveQueueLength_c   = 100;

}                                                                                                   // End anonymous namespace.

///////////////////////////////////////////////////////////////////////////////
// CDispatcher class implementation.

using namespace N7kSonarMessages;
using std::find;
using std::sort;

CDispatcher::CDispatcher( void )
{
    m_bIntialized = false;

    try
    {
        m_Subsystems.clear();
        m_Systems.clear();

        m_bIntialized = true;
    }
    catch ( ... )
    {
        m_bIntialized = false;
        TRACE ( _T( "CDispatcher::CDispatcher(), Unspecified exception caught\n" ) );
    }
}

CDispatcher::~CDispatcher( void )
{
    __TRY
    {
        m_SubsystemListGuard.Enter();
        m_SystemListGuard.Enter();

        DestroyAll();
    }
    __FINALLY
    {
        m_SystemListGuard.Leave();
        m_SubsystemListGuard.Leave();

        m_bIntialized = false;
    }
    __ENDFINALLY
}

bool CDispatcher::IsInitialized( void ) const
{
    return m_bIntialized;
}

bool CDispatcher::CreateSubsystem(  PFN_SUBSYSTEMCALLBACK   pfnCallback,
                                    void                   *pvParam,
                                    const int              &riSensorIndex,
                                    const unsigned long    &rulDeviceId,
                                    const unsigned short   &runSystemEnumerator,
                                    const EINTERFACETYPE   &reInterfaceType,
                                    const unsigned long    &rulPortOut,
                                    const unsigned long    &rulPortIn,
                                    const char             *pszAddress )
{
    bool bSuccess = false;          // Assume failure for now.

    try
    {
        // Register the subsystem if it's not already registered; remember, the sensor index must be unique.

        m_SubsystemListGuard.Enter();
        SubsystemListIterator_t pSubsystem  = m_Subsystems.end();
        bool                    bRegistered = IsRegistered( riSensorIndex, pSubsystem );
        m_SubsystemListGuard.Leave();

        // If it's not already in the subystem list then it must be new.

        if ( ! bRegistered )
        {
            // Create a temporary index object for comparison etc. Note: the interface index is unknown for now so use the default value for now.

            int              iSystemIndex = -1;
            CSubsystemIndex  SubsystemIndex( pfnCallback, pvParam, riSensorIndex, iSystemIndex, rulDeviceId, runSystemEnumerator );

            // Access both lists in thread save manner; catch any exceptions locally to ensure we leave the critical sections correctly.

            m_SubsystemListGuard.Enter();
            m_SystemListGuard.Enter();

            try
            {
                // Check the existing interface list to see whether we're to map to an existing interface or instantiate a new one.

                if ( IsNewInterface( SubsystemIndex, reInterfaceType ) )
                {
                    // A new sonar system (thus interface) is required thus get its descriptor and instantiate it.

                    void       *pvSonar = NULL;
                    C7kSystem   System;

                    if ( NewSystem( System, reInterfaceType, rulPortOut, rulPortIn, pszAddress ) )
                    {
                        switch ( reInterfaceType )
                        {
                            case interfaceTypeLocal:

                                {
                                    LocalSonar_t *pSonar = NULL;

                                    pvSonar = pSonar = new LocalSonar_t;

                                    if ( pSonar == NULL )
                                    {
                                        ThrowMessage_m( "Failed to construct sonar interface object" );
                                    }

                                    if ( ! pSonar->Connect() )
                                    {
                                        ThrowMessage_m( "Failed to set connect" );
                                    }
                                }

                                break;

                            case interfaceTypeRemote:

                                {
                                    RemoteSonar_t *pSonar = NULL;
                                    
                                    pvSonar = pSonar = new RemoteSonar_t;
                            
                                    if ( pSonar == NULL )
                                    {
                                        ThrowMessage_m( "Failed to construct sonar interface object" );
                                    }

                                    if ( ! pSonar->SetConnectionParameters( rulPortOut, rulPortIn, pszAddress ) )
                                    {
                                        ThrowMessage_m( "Failed to set connection parameters" );
                                    }

                                    if ( ! pSonar->Connect() )
                                    {
                                        ThrowMessage_m( "Failed to set connect" );
                                    }
                                }

                                break;

                            default:

                                TRACE( _T( "CDispatcher::CreateSubsystem(), Unrecognised interface type\n" ) );
                                break;
                        }

                        ASSERT( pvSonar != NULL );

                        if ( pvSonar != NULL )
                        {
                            // Update the interface index.

                            iSystemIndex = System.SystemIndex();

                            // Add the system to the system list then set up its callback.

                            m_Systems.push_back( System );

                            C7kSystem &rSystem = m_Systems.back();

                            rSystem.Parent( this );
                            rSystem.Add( pvSonar );
                        }
                    }

                    // Maintain the system list in sorted order (determined by C7kSystem::operator <() ).

                    SortInterfaceList();
                }
                else
                {
                    // The interface already exists hence we simply map to it. Note: multiple subsystems may map to a single 
                    // sonar system (thus interface) so we need to maintain reference counting internally.

                    SystemListIterator_t pInterface = m_Systems.end();
                    
                    if ( InterfaceFromSubsystemInfo( pInterface, SubsystemIndex, reInterfaceType ) )
                    {
                        pInterface->Add( NULL );                            // Cause its reference count to be incremented and...
                        iSystemIndex = pInterface->ReferenceCount();        // retrieve it.
                    }
                }

                // Now, update the interface index in the subsystem index object before pushing onto the list.

                SubsystemIndex.SystemIndex( iSystemIndex );

                m_Subsystems.push_back( SubsystemIndex );

                // In debug mode, verify the adjustment was actually applied to the pushed object.

                ASSERT( m_Subsystems.back().SystemIndex() == iSystemIndex );

                // Sort the subsystem list.

                SortSubsystemList();

                // Assume all is well for now.

                bSuccess = true;
            }
            catch ( LPCTSTR lpszMessage )
            {
                bSuccess = false;
                TRACE( _T( "CDispatcher::CreateSubsystem(), %s\n" ), lpszMessage );
            }

            // Leave the garded critical sections.

            m_SystemListGuard.Leave();
            m_SubsystemListGuard.Leave();
        }
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CDispatcher::CreateSubsystem(), Unspecified exception caught\n" ) );
    }

    return bSuccess;
}

bool CDispatcher::DestroySubsystem( const int &riSensorIndex )
{
    __TRY
    {
        m_SubsystemListGuard.Enter();

        // Firstly, locate the subsystem from its index if it exists.

        SubsystemListIterator_t pSubsystem = m_Subsystems.end();

        if ( IsRegistered( riSensorIndex, pSubsystem ) )
        {
            // Get its interface index, remove the subsystem from the subsystem list and resort the list.

            const int iSystemIndex = pSubsystem->SystemIndex();

            m_Subsystems.erase( pSubsystem );
            SortSubsystemList();

            // Next, remove the reference to the system (thus interface) to which the subsystem applied. Note: if the internal reference
            // count drops to zero, destroy the interface object otherwise other subsystems are likely attached.

            __TRY
            {
                m_SystemListGuard.Enter();

                SystemListIterator_t pInterface = find( m_Systems.begin(), m_Systems.end(), iSystemIndex );

                if ( pInterface != m_Systems.end() )
                {
                    const int  iType            = pInterface->Type();
                    void      *pvSonarInterface = pInterface->Remove();         // Sonar interface returned if and only if last instance destroyed.

                    // When last instance is about to be removed, we must destroy the object to which it points.

                    if ( pvSonarInterface != NULL )
                    {
                        switch ( iType )
                        {
                            case interfaceTypeLocal:

                                delete ( static_cast<LocalSonar_t *>( pvSonarInterface ) );
                                break;

                            case interfaceTypeRemote:

                                delete ( static_cast<RemoteSonar_t *>( pvSonarInterface ) );
                                break;
                        }
                    }

                    // Erase the system then re-sort the list.

                    m_Systems.erase( pInterface );

                    SortInterfaceList();
                }
            }
            __FINALLY
            {
                // We're done with the system list, so leave its guarded section.

                m_SystemListGuard.Leave();
            }
            __ENDFINALLY

            // Re-sort the subsystem list.

            SortSubsystemList();
        }
    }
    __FINALLY
    {
        m_SubsystemListGuard.Leave();
    }
    __ENDFINALLY

    return true;
}

bool CDispatcher::Write7kRecord(    const int              &riSensorIndex,
                                    const BYTE             *pby7kRecord,
                                    const unsigned long    &rulBytes )
{
    bool bSuccess = false;                  // Assume failure for now.

    try
    {
        if ( rulBytes == 0UL )
        {
            bSuccess = true;                // Nothing to do.
        }
        else if ( pby7kRecord == NULL )
        {
            ThrowMessage_m( "pbyRecord is NULL" );
        }
        else
        {
            // Firstly, look up the interface index from the sensor index list then...

            const int iSystemIndex = SystemIndexFromSensorIndex( riSensorIndex );
            
            if ( iSystemIndex == -1 )
            {
                ThrowMessage_m( "Can't locate specified sensor's sonar interface descriptor" );
            }

            // write the 7k record out the system's interface.

            m_SystemListGuard.Enter();

            try
            {
                bSuccess = SonarInterface( iSystemIndex )->Write7kRecord( pby7kRecord, rulBytes );
            }
            catch ( ... )
            {
                bSuccess = false;
                TRACE( _T( "CDispatcher::Write7kRecord(), Unspecified exception caught\n" ) );
            }

            m_SystemListGuard.Leave();
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "CDispatcher::Write7kRecord(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CDispatcher::Write7kRecord(), Unspecified exception caught\n" ) );
    }

    return bSuccess;
}

void CDispatcher::GenericSensorData(    const bool             &rbEnable,
                                        PFN_SUBSYSTEMCALLBACK   pfnCallback,
                                        void                   *pvParam )
{
    m_GenericSensorDataDispatcher.Enable( rbEnable, pfnCallback, pvParam );
}

CSystemInfo::DeviceInfo_t CDispatcher::DeviceInfo( const int &riSensorIndex )
{
    CSystemInfo::DeviceInfo_t DeviceInfo;

    __TRY
    {
        m_SubsystemListGuard.Enter();
        m_SystemListGuard.Enter();

        int iSystemIndex = SystemIndexFromSensorIndex( riSensorIndex );

        if ( iSystemIndex != -1 )
        {
            SystemListIterator_t pSystem = find( m_Systems.begin(), m_Systems.end(), iSystemIndex );

            if ( pSystem != m_Systems.end() )
            {
                DeviceInfo = (static_cast<CSystemInfo *>( *pSystem ))->DeviceInfo();
            }
        }
    }
    __FINALLY
    {
        m_SystemListGuard.Leave();
        m_SubsystemListGuard.Leave();
    }
    __ENDFINALLY

    return DeviceInfo;
}

bool CDispatcher::HasRecordBeenReceived( const unsigned long    &rulRecordType,
                                         const int              &riSensorIndex )
{
    bool bFoundRecord = false;

    // Locate the system to which this sensor index is mapped and then scan its received message queue
    // for the specfied message and return status of search.

    __TRY
    {
        m_SubsystemListGuard.Enter();
        m_SystemListGuard.Enter();

        int iSystemIndex = SystemIndexFromSensorIndex( riSensorIndex );

        if ( iSystemIndex != -1 )
        {
            SystemListIterator_t pSystem = find( m_Systems.begin(), m_Systems.end(), iSystemIndex );

            if ( pSystem != m_Systems.end() )
            {
                bFoundRecord = pSystem->HasRecordBeenReceived( rulRecordType );
            }
        }
    }
    __FINALLY
    {
        m_SystemListGuard.Leave();
        m_SubsystemListGuard.Leave();
    }
    __ENDFINALLY

    return bFoundRecord;
}

///////////////////////////////////////////////////////////////////////////////
// Private members.

inline
bool CDispatcher::DispatchToSubsystem(  const BYTE             *pby7kRecord,
                                        const unsigned long    &rulBytes,
                                        const unsigned long    &rulTimeStamp,
                                        const bool             &bDispatchToAllSubsystems )
{
    // Decode the 7k header, extract the 7k id doublet and execute the relevant subsystem callback.

    bool bSuccess = true;

    __TRY
    {
        m_SubsystemListGuard.Enter();

        // Decode the device id etc. to see which subsystem the message pertains.

        const RECORDHEADER7K *psRecordHeader     = reinterpret_cast<const RECORDHEADER7K *>( pby7kRecord );
        const unsigned long   ulDeviceId         = psRecordHeader->m_ulDeviceId;
        const unsigned short  unSystemEnumerator = psRecordHeader->m_unSystemEnumerator;

        if ( bDispatchToAllSubsystems )
        {
            for ( SubsystemListIterator_t pSubsystem = m_Subsystems.begin(); pSubsystem != m_Subsystems.end(); pSubsystem++ )
            {
               (*pSubsystem) ( pby7kRecord, rulBytes, rulTimeStamp );
            }
        }
        else
        {
            CSubsystemIndex *pSubsystem = SubsystemFrom7kIds( ulDeviceId, unSystemEnumerator );

            if ( pSubsystem != NULL )
            {
               (*pSubsystem) ( pby7kRecord, rulBytes, rulTimeStamp );
            }
            else
            {
                bSuccess = false;
                TRACE ( _T( "CDispatcher::DispatchToSubsystem(), pSubsystem is NULL (%lu, %u).\n" ), ulDeviceId,
                                                                                                     unSystemEnumerator );
            }
        }
    }
    __FINALLY
    {
        m_SubsystemListGuard.Leave();
    }
    __ENDFINALLY

    return bSuccess;
}

inline
CSubsystemIndex * CDispatcher::SubsystemFrom7kIds(  const unsigned long    &rulDeviceId,
                                                    const unsigned short   &runSystemEnumerator )
{
    CSubsystemIndex *pSubsystemIndex = NULL;

    for ( SubsystemListIterator_t pSubsystem = m_Subsystems.begin(); pSubsystem != m_Subsystems.end(); pSubsystem++ )
    {
        if ( pSubsystem->Is7kSubsystem( rulDeviceId, runSystemEnumerator ) )
        {
            pSubsystemIndex = &(*pSubsystem);
            break;
        }
    }

    return pSubsystemIndex;
}

inline
bool CDispatcher::IsRegistered( const int &riSensorIndex, SubsystemListIterator_t & rpSubsystem )
{
    rpSubsystem = SubsystemIteratorFromSensorIndex( riSensorIndex );

    return ( rpSubsystem != m_Subsystems.end() );
}

inline
void CDispatcher::SortSubsystemList( void )
{
    if ( ! m_Subsystems.empty() )
    {
        m_Subsystems.sort();
    }
}

inline
void CDispatcher::SortInterfaceList( void )
{
    if ( ! m_Systems.empty() )
    {
        m_Systems.sort();
    }
}

inline
CDispatcher::SubsystemListIterator_t CDispatcher::SubsystemIteratorFromSensorIndex( const int &riSensorIndex )
{
    return ( find( m_Subsystems.begin(), m_Subsystems.end(), riSensorIndex ) );
}

inline
int CDispatcher::SystemIndexFromSensorIndex( const int &riSensorIndex )
{
    SubsystemListIterator_t pSubsystem = SubsystemIteratorFromSensorIndex( riSensorIndex );

    return ( ( pSubsystem == m_Subsystems.end() ) ? -1 : pSubsystem->SystemIndex() );
}

inline
C7kSonarInterface * CDispatcher::SonarInterface( const int &riSystemIndex )
{
    C7kSonarInterface           *pSonar     = NULL;
    SystemListIterator_t pInterface = find ( m_Systems.begin(), m_Systems.end(), riSystemIndex );

    if ( pInterface != m_Systems.end() )
    {
        pSonar = static_cast<C7kSonarInterface *>( pInterface->Interface() );
    }

    ASSERT( pSonar != NULL );

    return pSonar;
}

inline
bool CDispatcher::IsNewInterface(   const CSubsystemIndex &rSubsystemIndex,
                                    const EINTERFACETYPE  &reInterfaceType )
{
    bool bNewInterface = false;

    if ( reInterfaceType == interfaceTypeRemote )
    {
        bNewInterface = true;
    }
    else
    {
        // Scan the list of subsystems to see if any have the same device and system enumeration ID. If they do not,
        // a new interface will be required otherwise we'll use the existing interface.

        bool bFound = false;

        for ( SubsystemListIterator_t pSubsystem = m_Subsystems.begin(); pSubsystem != m_Subsystems.end(); pSubsystem++ )
        {
            if ( ( rSubsystemIndex.DeviceId()         == pSubsystem->DeviceId()         ) &&
                 ( rSubsystemIndex.SystemEnumerator() == pSubsystem->SystemEnumerator() )  )
            {
                bFound = true;
                break;
            }
        }

        bNewInterface = ! bFound;
    }

    return bNewInterface;
}

inline
bool CDispatcher::NewSystem(    C7kSystem              &rSystem,
                                const EINTERFACETYPE   &reInterfaceType,
                                const unsigned long    &rulPortOut,
                                const unsigned long    &rulPortIn,
                                const char             *pszAddress )
{
    // Determine the next available system index.

    if ( m_Systems.empty() )
    {
        rSystem.SystemIndex( 0 );
    }
    else
    {
        m_Systems.sort();

        // Firstly, build a list of sensor indexes to be sorted.

        std::vector<int>  Assigned;

        for ( SystemListIterator_t pInterface = m_Systems.begin(); pInterface != m_Systems.end(); pInterface++ )
        {
            C7kSystem *pListedSystem = &(*pInterface);

            Assigned.push_back( pListedSystem->SystemIndex() );
        }

        ASSERT( ! Assigned.empty() );

        if ( ! Assigned.empty() )
        {
            sort( Assigned.begin(), Assigned.end() );

            // Run through the list and determine the next available index.

            std::vector<int>::iterator pItem = Assigned.begin();

            while ( pItem != Assigned.end() )
            {
                int iIndex1 = *pItem;

                pItem++;

                if ( pItem != Assigned.end() )
                {
                    if ( *pItem > ( iIndex1 + 1 ) )
                    {
                         rSystem.SystemIndex( iIndex1 + 1 );
                        break;
                    }
                }
                else
                {
                    rSystem.SystemIndex( iIndex1 + 1 );
                    break;
                }
            }
        }
    }

    // Finally, populate the remaining values.

    rSystem.InterfaceTypeInfo( reInterfaceType, rulPortOut, rulPortIn, pszAddress );

    return true;
}

inline
void CDispatcher::DestroyAll( void )
{
    // Firstly, destroy the sybsystem mapping list ...

    try
    {
        m_Subsystems.clear();
    }
    catch ( ... )
    {
        ASSERT( false );
    }

    // then the sonar interfaces and the list thereof.

    try
    {
        for ( SystemListIterator_t pInterface = m_Systems.begin(); pInterface != m_Systems.end(); pInterface++ )
        {
            C7kSystem rSystem = *pInterface;

            if ( rSystem.Interface() != NULL )
            {
                const int   iType            = rSystem.Type();
                void *      pvSonarInterface = rSystem.Remove();

                if ( pvSonarInterface != NULL )
                {
                    switch ( iType )
                    {
                        case interfaceTypeLocal:

                            delete ( static_cast<LocalSonar_t *>( pvSonarInterface ) );
                            break;

                        case interfaceTypeRemote:

                            delete ( static_cast<RemoteSonar_t *>( pvSonarInterface ) );
                            break;

                        default:
                            break;
                    }
                }
            }
        }

        m_Systems.clear();
    }
    catch ( ... )
    {
        ASSERT( false );
    }
}

inline
bool CDispatcher::InterfaceFromSubsystemInfo(   SystemListIterator_t   &rpInterface,
                                                const CSubsystemIndex  &rSubsystemIndex,
                                                const EINTERFACETYPE   &reInterfaceType )
{
    // Here we search the subsystem list to find the first occurance of any pre-existing subsystems that will share the interface 
    // based on its 7k ids. Therein, we require the subsystem ID to be unique and both the device and system enumeration IDs to be the same.
    // NB: This only applies to local interfaces; remote (networked) interfaces will always have their own interface object.

    rpInterface = m_Systems.end();

    if ( ( reInterfaceType == interfaceTypeLocal ) && ( ! m_Systems.empty() ) )
    {
        for ( SubsystemListIterator_t pSubsystem = m_Subsystems.begin(); pSubsystem != m_Subsystems.end(); pSubsystem++ )
        {
            if ( ( rSubsystemIndex.DeviceId()         == pSubsystem->DeviceId()         ) &&
                 ( rSubsystemIndex.SystemEnumerator() == pSubsystem->SystemEnumerator() )  )
            {
                rpInterface = find( m_Systems.begin(), m_Systems.end(), pSubsystem->SystemIndex() );
                break;
            }
        }
    }

    return ( rpInterface != m_Systems.end() );
}

///////////////////////////////////////////////////////////////////////////////
// CDispatcher::CGenericSensorDataDispatcher implementation.

inline
CDispatcher::CGenericSensorDataDispatcher::CGenericSensorDataDispatcher( void )
{
    m_bEnabled    = false;
    m_pfnCallback = NULL;
    m_pvParam     = NULL;
}

inline
CDispatcher::CGenericSensorDataDispatcher::~CGenericSensorDataDispatcher( void )
{
    m_bEnabled    = false;
    m_pfnCallback = NULL;
    m_pvParam     = NULL;
}

inline
void CDispatcher::CGenericSensorDataDispatcher::Enable( const bool             &rbEnable,
                                                        PFN_SUBSYSTEMCALLBACK   pfnCallback,
                                                        void                   *pvParam )
{
    if ( rbEnable )
    {
        m_bEnabled = ( pfnCallback != NULL );
    }
    else
    {
        m_bEnabled = false;
    }

    m_pfnCallback = pfnCallback;
    m_pvParam     = pvParam;
}

inline
bool CDispatcher::CGenericSensorDataDispatcher::IsEnabled( void ) const
{
    return m_bEnabled;
}

inline
bool CDispatcher::CGenericSensorDataDispatcher::operator () (   const BYTE             *pby7kRecord,
                                                                const unsigned long    &rulBytes,
                                                                const unsigned long    &rulTimeStamp ) const
{
    bool bSuccess = true;

    try
    {
        ASSERT( m_bEnabled );

        if ( m_pfnCallback != NULL )
        {
            ( m_pfnCallback ) ( m_pvParam, pby7kRecord, rulBytes, rulTimeStamp );
        }
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}

inline
bool CDispatcher::IsRecordDeviceIndependent(    const BYTE             *pby7kRecord,
                                                const unsigned long    &rulBytes )
{
    ASSERT( C7kProtocol::IsValid7kRecord( pby7kRecord, rulBytes ) );

#ifndef _DEBUG
    UNREFERENCED_PARAMETER( rulBytes );
#endif

    const RECORDHEADER7K * const psRecordHeader = reinterpret_cast<const RECORDHEADER7K *>( pby7kRecord );

#pragma CompileMessage_m( "TODO: DEVICE INDEPENDENT COORDINATE SHOULD BE (7000, 0); REMOVE (0,0) ONCE CONFIRMED BY PAM" )
    //ASSERT(  psRecordHeader->m_ulDeviceId != 0UL );
    return ( ( psRecordHeader->m_unSystemEnumerator == 0 ) && ( ( psRecordHeader->m_ulDeviceId == 0UL    )   ||
                                                                ( psRecordHeader->m_ulDeviceId == 7000UL ) )  );
}

inline
bool CDispatcher::IsAckNakRecord( const unsigned long &rulRecordType )
{
    return ( ( rulRecordType == recordTypeRemoteControlAck ) || ( rulRecordType == recordTypeRemoteControlNak ) );
}

///////////////////////////////////////////////////////////////////////////////
// CDispatcher::C7kSystem implementation.

inline
CDispatcher::C7kSystem::C7kSystem( CDispatcher const * pParent )
                       :CParent<CDispatcher>( pParent ),
                        m_uiMaxReceiveQueueLength( uiMaxReceiveQueueLength_c )
{
    __TRY
    {
        m_ReceivedMessageQueue.clear();
        m_RecordIgnoreList.clear();

        // Configure the list of known messages that we're not interested in.

#pragma CompileMessage_m( "TODO: CONFER WITH PAM AND CONFIRM THE FOLLOWING MESSAGES ARE SAFE TO IGNORE" )

        m_RecordIgnoreList.push_back( recordTypeRemoteControl );        // R7500.
        m_RecordIgnoreList.push_back( 4008 );                           // R4008 - Special IFREMER record to go away.
        m_RecordIgnoreList.push_back( 7021 );                           // R7021 - Special (Reserved)... unknown at this time.

    }
    __FINALLY
    {
        m_iSystemIndex          = 0;
        m_iReferenceCount       = 0;
        m_iType                 = 0;

        m_ulPortOut             = 0UL;
        m_ulPortIn              = 0UL;

        m_szAddress[ 0 ]        = '\0';
        m_pvSonarInterface      = NULL;
    }
    __ENDFINALLY
}

inline
CDispatcher::C7kSystem::~C7kSystem( void )
{
    __TRY
    {
        m_ReceivedMessageQueue.clear();
        m_RecordIgnoreList.clear();
    }
    __FINALLY
    {
        m_iSystemIndex          = 0;
        m_iReferenceCount       = 0;
        m_iType                 = 0;

        m_ulPortOut             = 0UL;
        m_ulPortIn              = 0UL;

        m_szAddress[ 0 ]        = '\0';
        m_pvSonarInterface      = NULL;
    }
    __ENDFINALLY
}

inline
CDispatcher::C7kSystem::C7kSystem( const C7kSystem &rRhs )
                       :CParent<CDispatcher>( rRhs.m_pParent ),
                        m_uiMaxReceiveQueueLength( uiMaxReceiveQueueLength_c )
{
    __TRY
    {
        strcpy( &m_szAddress[ 0 ], &(rRhs.m_szAddress[ 0 ]) );

        m_ReceivedMessageQueue  = rRhs.m_ReceivedMessageQueue;
        m_RecordIgnoreList      = rRhs.m_RecordIgnoreList;
    }
    __FINALLY
    {
        m_iSystemIndex          = rRhs.m_iSystemIndex;
        m_iReferenceCount       = rRhs.m_iReferenceCount;
        m_pvSonarInterface      = rRhs.m_pvSonarInterface;
        m_iType                 = rRhs.m_iType;
        m_ulPortOut             = rRhs.m_ulPortOut;
        m_ulPortIn              = rRhs.m_ulPortIn;
    }
    __ENDFINALLY
}

inline
CDispatcher::C7kSystem & CDispatcher::C7kSystem::operator = ( const C7kSystem &rRhs )
{
    if ( &rRhs != this )
    {
        __TRY
        {
            strcpy( &m_szAddress[ 0 ], &(rRhs.m_szAddress[ 0 ]) );

            m_ReceivedMessageQueue  = rRhs.m_ReceivedMessageQueue;
            m_RecordIgnoreList      = rRhs.m_RecordIgnoreList;
        }
        __FINALLY
        {
            m_iSystemIndex          = rRhs.m_iSystemIndex;
            m_iReferenceCount       = rRhs.m_iReferenceCount;
            m_pvSonarInterface      = rRhs.m_pvSonarInterface;
            m_iType                 = rRhs.m_iType;
            m_ulPortOut             = rRhs.m_ulPortOut;
            m_ulPortIn              = rRhs.m_ulPortIn;
        }
        __ENDFINALLY
    }

    return *this;
}

inline
bool CDispatcher::C7kSystem::operator < ( const C7kSystem &rRhs ) const
{
    return ( m_iSystemIndex < rRhs.m_iSystemIndex );
}

inline
bool CDispatcher::C7kSystem::operator == ( const int &riSystemIndex ) const
{
    return ( m_iSystemIndex == riSystemIndex );
}

inline
CDispatcher::C7kSystem::operator CSystemInfo * ( void )
{
    return ( static_cast<::CSystemInfo *>( this ) );
}

inline
void CDispatcher::C7kSystem::Add( void *pvInterface )
{
    ++m_iReferenceCount;

    if ( ( pvInterface != NULL ) && ( m_iReferenceCount == 1 ) )
    {
        m_pvSonarInterface = pvInterface;
        RegisterCallback( DataAvailable, this );
    }
}

inline
void * CDispatcher::C7kSystem::Remove( void )
{
    void *pvInterface = NULL;

    if ( --m_iReferenceCount <= 0 )
    {
        m_iReferenceCount  = 0;

        DeregisterCallback();

        pvInterface        = m_pvSonarInterface;
        m_pvSonarInterface = NULL;
    }

    return pvInterface;
}

inline
void CDispatcher::C7kSystem::SystemIndex( const &riSystemIndex )
{
    m_iSystemIndex = riSystemIndex;
}

inline
void CDispatcher::C7kSystem::InterfaceTypeInfo( const int           &riType,
                                                const unsigned long &rulPortOut,
                                                const unsigned long &rulPortIn,
                                                const char          *pszAddress )
{
    m_iType     = riType;
    m_ulPortOut = rulPortOut;
    m_ulPortIn  = rulPortIn;

    strncpy( &m_szAddress[ 0 ], pszAddress, 15 );
}

inline
void * CDispatcher::C7kSystem::Interface( void ) const
{
    return m_pvSonarInterface;
}

inline
int CDispatcher::C7kSystem::Type( void ) const
{
    return m_iType;
}

inline
int CDispatcher::C7kSystem::ReferenceCount( void ) const
{
    return m_iReferenceCount;
}

inline
int CDispatcher::C7kSystem::SystemIndex( void ) const
{
    return m_iSystemIndex;
}

void CDispatcher::C7kSystem::DataAvailable( const void             *pvParam,
                                            const BYTE             *pby7kRecord,
                                            const unsigned long    &rulBytes,
                                            const unsigned long    &rulTimeStamp )
{
    try
    {
        ASSERT( C7kProtocol::IsValid7kRecord( pby7kRecord, rulBytes ) );

        C7kSystem *pthis = static_cast<C7kSystem *>( const_cast<void *>( pvParam ) );

        if ( pthis != NULL )
        {
            if ( ! pthis->HandleInterfaceData( pby7kRecord, rulBytes, rulTimeStamp ) )
            {
                TRACE( _T( "CDispatcher::C7kSystem::InferfaceDataAvailable(), pthis->HandleInterfaceData() failed.\n" ) );
            }
        }
    }
    catch ( ... )
    {
        TRACE( _T( "CDispatcher::C7kSystem::InferfaceDataAvailable(), Unspecified exception caught\n" ) );
    }
}

inline
bool CDispatcher::C7kSystem::HandleInterfaceData(   const BYTE             *pby7kRecord,
                                                    const unsigned long    &rulBytes,
                                                    const unsigned long    &rulTimeStamp )
{
    // Here we have an inbound 7k message on the interface. First, check its ids to see whether it's
    // associated with a device or a global message. If the former, dispatch the message (by the 
    // corresponding subsystem's callback) otherwise we set the system's state as necessary then have the 
    // dispatcher deal with the record directly.

    bool bSuccess = false;

    try
    {
        const RECORDHEADER7K * const psDRF = reinterpret_cast<const RECORDHEADER7K * const>( pby7kRecord );

        //TRACE( _T( "Received: R%lu, Device: %lu, Enum: %u, Timetag: %3u %4u, %02u:%02u:%6.3f.\n" ), psDRF->m_ulRecordType,
        //                                                                                            psDRF->m_ulDeviceId,
        //                                                                                            psDRF->m_unSystemEnumerator,
        //                                                                                            psDRF->m_sTime7k.m_unDay,
        //                                                                                            psDRF->m_sTime7k.m_unYear,
        //                                                                                            psDRF->m_sTime7k.m_ucHours,
        //                                                                                            psDRF->m_sTime7k.m_ucMinutes,
        //                                                                                            psDRF->m_sTime7k.m_fSeconds );

        bSuccess = UpdateSystemStateAndDispatch( pby7kRecord, rulBytes, rulTimeStamp );

        AddRecordTypeToQueue( psDRF->m_ulRecordType );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CDispatcher::C7kSystem::HandleInterfaceData(), Unspecified exception caught\n" ) );
    }

    return bSuccess;
}

inline
bool CDispatcher::C7kSystem::RegisterCallback(  PFN_SUBSYSTEMCALLBACK   pfnCallback,
                                                void                   *pvParam )
{
    C7kSonarInterface *pSonarInterface = static_cast<C7kSonarInterface *>( m_pvSonarInterface );
    ASSERT( pSonarInterface != NULL );

    if ( pSonarInterface != NULL )
    {
        return pSonarInterface->RegisterCallback( pfnCallback, pvParam );
    }

    return false;
}

inline
bool CDispatcher::C7kSystem::DeregisterCallback( void )
{
    C7kSonarInterface *pSonarInterface = static_cast<C7kSonarInterface *>( m_pvSonarInterface );
    ASSERT( pSonarInterface != NULL );

    if ( pSonarInterface != NULL )
    {
        return pSonarInterface->DeregisterCallback();
    }

    return false;
}

inline
bool CDispatcher::C7kSystem::UpdateSystemStateAndDispatch(  const BYTE             *pby7kRecord,
                                                            const unsigned long    &rulBytes,
                                                            const unsigned long    &rulTimeStamp )
{
    // Handles a device non-specific record (e.g, reference point info) or a global reply that pertains
    // to all subsystems.

    CDispatcher            &rDispatcher              = Parent();

    bool                    bDispatch                = false;
    bool                    bDispatchToAllSubsystems = false;

    const unsigned long     ulRecordType = (reinterpret_cast<const RECORDHEADER7K *>( pby7kRecord ))->m_ulRecordType;

    if ( IsIgnoreRecord( ulRecordType ) )
    {
        bDispatch = false;
    }
    else if ( CDispatcher::IsRecordDeviceIndependent( pby7kRecord, rulBytes ) )
    {
        bDispatchToAllSubsystems = true;

        switch ( ulRecordType )
        {
            case recordTypeRemoteControlAck:
            case recordTypeRemoteControlNak:

                bDispatch = true;
                break;

            case recordTypeConfigurationSettings:

                DecodeDeviceInfo( pby7kRecord );            // Set the system state.
                bDispatch = true;                           // Pass onto the subsystem for subsequent handling as necessary.
                break;

            case recordTypeSystemEvents:
            case recordTypeSystemEventMessage:

                bDispatch = true;
                break;

            default:

                // Dispatch generic sensor data (record type 1000 to 1999 inclusive) or sonar dynamic correction data
                // such as sound speed, pitch, roll etc.

                if ( ( ulRecordType >= recordTypeGenericSensorBegin ) &&
                     ( ulRecordType <= recordTypeGenericSensorEnd   )  )
                {
                    if ( rDispatcher.m_GenericSensorDataDispatcher.IsEnabled() )
                    {
                        rDispatcher.m_GenericSensorDataDispatcher( pby7kRecord, rulBytes, rulTimeStamp );
                    }
                }
                else if ( ( ulRecordType >= recordTypeSonarCorrectionDataBegin ) &&
                          ( ulRecordType <= recordTypeSonarCorrectionDataEnd   )  )
                {
                    rDispatcher.m_GenericSensorDataDispatcher( pby7kRecord, rulBytes, rulTimeStamp );
                }
                else
                {
                    TRACE( _T( "CDispatcher::C7kSystem::UpdateSystemStateAndDispatch(), Record %lu ignored !!\n" ), ulRecordType );
                }

                break;
        }
    }
    else
    {
        bDispatch = true;
    }

    if ( bDispatch )
    {
        return rDispatcher.DispatchToSubsystem( pby7kRecord, rulBytes, rulTimeStamp, bDispatchToAllSubsystems );
    }

    return true;
}

inline
bool CDispatcher::C7kSystem::HasRecordBeenReceived( const unsigned long &rulRecordType )
{
    if ( ! m_ReceivedMessageQueue.empty() )
    {
        return ( find( m_ReceivedMessageQueue.begin(), m_ReceivedMessageQueue.end(), rulRecordType ) != m_ReceivedMessageQueue.end() );
    }

    return false;
}

inline
void CDispatcher::C7kSystem::AddRecordTypeToQueue( const unsigned long &rulRecordType )
{
    m_ReceivedMessageQueue.push_back( rulRecordType );

    if ( m_ReceivedMessageQueue.size() > m_uiMaxReceiveQueueLength )
    {
        //TRACE( _T( "CDispatcher::C7kSystem::AddRecordTypeToQueue(), Message type %lu being poped\n" ), m_ReceivedMessageQueue.front() );
        m_ReceivedMessageQueue.pop_front();
    }
}

inline
bool CDispatcher::C7kSystem::IsIgnoreRecord( const unsigned long &rulRecordType )
{
    if ( ! m_RecordIgnoreList.empty() )
    {
        return ( find( m_RecordIgnoreList.begin(), m_RecordIgnoreList.end(), rulRecordType ) != m_RecordIgnoreList.end() );
    }

    return false;
}
