//
//  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:   7kProtocol.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    Implementation file for C7kProtocol class.
//
//  Notes:      
//

#include "StdAfx.h"
#include "7kProtocol.h"

#include "SystemTime.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// C7kProtocol class implementation.

// Public services first... these should invoke ManageDLLState_m() first up 
// since they're being exported from a DLL.

C7kProtocol::C7kProtocol( void )
{
    ManageDllState_m();

    m_pRecord = std::auto_ptr<Buffer_t>( new Buffer_t );

    ASSERT( m_pRecord.get() != NULL );
}

C7kProtocol::C7kProtocol( const C7kProtocol &rRhs )
{
    ManageDllState_m();

    m_pRecord = std::auto_ptr<Buffer_t>( new Buffer_t );
    ASSERT( m_pRecord.get() != NULL );

    // Get the target CDynamicBuffer object and copy the encoded stream of bytes
    // using its assignment operator.

    CDynamicBuffer<unsigned char> *pSource = rRhs.m_pRecord.get();

    ASSERT( pSource != NULL );

    if ( pSource != NULL )
    {
        *(m_pRecord.get()) = *pSource;
    }
}

C7kProtocol & C7kProtocol::operator = ( const C7kProtocol &rRhs )
{
    ManageDllState_m();

    // Get the target CDynamicBuffer<> object and copy the encoded stream of bytes
    // using its assignment operator.

    CDynamicBuffer<unsigned char> *pSource = rRhs.m_pRecord.get();

    ASSERT( pSource != NULL );

    if ( pSource != NULL )
    {
        *(m_pRecord.get()) = *pSource;
    }

    return *this;
}

C7kProtocol::~C7kProtocol( void )
{
    ManageDllState_m();

    // Explicitly destroy our m_pRecord pointer. Not actually needed since 
    // m_pRecord will destroy itself by definition.

    try
    {
        if ( m_pRecord.get() != NULL )
        {
            delete ( m_pRecord.release() );
            m_pRecord = std::auto_ptr<Buffer_t>( NULL );
        }
    }
    catch ( ... )
    {
        TRACE( _T( "C7kProtocol::~C7kProtocol(), unspecified exception caught\n" ) );
    }
}

bool C7kProtocol::EncodeRecord( RECORDHEADER7K          *psRecordHeader,         // Input: record header.
                                unsigned char           *pucDynamicStream,       // Input: dynamic data portion of record, may be NULL.
                                const   unsigned long   &rulDynamicBytes,        // Input: number of dynamic bytes in dynamic data stream.
                                const   bool            &rbUseChecksum )         // Input: compute and store valid Checksum.
{
    // Routine to encode a complete record from its components. Internal buffer is used as storage 
    // and pointer to its byte stream returned.

    ManageDllState_m();

    bool bSuccess = false;      // Assume failure at first.

    ASSERT( m_pRecord.get() != NULL );

    if ( m_pRecord.get() != NULL )
    {
        m_pRecord->Reset();
    
        if ( psRecordHeader->IsValid() )
        {
            // Add the header to the encoded stream.

            m_pRecord->Add( (unsigned char *)(psRecordHeader), sizeof( RECORDHEADER7K ) );

            Checksum7k_t ulChecksum = 0;

            // Add the dynamic data bytes, if any.

            if ( ( rulDynamicBytes > 0UL ) && ( pucDynamicStream != NULL ) )
            {
                m_pRecord->Add( pucDynamicStream, rulDynamicBytes );
            }

            // Compute the record checksum, if necessary.

            if ( rbUseChecksum )
            {
                ulChecksum = ComputeChecksum( m_pRecord->GetAt( 0 ), m_pRecord->Size() );
            }

            // Finally, append the checksum and set its validity flag in the header.

            m_pRecord->Add( reinterpret_cast<unsigned char *>( &ulChecksum ), Checksum7kSize_m() );

            SetChecksumFlag( rbUseChecksum );

            bSuccess = true;
        }
    }

    return bSuccess;
}

C7kProtocol::operator unsigned char * () const
{
    ManageDllState_m();

    ASSERT( m_pRecord.get() != NULL );

    return m_pRecord->GetAt( 0 );
}

