//
//  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:   RESON7kGeneric.cpp : Defines the initialization routines for the DLL.
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    Implements the main module interface for the RESON7kGeneric DLL and its
//              exported C interface functions.
//
//  Notes:      1)  If this DLL is dynamically linked against the MFC
//                  DLLs, any functions exported from this DLL which
//                  call into MFC must have the AFX_MANAGE_STATE macro
//                  added at the very beginning of the function.
//                  The macro ManageDllState_m() is defined for this purpose (see below).
//
//                  See MFC Technical Notes 33 and 58 for additional info.
//

#include "StdAfx.h"
#include "RESON7kGeneric.h"

#include "..\..\Include\Exports.h"
#include "..\..\Include\SurveyEventTypes.h"

#include "..\..\..\Utils\NetUtils\7kProtocol.h"

#include <atlbase.h>

#ifdef _DEBUG
#pragma comment( lib, "..\\..\\..\\Utils\\NetUtils\\Debug\\NetUtils.lib" )
#else
#pragma comment( lib, "..\\..\\..\\Utils\\NetUtils\\Release\\NetUtils.lib" )
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define GetAppOrAbort_m( text )     CRESON7kGenericApp * pApp = GetApp_m();                                     \
                                                                                                                \
                                    if ( pApp == NULL )                                                         \
                                    {                                                                           \
                                        TRACE( _T( "File: " __FILE__ ", Line: %d: " text "\n" ), __LINE__ );    \
                                        ASSERT( false );                                                        \
                                        return ERROR_CODE_FAILURE;                                              \
                                    }

namespace                   // Unnamed namespace to guarantee internal linkage.
{
    const int           iMaxAddressSize_c                   = 16;
    const double        dModuleVersion_c                    = 3.0000;

    const unsigned long ulDefaultPort_c                     = 4888UL;           // (7000 & 6046).
    LPCTSTR             lpszDefaultAddress_c                = _T( "127.0.0.1" );

    LPCTSTR             lpsz6046RESON7kGenericRegistryKey_c = _T( ".DEFAULT\\SOFTWARE\\RESON\\6046\\RESON7kGeneric" );
    LPCTSTR             lpsz6046RESON7kGenericAddressKey_c  = _T( "Address" );
    LPCTSTR             lpsz6046RESON7kGenericPortKey_c     = _T( "Port" );

}                           // End unnamed namespace.

/////////////////////////////////////////////////////////////////////////////
// CRESON7kGenericApp class implementation.

BEGIN_MESSAGE_MAP(CRESON7kGenericApp, CWinApp)
    //{{AFX_MSG_MAP(CRESON7kGenericApp)
        // NOTE - the ClassWizard will add and remove mapping macros here.
        //    DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

CRESON7kGenericApp::CRESON7kGenericApp( void )
                   :CSensorInterface()
{
    m_ulPort  = ulDefaultPort_c;
    m_Address = lpszDefaultAddress_c;
}

CRESON7kGenericApp::~CRESON7kGenericApp( void )
{
    m_ulPort  = ulDefaultPort_c;
    m_Address = lpszDefaultAddress_c;
}

CRESON7kGenericApp theApp;                              // Our single DLL application object.

BOOL CRESON7kGenericApp::InitInstance( void )
{
    // Main initialization method.

    if ( ! AfxSocketInit() )
    {
        TRACE( _T( "CRESON7kGenericApp::InitInstance(), Windows sockets initialization failure\n" ) );
        ASSERT( false );
        return FALSE;
    }

    // Now set the modules version and restore any one-time parameters from the registry.

    Version( dModuleVersion_c );

    SettingsFromRegistry();

    return TRUE;
}

int CRESON7kGenericApp::ExitInstance( void )
{
    // TODO: Add per instance clean up here.


    return CWinApp::ExitInstance();
}

CRESON7kGenericApp * CRESON7kGenericApp::This( void )
{
    return ( static_cast<CRESON7kGenericApp *>( CSensorInterface::This() ) );
}

