//
//  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:   6046RemoteCommand.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    Parse incoming commands for the 7kSonar.
//
//  Notes:      
//

#include "StdAfx.h"
#include "6046RemoteCommand.h"
#include <stdlib.h>

#define MAP_COMMAND( CommandString, CommandId ) m_Commands[ CommandString ] = CommandId;

//////////////////////////////////////////////////////////////////////
// C6046RemoteCommand class implementation -- starting with publics.

C6046RemoteCommand::C6046RemoteCommand( void )
{
    try
    {
        m_eCommand = commandNone;
        m_ParameterList.clear();
        m_Commands.clear();

        MAP_COMMAND( "Range",                commandRange                    );
        MAP_COMMAND( "PulseFiles",           commandPulseFiles               );
        MAP_COMMAND( "Pulse",                commandPulse                    );
        MAP_COMMAND( "Ping"    ,             commandPing                     );
        MAP_COMMAND( "TxPower",              commandTxPower                  );
        MAP_COMMAND( "RxGain",               commandRxGain                   );
        MAP_COMMAND( "Duration",             commandDuration                 );
        MAP_COMMAND( "DefaultState",         commandDefaultState             );
        MAP_COMMAND( "QueryParameters",      commandQueryParameters          );
        MAP_COMMAND( "QueryCapabilities",    commandQueryCapabilities        );
        MAP_COMMAND( "QueryRemoteSettings",  commandQuerySonarRemoteSettings );
        MAP_COMMAND( "PulseWidth",           commandPulseWidth               );
        MAP_COMMAND( "RangeDepthFilters",    commandRangeDepthFilters        );
        MAP_COMMAND( "BottomDetectFlags",    commandBottomDetectFlags        );
        MAP_COMMAND( "MaxPingRate",          commandMaxPingRate              );
        MAP_COMMAND( "AbsorptionCoef",       commandAbsorptionCoef           );
        MAP_COMMAND( "SoundSpeed",           commandSoundSpeed               );
        MAP_COMMAND( "SpreadingLoss",        commandSpreadingLoss            );
    }
    catch ( ... )
    {
        TRACE( _T( "C6046RemoteCommand::C6046RemoteCommand(), Unspecified exception caught.\n" ) );
    }
}

C6046RemoteCommand::~C6046RemoteCommand( void )
{
    __TRY
    {
        m_ParameterList.clear();
        m_Commands.clear();
    }
    __FINALLY
    {
        m_eCommand = commandNone;
    }
    __ENDFINALLY
}

