//  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:   
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      
//

#include "StdAfx.h"
#include "Subsystem.h"

#include <atlbase.h>

#include "..\..\..\Utils\NetUtils\7kProtocolVersion.h"
#include "..\..\..\Utils\NetUtils\7kAcknowlege.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

namespace                                                       // Begin anonymous namespace.
{
    class CStoredAttributes
    {
    private:

        const char * const              m_szDefaultAddress;
        const unsigned int              m_uiDefaultPort;
        const unsigned short            m_unDefaultProtocolVersion;

        LPCTSTR                         m_lpszRESON7kGenericRegistryKey;
        LPCTSTR                         m_lpsz6046RESON7kGenericAddressRegistryKey;
        LPCTSTR                         m_lpsz6046RESON7kGenericPortRegistryKey;
        LPCTSTR                         m_lpsz6046RESON7kGenericTypeRegistryKey;
        LPCTSTR                         m_lpsz6046RESON7kGenericNameRegistryKey;
        LPCTSTR                         m_lpsz6046RESON7kGenericProtocolVersionRegistryKey;
        LPCTSTR                         m_lpsz6046RESON7kGenericHostReplyRegistryKey;

    public:

        ////////////////
        // Attributes.

        bool                            m_bHostReply;
        CString                         m_sName;
        CString                         m_sAddress;
        unsigned int                    m_uiPort;
        unsigned short                  m_unVersion;
        CGenericSocket::ESOCKETTYPE     m_eSocketType;

        ////////////////
        // Services.

                    CStoredAttributes   (   void );
        virtual    ~CStoredAttributes   (   void );

        void        SetDefaults         (   void );

        bool        QueryString         (   CRegKey    &rKey,
                                            LPCTSTR     lpszSubkey,
                                            CString    &rsReadValue );

        bool        Retrieve            (   const int  &riSensorIndex );
    };
};                                                              // End anonymous namespace.

//////////////////////////////////////////////////////////////////////
// CSubsystem class implementation.

CSubsystem::CSubsystem  ( const int &riSensorIndex )
           :CSensor     (            riSensorIndex )
{
    m_pHandler     = NULL;
}

CSubsystem::~CSubsystem( void )
{
    __TRY
    {
        if ( IsStarted() && ( ! Shutdown() ) )
        {
            TRACE( _T( "CSubsystem::CSubsystem(), Anomalous shutdown detected.\n" ) );
        }
    }
    __FINALLY
    {
        DestroyHandler();
        m_pHandler = NULL;
    }
    __ENDFINALLY
}

// Overrides to base class CSensor pure virtual methods.

bool CSubsystem::Startup(   const int           &riSensorIndex,
                            const DWORD         &rdwThreadId,
                            const unsigned int  &ruiDataReadyMessageId,
                            const unsigned int  &ruiReplyReadyMessageId,
                            const int           &riActivateOnStartup )
{
    bool bSuccess = false;

    try
    {
        ASSERT( m_pHandler == NULL );

        DestroyHandler();

        if ( ! CSensor::Startup( riSensorIndex,
                                 rdwThreadId,
                                 ruiDataReadyMessageId,
                                 ruiReplyReadyMessageId,
                                 riActivateOnStartup ) )
        {
            TRACE( _T( "CSubsystem::Startup(), CSensor::Startup() failed.\n" ) );
        }
        else if ( ( m_pHandler = new CDataHandler( this ) ) == NULL )
        {
            TRACE( _T( "CSubsystem::Startup(), new CDataHanlder() failed.\n" ) );
        }
        else if ( ! m_pHandler->Create() )
        {
            TRACE( _T( "CSubsystem::Startup(), m_pHandler->Create() failed.\n" ) );
        }
        else
        {
            bSuccess = true;
        }
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CSubsystem::Startup(), Unspecified exception caught!\n" ) );
    }

    return bSuccess;
}

bool CSubsystem::Shutdown( void )
{
    bool bSuccess = true;

    __TRY
    {
        // Perform any shutdown handling.

        DestroyHandler();
 
        // Now shutdown the subsystem.

        if ( ! CSensor::Shutdown() )
        {
            bSuccess = false;
        }
    }
    __FINALLY
    {
        ASSERT( m_pHandler == NULL );
        m_pHandler = NULL;
    }
    __ENDFINALLY

    return bSuccess;
}

bool CSubsystem::Load(  BYTE                    *pbyData,
                        unsigned long           *pulSize,
                        unsigned long           *pulTimeStamp )
{
    UNREFERENCED_PARAMETER( pbyData );
    UNREFERENCED_PARAMETER( pulSize );
    UNREFERENCED_PARAMETER( pulTimeStamp );

    // Obselete interface method; shouldn't be invoked.

    ASSERT( false );

    return true;
}

