//
//  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 "NetUtils.h"
#include "7kAcknowlege.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

///////////////////////////////////////////////////////////////////////////////
// C7kAcknowlege class implementation.

C7kAcknowlege::C7kAcknowlege( void )

              :C7kProtocol(),

               m_ulRecordHeaderSize( sizeof( RECORDHEADER7K ) ),
               m_ulRecordType( 11001UL )
{
    ManageDllState_m();
}

C7kAcknowlege::~C7kAcknowlege( void )
{
    ManageDllState_m();
}

C7kAcknowlege::C7kAcknowlege( const C7kAcknowlege &rRhs )

              :C7kProtocol(),

               m_ulRecordHeaderSize( sizeof( RECORDHEADER7K ) ),
               m_ulRecordType( 11001UL )
{
    ManageDllState_m();

    UNREFERENCED_PARAMETER( rRhs );
}

C7kAcknowlege & C7kAcknowlege::operator = ( const C7kAcknowlege &rRhs )
{
    ManageDllState_m();

    UNREFERENCED_PARAMETER( rRhs );

    return *this;
}

C7kAcknowlege::operator BYTE * ( void ) const
{
    ManageDllState_m();

    ASSERT( m_pRecord.get() != NULL );

    return static_cast<BYTE *>(m_pRecord->GetAt( 0 ));
}

C7kAcknowlege::operator unsigned long ( void ) const
{
    ManageDllState_m();

    return EncodedBytes();
}

bool C7kAcknowlege::Encode( const ESTATUS        &reStatus,
                            const unsigned long  &rulCommandId,
                            const unsigned long  &rulSensorId,
                            const bool           &rbUseChecksum )
{
    ManageDllState_m();

    bool bSuccess = false;

    BYTE *pbyMessage = NULL;

    try
    {
        const unsigned long ulRecordSize =  sizeof( RECORDHEADER7K ) +
                                                tagRECORDTYPEHEADER::Size() +
                                                    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 *psRecordTypeHeader = reinterpret_cast<RECORDTYPEHEADER *>( pbyMessage + sizeof( RECORDHEADER7K ) );

        psRecordTypeHeader->m_ulCommandId = rulCommandId;
        psRecordTypeHeader->m_ulSensorId  = rulSensorId;
        psRecordTypeHeader->m_ulStatus    = static_cast<unsigned long>( reStatus );

        bSuccess = EncodeRecord( psHeader, reinterpret_cast<BYTE *> (psRecordTypeHeader), 0UL, rbUseChecksum );
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C7kAcknowlege::Encode(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kAcknowlege::Encode(), unspecified exception caught\n" ) );
    }

    if ( pbyMessage != NULL )
    {
        delete [] pbyMessage;
        pbyMessage = NULL;
    }

    return bSuccess;
}

bool C7kAcknowlege::Decode( BYTE                *pbyStream,
                            const unsigned long &rulTotalBytes,
                            const bool          &rbUseChecksum,
                            ESTATUS             &reStatus,
                            unsigned long       &rulCommandId,
                            unsigned long       &rulSensorId )
{
    ManageDllState_m();

    bool bSuccess = false;  // Assume false for now.

    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 );

            rulCommandId = psRecordTypeHeader->m_ulCommandId;
            rulSensorId  = psRecordTypeHeader->m_ulSensorId;
            reStatus     = static_cast<ESTATUS>( psRecordTypeHeader->m_ulStatus );

            bSuccess = true;
        }
        else
        {
            bSuccess = false;
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C7kAcknowlege::Decode(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kAcknowlege::Decode(), unexpected exception caught\n" ) );
    }

    return bSuccess;
}


///////////////////////////////////////////////////////////////////////////////
// Internal private helpers.

inline
void C7kAcknowlege::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 C7kAcknowlege::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
}

