//
//  Copyright © 2001 - 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:   Generic7kRecordEncoder.h
//
//  Project:    6046.
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      
//              
//

#if !defined(AFX_GENERIC7KRECORDENCODER_H__2D3FEC1E_5EF8_4D1E_8751_60E0DE797F6A__INCLUDED_)
#define AFX_GENERIC7KRECORDENCODER_H__2D3FEC1E_5EF8_4D1E_8751_60E0DE797F6A__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "SystemTime.h"
#include "7kProtocol.h"

template <typename tRecordTypeHeader>
class CGeneric7kRecordEncoder
{
public:

        ////////////////
        // Services.

                                        CGeneric7kRecordEncoder (   void );
        virtual                        ~CGeneric7kRecordEncoder (   void );
                                       
                                        operator BYTE *         (   void )  const;
                                       
                                        operator unsigned long  (   void )  const;
                                       
                                        operator TIME7K *       (   void )  const;
                                       
        bool                            Encode                  (   const unsigned long     &rulRecordType,
                                                                    const tRecordTypeHeader *ptRecordTypeHeader,
                                                                    const BYTE              *pbyRecordData          = NULL,
                                                                    const unsigned long     &rulRecordDataBytes     = 0UL,
                                                                    const bool              &rbUseChecksum          = false );

protected:

        ////////////////
        // Attributes.

        const unsigned long             m_ulRecordHeaderSize;

        unsigned long                   m_ulAllocatedSize;
        BYTE                           *m_pbyMessage;

private:

        ////////////////
        // Services.

        bool                            AllocateStorage         (   const unsigned long     &rulRecordSize );

        void                            SetDefaultHeader        (   RECORDHEADER7K          *psRecordHeader );

        void                            SetDateTime             (   RECORDHEADER7K          *psRecordHeader ) const;

                                        CGeneric7kRecordEncoder (   const CGeneric7kRecordEncoder  &rRhs );                        // Not implemented thus private.
        CGeneric7kRecordEncoder &       operator =              (   const CGeneric7kRecordEncoder  &rRhs );                        // Not implemented thus private.
};

///////////////////////////////////////////////////////////////////////////////
// CGeneric7kRecordEncoder template class implementation.

template <typename tRecordTypeHeader>
inline CGeneric7kRecordEncoder<tRecordTypeHeader>::CGeneric7kRecordEncoder( void )
                                                  :m_ulRecordHeaderSize( tagRECORDHEADER7K::Size() )
{
    m_ulAllocatedSize = 0UL;
    m_pbyMessage      = NULL;
}

template <typename tRecordTypeHeader>
inline CGeneric7kRecordEncoder<tRecordTypeHeader>::~CGeneric7kRecordEncoder( void )
{
    m_ulAllocatedSize = 0UL;

    if ( m_pbyMessage != NULL )
    {
        delete [] m_pbyMessage;
        m_pbyMessage = NULL;
    }
}

template <typename tRecordTypeHeader>
inline bool CGeneric7kRecordEncoder<tRecordTypeHeader>::Encode(    const unsigned long     &rulRecordType,
                                                                   const tRecordTypeHeader *ptRecordTypeHeader,
                                                                   const BYTE              *pbyRecordData,
                                                                   const unsigned long     &rulRecordDataBytes,
                                                                   const bool              &rbUseChecksum )
{
    bool bSuccess = false;

    try
    {
        const unsigned long ulRecordSize = tagRECORDHEADER7K::Size() +
                                                sizeof( tRecordTypeHeader ) +
                                                    rulRecordDataBytes +
                                                        Checksum7kSize_m();

        if ( ! AllocateStorage( ulRecordSize ) )
        {
            ThrowMessage_m( "Can't allocate storage for 7k record" );
        }

        RECORDHEADER7K *psHeader = reinterpret_cast<RECORDHEADER7K *>( m_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                = rulRecordType;                                                  // 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.

#if ( DRF_VERSION_7K <= 2 )

#if ( DRF_VERSION_7K == 1 )

        psHeader->m_ulSubsystemId               = 0UL;

#else

        psHeader->m_unSubsystemId               = 0U;                                                             // u32 Identifier for the device subsystem

#endif

        psHeader->m_ulDataSetNumber             = 0UL;                                                            // u32 Data set number.

#endif

        psHeader->m_ulRecordNumber              = 0UL;                                                            // u32 Sequential record counter.


        tRecordTypeHeader *pRTH = reinterpret_cast<tRecordTypeHeader *>( m_pbyMessage + tagRECORDHEADER7K::Size() );

        ASSERT( ptRecordTypeHeader != NULL );

        ::memcpy( pRTH, ptRecordTypeHeader, sizeof( tRecordTypeHeader ) );

        if ( ( rulRecordDataBytes > 0UL ) && ( pbyRecordData != NULL ) )
        {
            ::memcpy( m_pbyMessage + tagRECORDHEADER7K::Size() + sizeof( tRecordTypeHeader ), pbyRecordData, rulRecordDataBytes );
        }

        // For now, we're ignoring checksum, so clear it and its bit flag in the header.

        unsigned short *punFlags    = &(psHeader->m_unFlags);
        unsigned long  *punChecksum = reinterpret_cast<unsigned long *>( &m_pbyMessage[ tagRECORDHEADER7K::Size() + sizeof( tRecordTypeHeader ) + rulRecordDataBytes ] );
        
        if ( rbUseChecksum )
        {
            *punChecksum = C7kProtocol::ComputeChecksum( m_pbyMessage, ulRecordSize - Checksum7kSize_m() );
            *punFlags |= 0x0001;                        // Set bit 0.
        }
        else
        {
            *punChecksum = 0UL;
            *punFlags ^= ( *punFlags & 0x0001 );        // Clear bit 0.
        }

        bSuccess = true;
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "CGeneric7kRecordEncoder<tRecordTypeHeader>::Encode(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CGeneric7kRecordEncoder<tRecordTypeHeader>::Encode(), unspecified exception caught\n" ) );
    }

    return bSuccess;
}

