//
//  Copyright © 2001 - 2002, 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:   7kCommand.cpp
//
//  Project:    6046.
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      

#include "StdAfx.h"
#include "7kCommand.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// C7kCommand class implementation.

C7kCommand::C7kCommand( void )
           :C7kProtocol(),
            m_ulRecordHeaderSize( sizeof( RECORDHEADER7K ) ),
            m_ulRecordType( 11000 )
{
    ManageDllState_m();

    m_DecodedCommand.Empty();
}

C7kCommand::C7kCommand( const C7kCommand &rRhs )
           :C7kProtocol( rRhs ),
            m_ulRecordHeaderSize( sizeof( RECORDHEADER7K ) ),
            m_ulRecordType( 11000 )
{
    ManageDllState_m();

    m_DecodedCommand = rRhs.m_DecodedCommand;
}

C7kCommand & C7kCommand::operator = ( const C7kCommand &rRhs )
{
    ManageDllState_m();

    m_DecodedCommand = rRhs.m_DecodedCommand;

    return *this;
}

C7kCommand::~C7kCommand( void )
{
    ManageDllState_m();

    m_DecodedCommand.Empty();
}

bool C7kCommand::Decode(    BYTE                *pbyStream,
                            const unsigned long &rulTotalBytes,
                            const bool          &rbUseChecksum,
                            int                 &riSensorIndex,
                            unsigned long       &rulCommand,
                            unsigned long       &rulAction )
{
    ManageDllState_m();

    bool bSuccess = false;  // Assume false for now.

    m_DecodedCommand.Empty();

    try
    {
        if ( rulTotalBytes == 0UL )
        {
            bSuccess = true;
        }
        else if ( pbyStream != NULL )
        {
            RECORDHEADER7K     *psRecordHeader    = NULL;
            BYTE               *pbyDynamicData    = NULL;
            unsigned long       ulDynamicBytes    = 0UL;

            // Pull the record out of the 7k record header.

            if ( ! DecodeRecord( pbyStream, rulTotalBytes, rbUseChecksum, psRecordHeader, pbyDynamicData, ulDynamicBytes ) )
            {
                ThrowMessage_m( "Can't decode record" );
            }

            if ( ( pbyDynamicData == NULL ) || ( ulDynamicBytes == 0UL )  )
            {
                ThrowMessage_m( "Invalid dynamic data portion of message" );
            }

            if ( psRecordHeader->m_ulRecordType != m_ulRecordType )
            {
                ThrowMessage_m( "Unexpected record type" );
            }

            // Next decode the Record type header.

            RECORDTYPEHEADER *psRecordTypeHeader = reinterpret_cast<RECORDTYPEHEADER *>( pbyDynamicData );

            riSensorIndex = psRecordTypeHeader->m_iSensorIndex;
            rulCommand    = psRecordTypeHeader->m_ulCommand;
            rulAction     = psRecordTypeHeader->m_ulAction;

            // Finally, decode remainder of the record (optional).

            if ( psRecordTypeHeader->m_ulBytesToFollow > 0UL )
            {
                m_DecodedCommand = (LPCTSTR) ( pbyDynamicData + tagRECORDTYPEHEADER::Size() );

                if ( m_DecodedCommand.IsEmpty() )
                {
                    ThrowMessage_m( "Extended information is empty !!!" );
                }
            }

            bSuccess = true;
        }
        else
        {
            bSuccess = false;
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C7kCommand::Decode(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kCommand::Decode(), Unexpected exception caught\n" ) );
    }

    return bSuccess;
}

bool C7kCommand::Encode(    const int           &riSensorIndex,
                            const unsigned long &rulCommand,
                            const unsigned long &rulAction,
                            const bool          &rbUseChecksum,
                            const CString       &rExtended )
{
    ManageDllState_m();

    bool bSuccess = false;

    BYTE *pbyMessage = NULL;

    try
    {
        const unsigned long ulExtendedLength =  static_cast<unsigned long>(rExtended.GetLength());
        const unsigned long ulRecordSize     =  sizeof( RECORDHEADER7K ) +
                                                    tagRECORDTYPEHEADER::Size() +
                                                        ulExtendedLength +
                                                            Checksum7kSize_m();
                                                        
        pbyMessage = new BYTE [ ulRecordSize ];

        if ( pbyMessage == NULL )
        {
            ThrowMessage_m( "Failed to allocate space for command" );
        }

        RECORDHEADER7K *psHeader = reinterpret_cast<RECORDHEADER7K *>( pbyMessage );

        SetDefaultHeader( psHeader );

        // Explicitly assign parameters to the header based on the data to follow as appropriate.

        psHeader->m_ulSize                      = ulRecordSize;                                                   // u32 Size in bytes of this record from the start of the version field to the end of the Checksum. It includes the embedded data size.

        psHeader->m_ulOffsetToOptionalData      = 0UL;                                                            // Data offset          u32 Offset in bytes to optional data field from start of record. Zero implies no optional data.
        psHeader->m_ulOptionalDataIdentifier    = 0UL;                                                            // Data idenfitifer     u32 Identifier for optional data field. Zero for no optional field. This identifier is described with each record type.

        psHeader->m_ulRecordType                = m_ulRecordType;                                                 // u32 Unique identifier of indicating the type of data embedded in this record.
        psHeader->m_ulDeviceId                  = 0UL;                                                            // u32 Identifier of the device that this data pertains.
        psHeader->m_ulSubsystemId               = 0UL;                                                            // u32 Identifier for the device subsystem
        psHeader->m_ulDataSetNumber             = 0UL;                                                            // u32 Data set number.
        psHeader->m_ulRecordNumber              = 0UL;                                                            // u32 Sequential record counter.

        RECORDTYPEHEADER *psRTH = reinterpret_cast<RECORDTYPEHEADER *>( pbyMessage + sizeof( RECORDHEADER7K ) );

        psRTH->m_iSensorIndex       = riSensorIndex;
        psRTH->m_ulCommand          = rulCommand;
        psRTH->m_ulAction           = rulAction;
        psRTH->m_ulBytesToFollow    = ulExtendedLength;

        if ( ulExtendedLength > 0UL )
        {
            ::memcpy( pbyMessage + sizeof( RECORDHEADER7K ) + sizeof( RECORDTYPEHEADER ),
                        (LPCTSTR) rExtended,
                            ulExtendedLength );
        }

        bSuccess = EncodeRecord( psHeader,
                                    reinterpret_cast<BYTE *> (psRTH),
                                        sizeof( RECORDTYPEHEADER ) + ulExtendedLength,
                                            rbUseChecksum );
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C7kCommand::Encode(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kCommand::Encode(), unspecified exception caught\n" ) );
    }

    if ( pbyMessage != NULL )
    {
        delete [] pbyMessage;
        pbyMessage = NULL;
    }

    return bSuccess;
}

bool C7kCommand::Encode(    const int           &riSensorIndex,
                            const unsigned long &rulCommandId,
                            const unsigned long &rulAction,
                            const bool          &rbUseChecksum,
                            LPCTSTR             lpszFormat, ... )
{
    ManageDllState_m();

    bool bSuccess = false;      // Assume failure for now.

    try
    {
        CString Format;

        if ( lpszFormat != NULL )
        {
            va_list pArg;
            va_start( pArg, lpszFormat );
            Format.FormatV( lpszFormat, pArg );
            va_end( pArg );
        }

        bSuccess = Encode( riSensorIndex, rulCommandId, rulAction, rbUseChecksum, Format );
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C7kCommand::Encode(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kCommand::Encode(), unspecified exception\n" ) );
    }

    return bSuccess;
}

C7kCommand::operator BYTE * ( void ) const
{
    ManageDllState_m();

    ASSERT( m_pRecord.get() != NULL );

    return static_cast<BYTE *>(m_pRecord->GetAt( 0 ));
}

C7kCommand::operator unsigned long ( void ) const
{
    ManageDllState_m();

    return EncodedBytes();
}

CString C7kCommand::Command( void ) const
{
    ManageDllState_m();

    return m_DecodedCommand;
}

///////////////////////////////////////////////////////////////////////////////
// Internal private helpers.

inline
void C7kCommand::SetDefaultHeader( RECORDHEADER7K *psRecordHeader )
{
    // Set sensible defaults for the 7k command header.

    if ( psRecordHeader != NULL )
    {
        ::memset( psRecordHeader, 0x00, m_ulRecordHeaderSize );

        psRecordHeader->m_unVersion                 = 1;                                                              // u16 Version of this frame (e.g.: 1, 2, …)
        psRecordHeader->m_unOffset                  = static_cast<unsigned short>( m_ulRecordHeaderSize - 
                                                        ( 2 * sizeof( unsigned short ) ) );                         // u16 Offset in bytes from the start of the sync pattern to the start of the DATA SECTION. This allows for expansion of the header whilst maintaining backward compatibility.
        psRecordHeader->m_ulSyncPattern             = 0x0000ffff;                                                     // u32 0x0000FFFF
        psRecordHeader->m_ulSize                    = 0UL;                                                            // u32 Size in bytes of this record from the start of the version field to the end of the Checksum. It includes the embedded data size.

        psRecordHeader->m_ulOffsetToOptionalData    = 0UL;                                                            // Data offset          u32 Offset in bytes to optional data field from start of record. Zero implies no optional data.
        psRecordHeader->m_ulOptionalDataIdentifier  = 0UL;                                                            // Data idenfitifer     u32 Identifier for optional data field. Zero for no optional field. This identifier is described with each record type.

        SetDateTime( psRecordHeader );

        psRecordHeader->m_ulRecordType              = 0UL;                                                            // u32 Unique identifier of indicating the type of data embedded in this record.
        psRecordHeader->m_ulDeviceId                = 0UL;                                                            // u32 Identifier of the device that this data pertains.
        psRecordHeader->m_ulSubsystemId             = 0UL;                                                            // u32 Identifier for the device subsystem
        psRecordHeader->m_ulDataSetNumber           = 0UL;                                                            // u32 Data set number.
        psRecordHeader->m_ulRecordNumber            = 0UL;                                                            // u32 Sequential record counter.

        psRecordHeader->m_i64PreviousRecord         = (__int64) -1;                                                   // i64 Pointer to the previous record of the same type (in bytes from start of file). This is an optional field for files and shall be -1 if not used.
        psRecordHeader->m_i64NextRecord             = (__int64) -1;                                                   // i64 Pointer to the next record of the same type in bytes from start of file. This is an optional field for files and shall be -1 if not used.
        psRecordHeader->m_unFlags                   = 0x0000;                                                         // u16 BIT FIELD: Bit 1 - Valid Checksum
    }
}

inline
void C7kCommand::SetDateTime( RECORDHEADER7K *psRecordHeader )
{
    SYSTEMTIME  sSystemTime;

    ::GetLocalTime( &sSystemTime );

    psRecordHeader->m_sTime7k.m_unYear    = (unsigned short) sSystemTime.wYear;                             // u16 0 - 65535
    psRecordHeader->m_sTime7k.m_unDay     = (unsigned short) sSystemTime.wDay;                              // u16 1 - 366
    psRecordHeader->m_sTime7k.m_ucHours   = (unsigned char) sSystemTime.wHour;                              // u8  0 - 23
    psRecordHeader->m_sTime7k.m_ucMinutes = (unsigned char) sSystemTime.wMinute;                            // u8  0 - 59
    psRecordHeader->m_sTime7k.m_fSeconds  = ( (float) sSystemTime.wSecond + 
                                                0.001f * (float) sSystemTime.wMilliseconds );               // f32 0.000000 - 59.000000
}
