//
//  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:   7kStatus.cpp
//
//  Project:    6046.
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      

#include "StdAfx.h"
#include "NetUtils.h"
#include "7kStatus.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// C7kStatus class implementation.

C7kStatus::C7kStatus( void )

          :C7kProtocol(),

           m_ulRecordHeaderSize( sizeof( RECORDHEADER7K ) ),
           m_ulRecordType( 11002 )
{
    ManageDllState_m();

    m_Status.Empty();
}

C7kStatus::C7kStatus( const C7kStatus &rRhs )

          :C7kProtocol(),

           m_ulRecordHeaderSize( sizeof( RECORDHEADER7K ) ),
           m_ulRecordType( 11002 )
{
    ManageDllState_m();

    m_Status = rRhs.m_Status;
}

C7kStatus::~C7kStatus( void )
{
    ManageDllState_m();
    m_Status.Empty();
}

C7kStatus & C7kStatus::operator = ( const C7kStatus &rRhs )
{
    ManageDllState_m();

    m_Status = rRhs.m_Status;

    return *this;
}

C7kStatus::operator BYTE * ( void ) const
{
    ManageDllState_m();

    ASSERT( m_pRecord.get() != NULL );

    return static_cast<BYTE *>(m_pRecord->GetAt( 0 ));
}

C7kStatus::operator unsigned long ( void ) const
{
    ManageDllState_m();

    return EncodedBytes();
}

CString C7kStatus::Message( void ) const
{
    return m_Status;
}

bool C7kStatus::Decode( BYTE                *pbyStream,
                        const unsigned long &rulTotalBytes,
                        const bool          &rbUseChecksum,
                        EMESSAGETYPE        &reMessageType,
                        EMODE               &reMode,
                        int                 &riMessageId )
{
    ManageDllState_m();

    bool bSuccess = false;  // Assume false for now.

    m_Status.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 );

            reMessageType = static_cast<C7kStatus::tagEMESSAGETYPE>( psRecordTypeHeader->m_iMessageType );
            reMode        = static_cast<C7kStatus::tagEMODE>       ( psRecordTypeHeader->m_iMode );
            riMessageId   = psRecordTypeHeader->m_iMessageId;

            // Finally, decode remainder of the record (optional).

            if ( psRecordTypeHeader->m_ulMessageBytesToFollow > 0UL )
            {
                m_Status = (LPCTSTR) ( pbyDynamicData + tagRECORDTYPEHEADER::Size() );

                if ( m_Status.IsEmpty() )
                {
                    ThrowMessage_m( "Message is empty !!!" );
                }
            }

            bSuccess = true;
        }
        else
        {
            bSuccess = false;
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C7kStatus::Decode(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kStatus::Decode(), Unexpected exception caught\n" ) );
    }

    return bSuccess;
}

bool C7kStatus::Encode( const EMESSAGETYPE  &reMessageType,
                        const EMODE         &reMode,
                        const int           &riMessageId,
                        LPCTSTR              lpszMessage,
                        const bool          &rbUseChecksum )
{
    ManageDllState_m();

    bool bSuccess = false;

    BYTE *pbyMessage = NULL;

    try
    {
        const unsigned long ulMessageLength  =  ( lpszMessage == NULL ) ? 0UL : static_cast<unsigned long>( strlen( lpszMessage ) );
        const unsigned long ulRecordSize     =  sizeof( RECORDHEADER7K ) +
                                                    tagRECORDTYPEHEADER::Size() +
                                                        ulMessageLength +
                                                            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;
        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_iMessageType = static_cast<int>( reMessageType );
        psRecordTypeHeader->m_iMode        = static_cast<int>( reMode );
        psRecordTypeHeader->m_iMessageId   = riMessageId;

        if ( ulMessageLength > 0UL )
        {
            psRecordTypeHeader->m_ulMessageBytesToFollow = ulMessageLength;

            ::memcpy( pbyMessage + sizeof( RECORDHEADER7K ) + sizeof( RECORDTYPEHEADER ),
                        lpszMessage,
                            ulMessageLength );
        }

        bSuccess = EncodeRecord( psHeader,
                                    reinterpret_cast<BYTE *> (psRecordTypeHeader),
                                        sizeof( RECORDTYPEHEADER ) + ulMessageLength,
                                            rbUseChecksum );
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C7kStatus::Encode(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kStatus::Encode(), unspecified exception caught\n" ) );
    }

    if ( pbyMessage != NULL )
    {
        delete [] pbyMessage;
        pbyMessage = NULL;
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Internal private helpers.

inline
void C7kStatus::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 C7kStatus::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
}