unsigned long C7kProtocol::EncodedBytes( void ) const
{
    ManageDllState_m();

    ASSERT( m_pRecord.get() != NULL );

    return m_pRecord->Size();
}

bool C7kProtocol::DecodeRecord( const unsigned char *pucStream,              // Input: record stream to decode.
                                const unsigned long &rulStreamBytes,         // Input: bytes in input stream.
                                const bool          &rbUseChecksum,          // Input: use or ignore Checksum in stream.
                                RECORDHEADER7K *    &rpsRecordHeader,        // Output: decoded record header.
                                unsigned char  *    &rpucDynamicStream,      // Output: pointer to dynamic data section.
                                unsigned long       &rulDynamicBytes )       // Output: bytes in dynamic data section.
{
    ManageDllState_m();

    // Service to decode a given encoded byte stream and break it out into its components and
    // do basic validity checking.

    bool bSuccess = false;      // Assume failure at first.

    if ( ( pucStream != NULL ) && ( rulStreamBytes > sizeof( RECORDHEADER7K ) ) )
    {
        CRecordHeader7k Record( pucStream, rulStreamBytes );

        if ( Record.IsValid() )
        {
            if ( ( ! rbUseChecksum ) || ( rbUseChecksum && Record.IsValidChecksum() ) )
            {
                rpsRecordHeader   = (RECORDHEADER7K *) Record;
                rulDynamicBytes   = Record.DynamicBytes();
                rpucDynamicStream = ( rulDynamicBytes == 0UL ) ? NULL : (unsigned char *) Record;

                bSuccess = true;
            }
        }
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Private internal helpers... shouldn't invoke ManageDLLState_m()

inline
void C7kProtocol::SetChecksumFlag( bool bChecksumValid ) const
{
    ASSERT( m_pRecord.get() != NULL );

    if ( ( m_pRecord.get() != NULL ) && ( m_pRecord->Size() > sizeof( RECORDHEADER7K ) ) )
    {
        unsigned short *punFlags = &(((RECORDHEADER7K *)(m_pRecord->GetAt( 0 )))->m_unFlags);

        // Set or clear the checksum bit in the header according to the bCheksumValid flag.
    
        if ( punFlags != NULL )
        {
            if ( bChecksumValid )
            {
                *punFlags |= 0x0001;                    // Set bit 0.
            }
            else
            {
                *punFlags ^= (*punFlags & 0x0001 );     // Clear bit 0.
            }
        }
    }
}

inline
unsigned long C7kProtocol::ComputeChecksum( const unsigned char *pucStream,
                                            const unsigned long &rulStreamBytes )
{
    ULARGE_INTEGER ui64ComputedChecksum = { 0 };

    for ( unsigned long ulByte = 0UL; ulByte < rulStreamBytes; ulByte++ )
    {
        ui64ComputedChecksum.QuadPart += pucStream[ ulByte ];
    }

    return ui64ComputedChecksum.LowPart;
}

bool C7kProtocol::IsValid7kRecord(  const BYTE             *pby7kRecord,
                                    const unsigned long    &rulBytes )
{
    if ( ( pby7kRecord != NULL ) && ( rulBytes > sizeof( DATARECORDFRAME ) ) )
    {
        const DATARECORDFRAME * const pDRF = reinterpret_cast<const DATARECORDFRAME *>( pby7kRecord );

        return ( pDRF->IsValid() && ( rulBytes >= pDRF->m_ulSize ) );
    }

    return false;
}

unsigned long C7kProtocol::RecordType(  const BYTE             *pby7kRecord,
                                        const unsigned long    &rulBytes )
{
    return ( ( IsValid7kRecord( pby7kRecord, rulBytes ) ) ?
             ( reinterpret_cast<const DATARECORDFRAME *>( pby7kRecord ) )->m_ulRecordType : 0UL );
}

void C7kProtocol::SetDateTime( RECORDHEADER7K *psRecordHeader ) const
{
    if ( ! CSystemTime::TimeStampTo7kTime( CSystemTime::GetTickCount(), &(psRecordHeader->m_sTime7k) ) )
    {
        TRACE( _T( "C7kProtocol::SetDateTime(), CSystemTime::TimeStampTo7kTime() failed\n" ) );
    }
}