template <typename tRecordTypeHeader>
inline CGeneric7kRecordEncoder<tRecordTypeHeader>::operator BYTE * ( void ) const
{
    return m_pbyMessage;
}

template <typename tRecordTypeHeader>
inline CGeneric7kRecordEncoder<tRecordTypeHeader>::operator TIME7K * ( void ) const
{
    return ( reinterpret_cast<TIME7K *>( &(reinterpret_cast<RECORDHEADER7K *>( m_pbyMessage )->m_sTime7k )) );
}

template <typename tRecordTypeHeader>
inline CGeneric7kRecordEncoder<tRecordTypeHeader>::operator unsigned long ( void ) const
{
    ASSERT( m_pbyMessage != NULL );

    if ( m_pbyMessage != NULL )
    {
        return (( reinterpret_cast<RECORDHEADER7K *>( m_pbyMessage ))->m_ulSize );
    }

    return 0UL;
}

template <typename tRecordTypeHeader>
inline void CGeneric7kRecordEncoder<tRecordTypeHeader>::SetDefaultHeader( RECORDHEADER7K *psRecordHeader )
{
    // Set sensible defaults for the 7k command header.

    if ( psRecordHeader != NULL )
    {
        ::memset( psRecordHeader, 0x00, m_ulRecordHeaderSize );

        psRecordHeader->m_unVersion                 = un7kDataProtocolVersion_c;                                     // 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.

#if ( DRF_VERSION_7K <= 2 )

#if ( DRF_VERSION_7K == 1 )

        psHeader->m_ulSubsystemId                   = 0UL;

#else

        psHeader->m_unSubsystemId                   = 0U;                                                             // u32 Identifier for the device subsystem

#endif

        psHeader->m_ulDataSetNumber                 = 0UL;                                                            // u32 Data set number.

#endif

        psRecordHeader->m_ulRecordNumber            = 0UL;                                                            // u32 Sequential record counter.

#if ( DRF_VERSION_7K <= 2 )

        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.

#endif

        psRecordHeader->m_unFlags                   = 0x0000;                                                         // u16 BIT FIELD: Bit 1 - Valid Checksum
    }
}

template <typename tRecordTypeHeader>
inline void CGeneric7kRecordEncoder<tRecordTypeHeader>::SetDateTime( RECORDHEADER7K *psRecordHeader ) const
{
    if ( ! CSystemTime::TimeStampTo7kTime( CSystemTime::GetTickCount(), &(psRecordHeader->m_sTime7k) ) )
    {
        TRACE( _T( "CGeneric7kRecordEncoder<tRecordTypeHeader>::SetDateTime(), CSystemTime::TimeStampTo7kTime() failed\n" ) );
    }
}

template <typename tRecordTypeHeader>
inline bool CGeneric7kRecordEncoder<tRecordTypeHeader>::AllocateStorage( const unsigned long &rulRecordSize )
{
    if ( ( m_pbyMessage != NULL ) && ( rulRecordSize > m_ulAllocatedSize ) )
    {
        delete [] m_pbyMessage;
        m_pbyMessage = NULL;
        m_ulAllocatedSize = 0UL;
    }

    if ( ( m_pbyMessage == NULL ) && ( rulRecordSize > 0UL ) )
    {
        if ( ( m_pbyMessage = new BYTE [ rulRecordSize ] ) == NULL )
        {
            m_ulAllocatedSize = 0UL;
        }
        else
        {
            m_ulAllocatedSize = rulRecordSize;
        }
    }

    bool bSuccess = ( ( m_pbyMessage != NULL ) && ( m_ulAllocatedSize > 0UL ) );

    ASSERT( bSuccess );

    return bSuccess;
}

#endif // !defined(AFX_GENERIC7KRECORDENCODER_H__2D3FEC1E_5EF8_4D1E_8751_60E0DE797F6A__INCLUDED_)
