//
//  Copyright © 2001 - 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:   MessageProcessor.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      
//

#include "StdAfx.h"
#include "MessageProcessor.h"
#include "EdgeTechFSDW.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// CMessageProcessor class implmentation.

CMessageProcessor::CMessageProcessor(   PFN_HANDLER_CALLBACK    pfnHandlerCallback,
                                        void                   *pvParam,
                                        char                   *pszAddress,
                                        const unsigned long    &rulPort,
                                        const unsigned long    &rulMaxMessageSize )

                  :m_ulInvalidPort( (unsigned long) -1 ),

                  m_pfnHandlerCallback( pfnHandlerCallback ),
                  m_pvParam( pvParam )

{
    ASSERT( pszAddress        != NULL );
    ASSERT( rulPort           != m_ulInvalidPort );
    ASSERT( rulMaxMessageSize >  0UL );

    m_pChannel = std::auto_ptr<CSonarChannel_t>( new CSonarChannel_t( MessageCallback, this, pszAddress, rulPort, rulMaxMessageSize, SOCK_STREAM ) );

    ASSERT( m_pChannel.get() != NULL );

    if ( ! m_pChannel->Connect( true ) )
    {
        TRACE( "CMessageProcessor::CMessageProcessor(), m_pChannel->Connect() failed\n" );
    }
}

CMessageProcessor::~CMessageProcessor( void )
{
    DestroyAutoPointer( m_pChannel );
}

bool CMessageProcessor::SendCommand( CEdgeTechMessage &rCommand )
{
    bool bSuccess = false;

    ASSERT( m_pChannel.get() != NULL );

    if ( m_pChannel.get() != NULL )
    {
        __try
        {
            m_SendGuard.Enter();
            bSuccess = m_pChannel->Send( rCommand.Message(), rCommand.MessageSize() );
        }
        __finally
        {
            m_SendGuard.Leave();
        }
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Protected services.

void CMessageProcessor::HandleInboundMessage(   BYTE               *pbyData,
                                                unsigned long       ulNumBytes,
                                                unsigned long       ulTimeStamp )
{
    ASSERT( pbyData    != NULL );
    ASSERT( ulNumBytes >  0UL );

    try
    {
        if ( ( ulNumBytes > 0UL ) && ( pbyData != NULL ) )
        {
            // Validate message type and, if valid, queue ready for the handling thread.

            if ( ! m_InboundMessage.SetHeader( (SonarMessageHeaderType *) pbyData ) )
            {
                ThrowMessage_m( "Invalid message detected" );
            }

            const unsigned char ucCommand = m_InboundMessage.SonarCommand();

            if ( ( ucCommand == SONAR_COMMAND_REPLY )    || 
                 ( ucCommand == SONAR_COMMAND_PLAYBACK ) ||
                 ( ucCommand == SONAR_COMMAND_ERROR )     )
            {
                HandleSonarMessage( ulTimeStamp, m_InboundMessage );
            }
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        TRACE( _T( "CSonarCommand::HandleInboundMessage(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        TRACE( _T( "CSonarCommand::HandleInboundMessage(), Unspecified exception\n" ) );
    }
}

bool CMessageProcessor::HandleSonarMessage( const unsigned long &rulTimeStamp,
                                            CEdgeTechMessage    &rMessage )
{
    const unsigned short unMessage = rMessage.SonarMessage();

    if ( ( unMessage == SONAR_MESSAGE_ALIVE ) || ( unMessage == SONAR_COMMAND_ERROR ) )
    {
        return CallCallback( rulTimeStamp, rMessage );
    }

    return true;
}

bool CMessageProcessor::CallCallback(   const unsigned long &rulTimeStamp,
                                        CEdgeTechMessage    &rMessage )
{
    bool bSuccess = false;

    try
    {
        ASSERT( m_pfnHandlerCallback != NULL );

        if ( m_pfnHandlerCallback != NULL )
        {
            bSuccess = ( m_pfnHandlerCallback ) ( rMessage, rulTimeStamp, const_cast<void *>( m_pvParam ) );
        }
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CMessageProcessor::CallCallback(), Unspecified exception caught\n" ) );
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Private members.

void CMessageProcessor::MessageCallback(    BYTE           *pbyData,                // Data pointer where relevant - can be NULL.
                                            PVOID           lpvParam,               // Optional parameter.
                                            ULONG           ulNumBytes,             // Number of data bytes received - can be zero.
                                            INT             iSocketIndex,           // Socket index (relevant to server end only - will be zero otherwise).
                                            ULONG           ulTimeStamp,            // timestamp.
                                            INT             iReasonForCallback )    // Reason for this callback.
{
    // Callbacks invoked by the reader threads inside the SonarChannel_t objects above upon 
    // reciept of a valid socket data message from either the command or data channels
    // of the FS-AU subsystem.

    UNREFERENCED_PARAMETER( iSocketIndex );

    try
    {
        CMessageProcessor *pthis = (CMessageProcessor *) lpvParam;

        ASSERT( pthis != NULL );

        if ( pthis != NULL )
        {
            switch ( iReasonForCallback )
            {
                case callbackConnect:

                    TRACE( "CMessageProcessor::MessageCallback(), Connect detected\n" );
                    break;

                case callbackDisconnect:

                    TRACE( "CMessageProcessor::MessageCallback(), Disconnect detected\n" );
                    NotifyOfDisconnect();
                    break;

                case callbackData:

                    pthis->HandleInboundMessage( pbyData, ulNumBytes, ulTimeStamp );
                    break;
            }
        }
    }
    catch ( ... )
    {
        TRACE( _T( "CMessageProcessor::MessageCallback(), Unspecified exception caught\n" ) );
    }
}

void CMessageProcessor::NotifyOfDisconnect( void )
{
    CEdgeTechFSDWApp * pApp = GetApp();

    ASSERT( pApp != NULL );

    if ( pApp != NULL )
    {
        pApp->SocketDisconnect();
    }
}