bool CSubsystem::SendCommand(   int             iClientIndex,
                                char           *pszCommand )
{
    // Not relevant to this device sensor type therefore do nothing.

    UNREFERENCED_PARAMETER( iClientIndex );
    UNREFERENCED_PARAMETER( pszCommand );

    return true;
}

bool CSubsystem::RetrieveStatus(    BYTE           *pbyData,
                                    unsigned long  *pulSize,
                                    unsigned long  *pulTimeStamp,
                                    int            *piClientIndex )
{

#pragma CompileMessage_m( "TODO: IMPLEMENTATION NEEDED ONCE QUEUED MESSAGES BECOME RELEVANT" )

    DBG_UNREFERENCED_PARAMETER( pbyData );

    *pulSize        = 0UL;
    *pulTimeStamp   = 0UL;
    *piClientIndex  = -1;

    return true;
}

bool CSubsystem::IsSensorHealthy( void )
{
    if ( m_pHandler != NULL )
    {
        return m_pHandler->IsHealthy();
    }

    return false;
}

bool CSubsystem::Reset( void )
{
    bool bSuccess = false;

    try
    {
        DestroyHandler();

        if ( ( m_pHandler = new CDataHandler( this ) ) == NULL )
        {
            TRACE( _T( "CSubsystem::Reset(), new CDataHanlder() failed.\n" ) );
        }
        else if ( ! m_pHandler->Create() )
        {
            TRACE( _T( "CSubsystem::Reset(), m_pHandler->Create() failed.\n" ) );
        }
        else
        {
            bSuccess = true;
        }
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}

bool CSubsystem::RouteMessage(  const BYTE                 *pbyMessage,
                                const unsigned long        &rulSize,
                                const unsigned long        &rulTimeStamp )
{
    UNREFERENCED_PARAMETER( rulTimeStamp );

    return Send( const_cast<BYTE *>( pbyMessage ), rulSize );
}

bool CSubsystem::Identify( SENSORINFO *psSensorInfo )
{
    // Irrelevant therefore do nothing.

    UNREFERENCED_PARAMETER( psSensorInfo );

    return true;
}

void CSubsystem::MessageError( const unsigned long &rulRecordId )
{
    // This routine is called from the data handler subordinate object when the message handler has
    // indicated a received message is erroneous. Consequent handling may include any or all of the
    // following:
    //
    //  - client status notification;
    //  - send NAK to source (if so configured);
    //  - annotate event log; or
    //  - raise an alarm.

    try
    {
        bool bQueueStatusInfo = false;
        bool bGenerateAlarm   = false;

        DBG_UNREFERENCED_PARAMETER( rulRecordId );

        if ( bGenerateAlarm )
        {
        }

        if ( bQueueStatusInfo )
        {
        }
    }
    catch ( ... )
    {
        TRACE( _T( "CSubsystem::MessageError(), Unspecified exception caught.\n" ) );
    }
}

inline
void CSubsystem::DestroyHandler( void )
{
    __TRY
    {
        if ( m_pHandler != NULL )
        {
            delete m_pHandler;
            m_pHandler = NULL;
        }
    }
    __FINALLY
    {
        ASSERT( m_pHandler == NULL );
        m_pHandler = NULL;
    }
    __ENDFINALLY
}

inline
bool CSubsystem::Send( BYTE * const         pbyStream,
                       const unsigned long &rulNumBytes )
{
    ASSERT( m_pHandler != NULL );

    if ( m_pHandler != NULL )
    {
        return m_pHandler->Send( pbyStream, rulNumBytes );
    }

    return false;
}

///////////////////////////////////////////////////////////////////////////////
// CSubsystem::CDataHandler internal helper implementation.

inline
CSubsystem::CDataHandler::CDataHandler( CSubsystem const * pParent )
                         :CParent<CSubsystem>( pParent )
{
    m_bReplyToHost   = false;
    m_pGenericSocket = NULL;
}

inline
CSubsystem::CDataHandler::~CDataHandler( void )
{
    __TRY
    {
        if ( m_pGenericSocket != NULL )
        {
            delete m_pGenericSocket;
            m_pGenericSocket = NULL;
        }
    }
    __FINALLY
    {
        ASSERT( m_pGenericSocket == NULL );
        m_pGenericSocket = NULL;
        m_bReplyToHost   = false;
    }
    __ENDFINALLY
}

inline
bool CSubsystem::CDataHandler::Create( void )
{
    bool bSuccess = false;      // Assume failure for now.

    try
    {
        CStoredAttributes SocketAttributes;

        if ( ! SocketAttributes.Retrieve( Parent().SensorIndex() ) )
        {
            TRACE( _T( "CSubsystem::CDataHandler::Create(), Socket attributes unsuccessfully retrieved... about to utilize default settings.\n" ) );
            ASSERT( false );
            SocketAttributes.SetDefaults();
        }

        m_bReplyToHost = SocketAttributes.m_bHostReply;

        // If supplied, convert the send address to its equivalent unsigned long representation.

        unsigned long ulSendAddress = 0UL;

        if ( ! SocketAttributes.m_sAddress.IsEmpty() )
        {
            ulSendAddress = htonl( inet_addr( static_cast<LPCTSTR>( SocketAttributes.m_sAddress ) ) );
        }

        if ( m_pGenericSocket == NULL )
        {
            m_pGenericSocket = new CGenericSocket();
        }

        ASSERT( m_pGenericSocket != NULL );

        if ( m_pGenericSocket != NULL )
        {
            if ( SocketAttributes.m_eSocketType == CGenericSocket::socketTypeUnspecified )
            {
                TRACE( _T( "CSubsystem::CDataHandler::Create(), Socket type is unspecified.\n" ) );
            }
            else if ( ! m_pGenericSocket->Configure( SocketAttributes.m_eSocketType,
                                                            static_cast<unsigned short>( SocketAttributes.m_uiPort ),
                                                                false,
                                                                    DataAvailableCallback,
                                                                        this,
                                                                            ulSendAddress ) )
            {
                TRACE( _T( "CSubsystem::CDataHandler::Create(), m_pGenericSocket->Configure() failed.\n" ) );
            }
            else
            {
                bSuccess = true;
            }

            ASSERT( bSuccess );
        }
    }
    catch ( ... )
    {
         bSuccess = false;
    }

    return bSuccess;
}

bool CSubsystem::CDataHandler::DataAvailableCallback(   const void              *pvthis,
                                                        const BYTE              *pbyData,
                                                        const unsigned long     &rulBytes,
                                                        const unsigned long     &rulTimestamp )
{
    bool bSuccess = false;

    try
    {
        CSubsystem::CDataHandler *pthis = static_cast<CSubsystem::CDataHandler *>( const_cast<void *>( pvthis ) );
        ASSERT( pthis != NULL );

        if ( pthis != NULL )
        {
            if ( ! pthis->HandleReceivedData( pbyData, rulBytes, rulTimestamp ) )
            {
                TRACE( _T( "CSubsystem::CDataHandler::DataAvailableCallback(), pthis->HandleReceivedData() failed.\n" ) );
            }
        }

        bSuccess = true;
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}

bool CSubsystem::CDataHandler::HandleReceivedData(  BYTE const          *pby7kData,
                                                    const unsigned long &rulBytes,
                                                    const unsigned long &rulTimestamp )
{
    try
    {
        if ( rulBytes == 0UL )
        {
            // Nothing to do.
        }
        else if ( pby7kData != NULL )
        {
            CSubsystem &rParent      = Parent();
            const int   iSensorIndex = rParent.SensorIndex();

            if ( ! m_MessageProcessor.ValidateAndQueueRecord( pby7kData, rulBytes, rulTimestamp, iSensorIndex ) )
            {
                rParent.MessageError();

                if ( m_bReplyToHost )
                {
                    // Format a NAK message and send to the remote client.

                    ASSERT( m_pParent != NULL );
                    const int iSensorIndex = m_pParent->SensorIndex();

                    C7kAcknowlege Message;

                    if ( ! Message.Encode( C7kAcknowlege::statusRejected, 0UL, iSensorIndex, false ) )
                    {
                        TRACE( _T( "CSubsystem::MessageError(), StatusMessage.Encode() failed.\n" ) );
                    }
                    else if ( ! Send( static_cast<BYTE *>( Message ), static_cast<unsigned long>( Message ) ) )
                    {
                        TRACE( _T( "CSubsystem::MessageError(), m_pHandler->Send() failed.\n" ) );
                    }
                }
            }
        }
    }
    catch ( ... )
    {
        TRACE( _T( "CSubsystem::CDataHandler::HandleReceivedData(), Unspecified exception caught.\n" ) );
    }

    // Always return true, for now.

    return true;
}

bool CSubsystem::CDataHandler::Send( BYTE * const         pbyStream,
                                     const unsigned long &rulNumBytes )
{
    if ( m_pGenericSocket != NULL )
    {
        return m_pGenericSocket->Send( pbyStream, rulNumBytes );
    }

    return false;
}

inline
bool CSubsystem::CDataHandler::IsHealthy( void )
{
    if ( m_pGenericSocket != NULL )
    {
        return m_pGenericSocket->IsHealthy();
    }

    return false;
}

///////////////////////////////////////////////////////////////////////////////
// CStoredAttributes internal helper (anonymous namespace).

CStoredAttributes::CStoredAttributes( void )
                  :m_szDefaultAddress                                 ( "127.0.0.1" ),
                    m_uiDefaultPort                                    ( 4888U ),
                    m_unDefaultProtocolVersion                         ( un7kSocketProtocolVersion_c ),
                    m_lpszRESON7kGenericRegistryKey                    ( _T( ".DEFAULT\\SOFTWARE\\RESON\\6046\\RESON7kGeneric" ) ),
                    m_lpsz6046RESON7kGenericNameRegistryKey            ( _T( "Name" ) ),
                    m_lpsz6046RESON7kGenericAddressRegistryKey         ( _T( "Address" ) ),
                    m_lpsz6046RESON7kGenericPortRegistryKey            ( _T( "Port" ) ),
                    m_lpsz6046RESON7kGenericTypeRegistryKey            ( _T( "Type" ) ),
                    m_lpsz6046RESON7kGenericProtocolVersionRegistryKey ( _T( "ProtocolVersion" ) ),
                    m_lpsz6046RESON7kGenericHostReplyRegistryKey       ( _T( "HostReply" ) )
{
    m_bHostReply  = false;
    m_sName       = "";
    m_sAddress    = m_szDefaultAddress;
    m_uiPort      = m_uiDefaultPort;
    m_unVersion   = m_unDefaultProtocolVersion;
    m_eSocketType = CGenericSocket::socketTypeUnspecified;
}

CStoredAttributes::~CStoredAttributes( void )
{
    m_sName.Empty();
    m_sAddress.Empty();

    m_bHostReply  = false;
    m_uiPort      = 0U;
    m_unVersion   = 0;
    m_eSocketType = CGenericSocket::socketTypeUnspecified;
}

void CStoredAttributes::SetDefaults( void )
{
    m_bHostReply  = false;
    m_sAddress    = m_szDefaultAddress;
    m_uiPort      = m_uiDefaultPort;
    m_unVersion   = m_unDefaultProtocolVersion;
    m_eSocketType = CGenericSocket::socketTypeUnspecified;
}

bool CStoredAttributes::QueryString( CRegKey &rKey, LPCTSTR lpszSubkey, CString &rsReadValue )
{
    rsReadValue.Empty();

    CString sValue;
    DWORD   dwLength = _MAX_PATH;
    long    lResult = rKey.QueryValue( sValue.GetBuffer(_MAX_PATH), lpszSubkey, &dwLength );
    sValue.ReleaseBuffer();

    if ( ( lResult == ERROR_SUCCESS ) && ( ! sValue.IsEmpty() ) )
    {
        rsReadValue = sValue;
        return true;
    }

    return false;
}

bool CStoredAttributes::Retrieve( const int &riSensorIndex )
{
    bool bRetrieved = true;

    CString sSubKey;
    sSubKey.Format( "%s\\Interface_%d", m_lpszRESON7kGenericRegistryKey, riSensorIndex );
    CRegKey Key;
    Key.Attach( HKEY_USERS );

    if ( Key.Open( HKEY_USERS, sSubKey ) == ERROR_SUCCESS )
    {
        CString sValue;
        DWORD   dwValue;

        if ( QueryString( Key, m_lpsz6046RESON7kGenericNameRegistryKey, sValue ) )
        {
            m_sName = sValue;
        }

        if ( QueryString( Key, m_lpsz6046RESON7kGenericAddressRegistryKey, sValue ) )
        {
            m_sAddress = sValue;
        }

        if ( Key.QueryValue( dwValue, m_lpsz6046RESON7kGenericProtocolVersionRegistryKey ) == ERROR_SUCCESS )
        {
            m_unVersion = static_cast<unsigned short>( dwValue );
        }

        if ( Key.QueryValue( dwValue, m_lpsz6046RESON7kGenericPortRegistryKey ) == ERROR_SUCCESS )
        {
            m_uiPort = static_cast<unsigned int>( dwValue );
        }

        if ( Key.QueryValue( dwValue, m_lpsz6046RESON7kGenericTypeRegistryKey ) == ERROR_SUCCESS )
        {
            m_eSocketType = static_cast<CGenericSocket::ESOCKETTYPE>( dwValue );
        }

        if ( Key.QueryValue( dwValue, m_lpsz6046RESON7kGenericHostReplyRegistryKey ) == ERROR_SUCCESS )
        {
            m_bHostReply = ( dwValue != 0UL );
        }

        Key.Close();
    }

    return bRetrieved;
}