void CRESON7kGenericApp::SettingsFromRegistry( void )
{
    m_Critical.Enter();

    try
    {
        if ( ! m_bRetrievedSonarSettings )
        {
            CRegKey Key;
            DWORD   dwValue = 0UL;

            Key.Attach( HKEY_USERS );

            if ( Key.Open( HKEY_USERS, lpsz6046RESON7kGenericRegistryKey_c ) == ERROR_SUCCESS )
            {
                DWORD dwAddressLength = static_cast<DWORD>( iMaxAddressSize_c );

                CString Address;
                LONG lSuccessCode = Key.QueryValue( Address.GetBuffer( iMaxAddressSize_c ), lpsz6046RESON7kGenericAddressKey_c, &dwAddressLength );
                Address.ReleaseBuffer();

                if ( ( lSuccessCode == ERROR_SUCCESS ) && ( dwAddressLength > 0UL ) )
                {
                    m_Address = Address;
                    ASSERT( ! m_Address.IsEmpty() );
                }

                if ( Key.QueryValue( dwValue, lpsz6046RESON7kGenericPortKey_c ) == ERROR_SUCCESS )
                {
                    m_ulPort = static_cast<unsigned long>( dwValue );
                }

                Key.Close();
            }

            m_bRetrievedSonarSettings = true;
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        TRACE( _T( "CRESON7kGenericApp::SonarSettingsFromRegistry(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        TRACE( _T( "CRESON7kGenericApp::SonarSettingsFromRegistry(), unspecified exception caught\n" ) );
    }

    m_Critical.Leave();
}

CString CRESON7kGenericApp::Address( void ) const
{
    m_Critical.Enter();
    CString Address = m_Address;
    m_Critical.Leave();

    return Address;
}

unsigned long CRESON7kGenericApp::Port( void ) const
{
    m_Critical.Enter();
    unsigned long ulPort = m_ulPort;
    m_Critical.Enter();

    return ulPort;
}

CSensorList<CSubsystem> & CRESON7kGenericApp::SensorList( void ) const
{
    return m_SensorList;
}

///////////////////////////////////////////////////////////////////////////////
// Exported 6046 interface functions.

extern "C" __declspec( dllexport )
int WINAPI Startup( SENSORINFO     *psSensorInfo,
                    DWORD           dwThreadId,
                    UINT            uiDataReadyMessageId,
                    UINT            uiReplyReadyMessageId,
                    void           *vpfnLog,
                    void           *vpCriticalParameters )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[RESON7kGeneric] Startup(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;                              // Assume failure for now.

    try
    {
        // The sensor index is assigned by the 6046 and is unique to each sensor in the suite.

        ASSERT( ( psSensorInfo != NULL ) && ( psSensorInfo->m_iSensorIndex >= 0 ) );

        const int iSensorIndex = psSensorInfo->m_iSensorIndex;
        int       iRefCount;

        if ( ( iRefCount = pApp->AddSensorRef() ) == 1 )
        {
            pApp->EventLog( vpfnLog );

#if 1
#pragma CompileMessage_m( "TODO: RE-ENABLE CRITICAL PARAMETER HANDLING ONCE MECHAMISM IS COMPLETED" )
            DBG_UNREFERENCED_PARAMETER( vpCriticalParameters );
#else
            pApp->CriticalParameterTable( vpCriticalParameters );
#endif
        }

        CSensorList<CSubsystem> &rSensorList = pApp->SensorList();
        CSubsystem *pSensor                  = rSensorList.AddSensor( iSensorIndex );
        ASSERT( pSensor != NULL );

        // Sensor id is a 6046 specific code for each sensor (see ESENSORID of processinf.h) whereas
        // the device ID is the 7k device ID per the 7k ICD. They shouldn't be confused.

        psSensorInfo->m_iPriority            = 1;
        psSensorInfo->m_iFilter              = 0;
        psSensorInfo->m_iSensorId            = sensorIdRESON7kGeneric;           // 6046 specific index for this device type.
        psSensorInfo->m_iMediaType           = mediaTypeInternal;
        psSensorInfo->m_iAccepts7kRecords    = 0;                           // Sensor doesn't accept routed (inbound) 7k messages.

        // Startup the subsystem.

        if ( ! pSensor->Startup( iSensorIndex, dwThreadId, uiDataReadyMessageId, uiReplyReadyMessageId, psSensorInfo->m_iActivateOnStart ) )
        {
            pApp->SensorList().RemoveSensor( iSensorIndex );
            ThrowMessage_m( "Sensor startup failure" );
        }

        pApp->LogEvent( eventLogAdvisory, iUnspecified_c, iUnspecified_c, "[RESON7kGeneric] Startup (Index: %d, AutoStart: %d)", iSensorIndex, psSensorInfo->m_iActivateOnStart );

        // Specific sensor type may be unknown until the sonar has started. So, retrieve the specific sonar type and populate the 
        // remaining fields after this. Remember: psSensorInfo->m_iDeviceId corresponds to the 7k device ID (per the device
        // ID table of the 7k data format ICD).

        psSensorInfo->m_ulDeviceId  = deviceIdUnknown;
        psSensorInfo->m_iSensorType = sensorTypeRAW;
        strcpy( &psSensorInfo->m_szName[ 0 ], "Generic" );

        iSuccess = ERROR_CODE_SUCCESS;
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, Startup() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, Startup(), unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI Shutdown( int iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[RESON7kGeneric] Shutdown(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;      // Assume failure for now.

    try
    {
        pApp->LogEvent( eventLogAdvisory,
                            iUnspecified_c,
                                iUnspecified_c,
                                    "[RESON7kGeneric] Shutdown (Index: %d).", iSensorIndex );


        CSensorList<CSubsystem>  &rSensorList = pApp->SensorList();
        CSubsystem *pSensor = rSensorList.GetSensor( iSensorIndex );

        if ( pSensor == NULL )
        {
            ThrowMessage_m( "pSensor is NULL" );
        }

        // Go ahead and shutdown. Here we assume success then preserve a fail code for return
        // if one is detected. We don't through an eception since we want the successive steps
        // to follow. Eg. remove and disconect even if a sensor shutdown has failed.

        iSuccess = ERROR_CODE_SUCCESS;

        if ( ! pSensor->Shutdown() )
        {
            iSuccess = ERROR_CODE_FAILURE;
        }

        if ( ! rSensorList.RemoveSensor( iSensorIndex ) )
        {
            iSuccess = ERROR_CODE_FAILURE;
        }

        if ( pApp->ReleaseSensorRef() == 0 )
        {
            pApp->CriticalParameterTable( NULL );
            pApp->EventLog( NULL );
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, Startup() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "Shutdown(), RESON7kGeneric DLL - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI Identify( SENSORINFO *psSensorInfo, int iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[RESON7kGeneric] Identify(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;      // Assume failure for now.

    try
    {
        pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[RESON7kGeneric] Identify() request." );

        CSensorList<CSubsystem> &rSensorList = pApp->SensorList();
        CSubsystem *pSensor = rSensorList.GetSensor( iSensorIndex );

        if ( pSensor == NULL )
        {
            ThrowMessage_m( "pSensor is NULL" );
        }

        if ( ! pSensor->Identify( psSensorInfo ) )
        {
            ThrowMessage_m( "pSensor->Identify() failed\n" );
        }

        iSuccess = ERROR_CODE_SUCCESS;
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, Identify() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, Identify() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI Load(    BYTE           *pbyData,
                    unsigned long  *pulSize,
                    unsigned long  *pulTimestamp,
                    int             iSensorIndex )
{
    ManageDllState_m();

    // NB: Load() is redundant as sensors now load their data directly into the PLC's
    // shared memory.

    UNREFERENCED_PARAMETER( pbyData );
    UNREFERENCED_PARAMETER( pulSize );
    UNREFERENCED_PARAMETER( pulTimestamp );
    UNREFERENCED_PARAMETER( iSensorIndex );

    return ERROR_CODE_FAILURE;
}

extern "C" __declspec( dllexport )
double WINAPI GetModuleVersion( void )
{
    ManageDllState_m();

    double dVersion = 0.00;

    CRESON7kGenericApp *pApp = GetApp_m();

    ASSERT( pApp != NULL );

    if ( pApp != NULL )
    {
        dVersion = pApp->Version();
    }

    pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[RESON7kGeneric] GetModuleVersion(), Version is %.2lf", dVersion );

    return dVersion;
}

extern "C" __declspec( dllexport )
int WINAPI SendCommand( int     iSensorIndex,
                        int     iClientIndex,
                        char   *pszCommand )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[RESON7kGeneric] SendCommand(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        pApp->LogEvent( eventLogAdvisory, iUnspecified_c, iUnspecified_c, "[RESON7kGeneric] Sensor: %d, Command: %s", iSensorIndex, pszCommand );

        CSensorList<CSubsystem>  &rSensorList = pApp->SensorList();
        CSubsystem *pSensor     = rSensorList.GetSensor( iSensorIndex );

        if ( pSensor == NULL )
        {
            ThrowMessage_m( "pSensor is NULL" );
        }

        if ( ! pSensor->SendCommand( iClientIndex, pszCommand ) )
        {
            ThrowMessage_m( "pSensor->SendCommand() failed" );
        }

        iSuccess = ERROR_CODE_SUCCESS;
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, SendCommand() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, SendCommand() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI RetrieveStatus(  BYTE           *pbyData,
                            unsigned long  *pulSize,
                            unsigned long  *pulTimestamp,
                            int            *piClientIndex,
                            int             iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[RESON7kGeneric] RetrieveStatus(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        CSensorList<CSubsystem>  &rSensorList = pApp->SensorList();
        CSubsystem *pSensor     = rSensorList.GetSensor( iSensorIndex );

        if ( pSensor == NULL )
        {
            ThrowMessage_m( "pSensor is NULL" );
        }

        // Here, the fail code simply means there is no more queued data available.

        iSuccess = ( pSensor->RetrieveStatus( pbyData, pulSize, pulTimestamp, piClientIndex ) ) ? ERROR_CODE_SUCCESS : ERROR_CODE_FAILURE;
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, RetrieveStatus() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, RetrieveStatus() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI RouteMessage(    BYTE           *pbyMessage,
                            unsigned long   ulSize,
                            unsigned long   ulTimeStamp,
                            int             iSocketIndex,
                            int             iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[RESON7kGeneric] RouteMessage(), pApp is NULL" );

    UNREFERENCED_PARAMETER( iSocketIndex );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        pApp->LogEvent( eventLogAdvisory, iUnspecified_c, iUnspecified_c, "[RESON7kGeneric] Routed Message, Sensor: %d, Size: %lu", iSensorIndex, ulSize );

        CSensorList<CSubsystem>  &rSensorList = pApp->SensorList();
        CSubsystem *pSensor     = rSensorList.GetSensor( iSensorIndex );

        if ( pSensor == NULL )
        {
            ThrowMessage_m( "pSensor is NULL" );
        }

        if ( ! pSensor->RouteMessage( pbyMessage, ulSize, ulTimeStamp ) )
        {
            ThrowMessage_m( "pSensor->RouteMessage() failed" );
        }

        iSuccess = ERROR_CODE_SUCCESS;
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL RouteMessage(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, RouteMessage() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI ResetSensor( int iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[RESON7kGeneric] ResetSensor(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[RESON7kGeneric] ResetSensor(), Sensor: %d", iSensorIndex );

        CSensorList<CSubsystem>  &rSensorList = pApp->SensorList();
        CSubsystem *pSensor     = rSensorList.GetSensor( iSensorIndex );

        if ( pSensor == NULL )
        {
            ThrowMessage_m( "pSensor is NULL" );
        }

        if ( ! pSensor->Reset() )
        {
            ThrowMessage_m( "pSensor->Reset() failed." );
        }

        iSuccess = ERROR_CODE_SUCCESS;
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, ResetSensor() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, ResetSensor() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI IsSensorHealthy( int iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[RESON7kGeneric] IsSensorHealthy(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[RESON7kGeneric] IsSensorHealthy(), Sensor: %d", iSensorIndex );

        iSuccess = ERROR_CODE_SUCCESS;

        DBG_UNREFERENCED_PARAMETER( iSensorIndex );

        CSensorList<CSubsystem>  &rSensorList = pApp->SensorList();
        CSubsystem *pSensor     = rSensorList.GetSensor( iSensorIndex );

        if ( pSensor == NULL )
        {
            ThrowMessage_m( "pSensor is NULL" );
        }

        // Here the fail code means that the sensor has failed its own gross binary health check.

        iSuccess = ( pSensor->IsSensorHealthy() ) ? ERROR_CODE_SUCCESS : ERROR_CODE_FAILURE;

        if ( iSuccess != ERROR_CODE_SUCCESS )
        {
            pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[RESON7kGeneric] IsSensorHealthy(), Sensor is unhealthy" );
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, IsSensorHealthy() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "RESON7kGeneric DLL, IsSensorHealthy() - unspecified exception\n" ) );
    }

    return iSuccess;
}

