//
//  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:   7kProtocol.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    Implementation file for C7kProtocol class.
//
//  Notes:      
//

#include "StdAfx.h"
#include "7kProtocol.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.

    if ( m_pRecord.get() != NULL )
    {
        delete ( m_pRecord.release() );
        m_pRecord = std::auto_ptr<Buffer_t>( NULL );
    }
}

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 ) );

            bool            bUseComputedChecksum = false;
            unsigned long   ulChecksum           = 0x00000000;

            // Add the dynamic data bytes, if any.

            if ( ( rulDynamicBytes > 0UL ) && ( pucDynamicStream != NULL ) )
            {
                m_pRecord->Add( pucDynamicStream, rulDynamicBytes );

                if ( rbUseChecksum )
                {
                    // Compute Checksum of stream. It's a u32 and is the sum of the bytes in data section.

                    ulChecksum = ComputeChecksum( pucDynamicStream, rulDynamicBytes );

                    bUseComputedChecksum = true;
                }
            }

            // Next, append the Checksum and set its validity flag in the header.

            m_pRecord->Add( ((unsigned char *) (&ulChecksum)), sizeof( unsigned long ) );

            SetChecksumFlag( bUseComputedChecksum );

            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();

                if ( rulDynamicBytes == 0UL )
                {
                    rpucDynamicStream = NULL;
                }
                else
                {
                    rpucDynamicStream = (unsigned char *) Record;
                }

                bSuccess = true;
            }
        }
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Private internal helpers... shouldn't invoke ManageDLLState_m()

inline
void C7kProtocol::SetChecksumFlag( bool bChecksumValid )
{
    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 *pucDynamicStream,
                                            const unsigned long &rulDynamicStreamBytes )
{
    unsigned long ulChecksum = 0UL;

    if ( ( pucDynamicStream != NULL ) && ( rulDynamicStreamBytes > 0UL ) )
    {
        const unsigned char *pucByte = pucDynamicStream;

        for ( unsigned long ulByte = 0UL; ulByte < rulDynamicStreamBytes; ulByte++ )
        {
            ulChecksum += (unsigned long) (pucByte[ ulByte ]);
        }
    }

    return ulChecksum;
}