bool C6046RemoteCommand::Decode( const char *pszCommand )
{
    bool bSuccess = false;

    try
    {
        m_eCommand = commandNone;
        m_ParameterList.clear();

        CString RemoteCommand( pszCommand );

        if ( RemoteCommand.IsEmpty() )
        {
            bSuccess = true;            // Nothing to do.
        }
        else
        {
            CString Keyword( pszCommand );

            int iParamPos = Keyword.Find( "=", 0 );

            if ( iParamPos != -1 )
            {
                Keyword = Keyword.Left( iParamPos - 1 );
                Keyword.TrimRight( " " );
            }

            CommandMapIterator_t pCommand = m_Commands.find( static_cast<LPCTSTR>( Keyword ) );

            if ( pCommand != m_Commands.end() )
            {
                m_eCommand = pCommand->second;
            }

            if ( iParamPos != -1 )
            {
                // Point just past the '=' and then trim off white space before assigning the parameter(s).

                int iLength = RemoteCommand.GetLength();

                iParamPos++;

                while ( ( iParamPos < iLength ) && isspace( RemoteCommand[ iParamPos ] ) )
                {
                    iParamPos++;
                }

                DecodeParameters( RemoteCommand, iParamPos, m_eCommand );
            }

            bSuccess = ( m_eCommand != commandNone );
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C6046RemoteCommand::Decode(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C6046RemoteCommand::Decode(), unspecified exception caught\n" ) );
    }

    return bSuccess;
}

bool C6046RemoteCommand::IsCommandValid( void ) const
{
    return ( m_eCommand != commandNone );
}

bool C6046RemoteCommand::AreParametersAvailable( void ) const
{
    return ( ! m_ParameterList.empty() );
}


C6046RemoteCommand::ECOMMANDID C6046RemoteCommand::Command( void ) const
{
    return m_eCommand;
}

C6046RemoteCommand::operator bool( void ) const
{
    ASSERT( ! m_ParameterList.empty() );

    return ( atoi( m_ParameterList.front().m_sParameter.c_str() ) != 0 );
}

C6046RemoteCommand::operator int( void ) const
{
    ASSERT( ! m_ParameterList.empty() );

    return ( atoi( m_ParameterList.front().m_sParameter.c_str() ) );
}

C6046RemoteCommand::operator float( void ) const
{
    ASSERT( ! m_ParameterList.empty() );

    return ( static_cast<float>( atof( m_ParameterList.front().m_sParameter.c_str() ) ) );
}

C6046RemoteCommand::operator const char * ( void ) const
{
    ASSERT( ! m_ParameterList.empty() );

    return ( m_ParameterList.front().m_sParameter.c_str() );
}

C6046RemoteCommand::operator unsigned long( void ) const
{
    ASSERT( ! m_ParameterList.empty() );

    return ( static_cast<unsigned long>( atol( m_ParameterList.front().m_sParameter.c_str() ) ) );
}

int C6046RemoteCommand::NumberOfParameters( void ) const
{
    return ( static_cast<int>( m_ParameterList.size() ) );
}

void C6046RemoteCommand::ResetParamRetrievalPoint( void )
{
    if ( m_ParameterList.empty() )
    {
        m_pRetrievalPoint = m_ParameterList.end();
    }
    else
    {
        m_pRetrievalPoint = m_ParameterList.begin();
    }
}

bool C6046RemoteCommand::GetNextParameter(  void                    *pvParam,
                                            const EPARAMETERTYPE    &eParameterType,
                                            const unsigned long     &rulMaxStringLength )
{
    bool bDataAvailable = false;

    if ( m_pRetrievalPoint != m_ParameterList.end() )
    {
        bDataAvailable = true;

        void *pvSource = const_cast<char *>( m_pRetrievalPoint->m_sParameter.c_str() );

        ASSERT( eParameterType == m_pRetrievalPoint->m_eParameterType );        // One would normally exptect to retrive the correct type.

        typedef union tagGENERALPARAM
        {
            bool            m_bValue;
            int             m_iValue;
            float           m_fValue;
            unsigned long   m_ulValue;
        }
        GENERALPARAM;

        GENERALPARAM        sParam = { 0 };

        switch ( eParameterType )
        {
            case paramTypeString:

                strncpy( static_cast<char *>( pvParam ), static_cast<char *>( pvSource ), rulMaxStringLength );
                break;

            case paramTypeBool:

                sParam.m_bValue = ( atoi( static_cast<char *>( pvSource ) ) != 0 );
                memcpy( pvParam, &sParam, sizeof( bool ) );
                break;

            case paramTypeInt:

                sParam.m_iValue = ( atoi( static_cast<char *>( pvSource ) ) );
                memcpy( pvParam, &sParam, sizeof( int ) );
                break;

            case paramTypeFloat:

                sParam.m_fValue = static_cast<float>( ( atof( static_cast<char *>( pvSource ) ) ) );
                memcpy( pvParam, &sParam, sizeof( float ) );
                break;

            case paramTypeUnsignedLong:

                sParam.m_ulValue = static_cast<unsigned long>( atol( static_cast<char *>( pvSource ) ) );
                memcpy( pvParam, &sParam, sizeof( unsigned long ) );
                break;

            default:

                bDataAvailable = false;
                break;
        }

        m_pRetrievalPoint++;
    }

    return bDataAvailable;
}

///////////////////////////////////////////////////////////////////////////////
// C6046RemoteCommand private members.

bool C6046RemoteCommand::DecodeParameters(  const CString    &rsCommand,
                                            const int        &riParamPos,
                                            const ECOMMANDID &reCommandId )
{
    bool        bSuccess         = true;
    CString     sParameterString = rsCommand.Right( rsCommand.GetLength() - riParamPos );
    CParameter  Parameter;

    switch ( reCommandId )
    {
        case commandRange:                                      // Single floats.
        case commandTxPower:
        case commandDuration:
        case commandPulseWidth:
        case commandMaxPingRate:
        case commandAbsorptionCoef:
        case commandSoundSpeed:
        case commandSpreadingLoss:

            Parameter.m_eParameterType = paramTypeFloat;
            Parameter.m_sParameter     = sParameterString;

            m_ParameterList.push_back( Parameter );

            break;

        case commandPulse:                                      // Single string.

            Parameter.m_eParameterType = paramTypeString;
            Parameter.m_sParameter     = sParameterString;

            m_ParameterList.push_back( Parameter );

            break;

        case commandPing:                                       // Single integer.
        case commandRxGain:

            Parameter.m_eParameterType = paramTypeInt;
            Parameter.m_sParameter     = sParameterString;

            m_ParameterList.push_back( Parameter );

            break;

        case commandPulseFiles:                                 // No parameters.
        case commandDefaultState:
        case commandQueryParameters:
        case commandQueryCapabilities:
        case commandQuerySonarRemoteSettings:

            // No parameters -- nothing to do.

            break;

        case commandRangeDepthFilters:                          // Four floats.

            {
                const int   iNumParameters           = 4;
                float       fValue[ iNumParameters ] = { 0 };

                if ( sscanf( static_cast<LPCTSTR>( sParameterString ),  "%f, %f, %f, %f", 
                                                                        &fValue[ 0 ],
                                                                        &fValue[ 1 ],
                                                                        &fValue[ 2 ],
                                                                        &fValue[ 3 ] ) == iNumParameters )
                {
                    Parameter.m_eParameterType = paramTypeFloat;
                    CString Param;

                    for ( int iParam = 0; iParam < iNumParameters; iParam++ )
                    {
                        Param.Format( "%.2f", fValue[ iParam ] );
                        Parameter.m_sParameter = static_cast<LPCTSTR>( Param );
                        m_ParameterList.push_back( Parameter );
                    }
                }
            }

            break;

        case commandBottomDetectFlags:                          // Single unsigned long.

            Parameter.m_eParameterType = paramTypeUnsignedLong;
            Parameter.m_sParameter     = sParameterString;

            m_ParameterList.push_back( Parameter );

            break;

        case commandNone:
        default:

            bSuccess = false;
            break;
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// C6046RemoteCommand::CParameter helper class implementation.

C6046RemoteCommand::CParameter::CParameter( void )
{
    m_sParameter     = "";
    m_eParameterType = paramTypeUnspecified;
}

C6046RemoteCommand::CParameter::~CParameter( void )
{
    m_sParameter     = "";
    m_eParameterType = paramTypeUnspecified;
}

