//
//  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:   C7kProtocol.h
//
//  Project:    6046.
//
//  Author(s):  W. Arcus
//
//  Purpose:    Defines the 7k record and frame header definitions.
//
//  Notes:      In order to be C7kHeaderDecode<tHeader> template compliant, all structs below
//              should implement the following services:
//
//              bool            IsValid      ( void ) const     - General header validity check.
//              unsigned long   DynamicBytes ( void ) const     - Number of bytes in dynamic portion of record following this header; may be zero.
//

#if !defined(AFX_7KPROTOCOL_H__6035DD58_0036_4BBE_9923_529330B122F2__INCLUDED_)
#define AFX_7KPROTOCOL_H__6035DD58_0036_4BBE_9923_529330B122F2__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "7kProtocolVersion.h"
#include "DynamicBuffer.h"

const unsigned short unBitFlagCheckSum_c            = 0x0001;
const unsigned short unBitFlagFragmentedSequence_c  = 0x0004;

typedef enum tagERECORDTYPE
{
    recordTypeEdgeTechFSDWSSS   = 3000,
    recordTypeEdgeTechFSDWSBP   = 3001,
    recordTypeBlueFinVC         = 3100
}
ERECORDTYPE;

typedef enum tagEDEVICEID                                       // Device id's per 7K ICD table (section 10).
{
    deviceIdUnknown             = 0,                            // Unspecified in ICD; internal state only.

    // RESON 7k series sonars.

    deviceIdRESONSeaBat7k       = 7000,                         // Generic SeaBat 7k ... if unknown.
    deviceIdRESONSeaBat7001     = 7001,
    deviceIdRESONSeaBat7012     = 7012,
    deviceIdRESONSeaBat7100     = 7100,
    deviceIdRESONSeaBat7101     = 7101,
    deviceIdRESONSeaBat7102     = 7102,
    deviceIdRESONSeaBat7112     = 7112,
    deviceIdRESONSeaBat7123     = 7123,
    deviceIdRESONSeaBat7125     = 7125,
    deviceIdRESONSeaBat7128     = 7128,
    deviceIdRESONSeaBat7150     = 7150,
    deviceIdRESONSeaBat7160     = 7160,

    // EdgeTech FSDW series subsystems.

    deviceIdEdgeTechFSDWSBP     = 11000,
    deviceIdEdgeTechFSDWLFSSS   = 11001,
    deviceIdEdgeTechFSDWHFSSS   = 11002,

    // Vehicle controllers.

    deviceIdBlueFinVC           = 11100
}
EDEVICEID;

// Later on, make these specific to EdgeTech FSDW and rename structs appropriately.

#pragma pack( push, PROTOCOL7K_H_PACK, 1 )                      // Set single byte alignment.

typedef struct tagPINGRECORDTYPEHEADER
{
    unsigned long   m_ulTimestamp;                              // Milliseconds since Windows started when ping recieved.
    unsigned long   m_ulPingNumber;                             // Device specific byte.
    unsigned long   m_ulNumberOfChannels;                       // Number of channels to follow.
    unsigned long   m_ulTotalBytes;                             // Total bytes in channel data to follow plus additional optional data.
    unsigned long   m_ulDataFormat;                             // 0 - Envelope, 1 - Complex (i.e., I and Q)., 2 - raw (pre-matched filter), real part of analytic signal.

    unsigned char   m_ucReserved[ 12 ];                         // Padding to 32 byte boundary.

    bool IsValid( void ) const
    {
        return ( m_ulNumberOfChannels > 0 );
    }

    unsigned long DynamicBytes( void ) const
    {
        return m_ulTotalBytes;
    }
}
PINGRECORDTYPEHEADER,      *PPINGRECORDTYPEHEADER,
SIDESCANRECORDTYPEHEADER,  *PSIDESCANRECORDTYPEHEADER,
SUBBOTTOMRECORDTYPEHEADER, *PSUBBOTTOMRECORDTYPEHEADER;

typedef struct tagCHANNELINFO                                   // Sidescan and SBP channel header.
{
    unsigned char   m_ucChannelNumber;                          // 0, 1, ... number of channels - 1.
    unsigned char   m_ucChannelType;                            // 0 = Port, 1 = Stbd, 2 = SBP
    unsigned char   m_ucDataType;                               // 0 = Slantrange, 1 = Ground range
    unsigned char   m_ucPolarity;                               // 0 - bipolar, 1 - unipolar.
    unsigned char   m_ucBytesPerSample;                         // Bytes per sample in this channel.

    unsigned char   m_ucReserved1[ 3 ];

    unsigned long   m_ulNumberOfSamples;                        // Number of samples in this channel.
    unsigned long   m_ulTimeStartOffset;                        // Start of first sample from ping timestamp (us).
    unsigned long   m_ulSampleIntervalMicroseconds;             // Sample interval (us).

    float           m_fRange;                                   // Range (m) (slant or ground depending on m_ucDataType) (-1 if not set).
    float           m_fVoltageFSD;                              // Equivalent maximum voltage amplitude (-1 if not set).

    char            m_szName [ 16 ];                            // Channel name (NULL terminated).
    unsigned char   m_ucReserved2[ 20 ];                        // Padding to 64 byte boundary.
}
CHANNELINFO, *PCHANNELINFO;

namespace NBlueFin7kMessages
{
    typedef struct tagBLUEFINRECORDTYPEHEADER                       // Data type header for BlueFin data types.
    {
        unsigned long   m_ulTimeStamp;                              // u32     Millisecond time stamp of first frame in record.
        unsigned long   m_ulNumberOfFrames;                         // u32     Number of frames embedded in this record.
        unsigned long   m_ulFrameSize;                              // u32     Embedded frame size in bytes.
        unsigned long   m_ulDataFormat;                             // u32     Data type identifier. 0 => Navigation data, 1 => Environment data.
                                                                
        BYTE            m_byReserved[ 16 ];                         // 16 * u8 Reserved.

        tagBLUEFINRECORDTYPEHEADER( void )
        {
            ::memset( this, 0x00, sizeof( struct tagBLUEFINRECORDTYPEHEADER ) );
        }

        bool IsValid( void ) const                                  // Header validity check.
        {
            return true;
        }

        unsigned long DynamicBytes( void ) const                    // Dynamic bytes to follow this record
        {
            return ( m_ulNumberOfFrames * m_ulFrameSize );
        }

        static unsigned long Size( void )
        {
            return static_cast<unsigned long>( sizeof( struct tagBLUEFINRECORDTYPEHEADER ) );
        }
    }
    BLUEFINRECORDTYPEHEADER, *PBLUEFINRECORDTYPEHEADER;

    typedef struct tagBLUEFINNETWORKHEADER
    {
        unsigned long   m_ulPacketSize;                                 // u32   -- Size in bytes of this packet including the header and appended data.
        unsigned short  m_unVersion;                                    // u16   -- Version of this frame (e.g.: 1, 2, etc.)
        unsigned short  m_unOffset;                                     // u16   -- Offset, in bytes, to the start of data from the start of this packet.
        unsigned long   m_ulDataType;                                   // u32   -- Data Description.
        unsigned long   m_ulDataSize;                                   // u32   -- Size of data, in bytes.
        BYTE            m_byTimeStamp [ 10 ];                           // u8*10 -- 7K Time Format, UTC.
        unsigned long   m_ulChecksum;                                   // u32   -- Sum of bytes in data section. Always valid.
        unsigned short  m_unReserved;                                   // u16   -- Padding to 32 byte boundary.

        static unsigned long Size( void )
        {
            return sizeof( struct tagBLUEFINNETWORKHEADER );
        }
    }
    BLUEFINNETWORKHEADER, *PBLUEFINNETWORKHEADER;

    typedef struct tagBLUEFINNAVIGATIONMESSAGE
    {
        unsigned long   m_ulQualityMetrics;     // u32          NA
        double          m_dLatitude;            // f64          Radians
        double          m_dLongitude;           // f64          Radians
        float           m_fSpeed;               // f32          Meters / Second
        double          m_dDepth;               // f64          Meters
        double          m_dAltitude;            // f64          Meters
        float           m_fRoll;                // f32          Radians
        float           m_fPitch;               // f32          Radians
        float           m_fYaw;                 // f32          Radians
        float           m_fNorthingRate;        // f32          Meters / Second
        float           m_fEastingRate;         // f32          Meters / Second
        float           m_fDepthRate;           // f32          Meters / Second
        float           m_fAltitudeRate;        // f32          Meters / Second
        float           m_fRollRate;            // f32          Radians / Second
        float           m_fPitchRate;           // f32          Radians / Second
        float           m_fYawRate;             // f32          Radians / Second
        double          m_dPositionTime;        // f64          Seconds
        double          m_dAtitudeTime;         // f64          Seconds

        static unsigned long Size( void )
        {
            return static_cast<unsigned long>( sizeof( struct tagBLUEFINNAVIGATIONMESSAGE ) );
        }
    }
    BLUEFINNAVIGATIONMESSAGE, *PBLUEFINNAVIGATIONMESSAGE;

    typedef struct tagBLUEFINENVIRONMENTMESSAGE
    {
        unsigned long   m_ulQualityMetrics;         // ui32         Bit flags.
        float           m_fSoundVelocity;           // f32          Sound speed in m/s.
        BYTE            m_byReserved    [ 88 ];     // u8 * 88      Padding to make same size as nav record above.

        static unsigned long Size( void )
        {
            return static_cast<unsigned long>( sizeof( struct tagBLUEFINENVIRONMENTMESSAGE ) );
        }
    }
    BLUEFINENVIRONMENTMESSAGE, *PBLUEFINENVIRONMENTMESSAGE;

    typedef enum tagEVALIDFLAGBITNUMBERNAVIGATION
    {
        // Bits numbers of flags in tagBLUEFINNAVIGATIONMESSAGE::m_ulQualityMetrics that are valid.
        // 0 => Invalid, 1 => Valid.

        validFlagBitNavLatitude         =   0,
        validFlagBitNavLongitude        =   1,
        validFlagBitNavSpeed            =   2,
        validFlagBitNavDepth            =   3,
        validFlagBitNavAltitude         =   4,
        validFlagBitNavRoll             =   5,
        validFlagBitNavPitch            =   6,
        validFlagBitNavYaw              =   7,
        validFlagBitNavNorthingRate     =   8,
        validFlagBitNavEastingRate      =   9,
        validFlagBitNavDepthRate        =   10,
        validFlagBitNavAltitudeRate     =   11,
        validFlagBitNavRollRate         =   12,
        validFlagBitNavPitchRate        =   13,
        validFlagBitNavYawRate          =   14,
        validFlagBitNavPositionTime     =   15,
        validFlagBitNavAttitudeTime     =   16
    }
    EVALIDFLAGBITNUMBERNAVIGATION;

    typedef enum tagEVALIDFLAGBITNUMBERENVIRONMENT
    {
        // Bits numbers of flags in tagBLUEFINENVIRONMENTMESSAGE::m_ulQualityMetrics that are valid.
        // 0 => Invalid, 1 => Valid.

        validFlagBitEnvSoundSpeed       =   0

    }
    EVALIDFLAGBITNUMBERENVIRONMENT;

}   // end namespace NBlueFinMessages

#pragma pack( pop, PROTOCOL7K_H_PACK )                          // Restore previous byte alignment.

///////////////////////////////////////////////////////////////////////////////
// C7kHeaderDecode helper template class to provide access to common types above.

template <class tHeader>
class C7kHeaderDecode
{
private:

    bool                    m_bIsValid;
    tHeader                *m_pHeader;
    unsigned long           m_ulStreamSize;

public:

    // Construction, destruction and assignment.

                        
                            C7kHeaderDecode             (   void );

                            C7kHeaderDecode             (   const unsigned char        *pbyStream,
                                                            const unsigned long        &rulBytes );

                            C7kHeaderDecode             (   const C7kHeaderDecode<tHeader>   &rRhs );

    C7kHeaderDecode<tHeader> & operator =               (   const C7kHeaderDecode<tHeader>   &rRhs );

    virtual                ~C7kHeaderDecode             (   void );

    // General helpers.

    bool                    SetStream                   (   const unsigned char       *pbyStream,
                                                            const unsigned long       &rulBytes );

    void                    Reset                       (   void );                                 // Clears all header bytes to 0x00.

    bool                    IsValid                     (   void )      const;                      // Checks to see if the header passed basic validity checks.

    // Read-only bit flags from Flags bit-field.

    bool                    IsValidChecksum             (   void )      const;                      // Returns the Valid Checksum bit of the Flags bit-field as a boolean.
    bool                    IsFragmented                (   void )      const;                      // Returns the Fragmented sequence bit of the Flags bit-field as a boolean.

    // Services for header and dynamic data access.

    static
    unsigned long           Size                        (   void );

    unsigned long           DynamicBytes                (   void )      const;                      // Size of the dynamic data if relevant.

                            operator tHeader *          (   void )      const;                      // Accesses to the header.
                            operator unsigned char *    (   void )      const;                      // Accesses to the dynamic data otherwise NULL.

};

template <class tHeader>
C7kHeaderDecode<tHeader>::C7kHeaderDecode( void )                                                   // Not implemented... need to specify an input stream.
{
    m_bIsValid     = false;
    m_pHeader      = NULL;
    m_ulStreamSize = 0UL;

    TRACE(_T( "C7kHeaderDecode<tHeader>::C7kHeaderDecode(), default constructor not implemented.\n" ) );

    ASSERT( false );
}

template <class tHeader>
C7kHeaderDecode<tHeader>::C7kHeaderDecode(  const unsigned char  *pbyStream,
                                            const unsigned long  &rulBytes )
{
    m_bIsValid     = false;
    m_pHeader      = NULL;
    m_ulStreamSize = 0UL;

    ASSERT( pbyStream != NULL );
    ASSERT( rulBytes   >  0UL );

    if ( ! SetStream( pbyStream, rulBytes ) )
    {
        TRACE( _T( "C7kHeaderDecode<tHeader>::C7kHeaderDecode(), Unable to set stream\n" ) );
    }
}

template <class tHeader>
C7kHeaderDecode<tHeader>::C7kHeaderDecode( const C7kHeaderDecode<tHeader> &rRhs )
{
    m_bIsValid     = false;
    m_pHeader      = NULL;
    m_ulStreamSize = 0UL;

    ASSERT( rRhs.m_pHeader != NULL );

    m_bIsValid     = rRhs.m_bIsValid;
    m_pHeader      = rRhs.m_pHeader;
    m_ulStreamSize = rRhs.m_ulStreamSize;
}

template <class tHeader>
C7kHeaderDecode<tHeader> & C7kHeaderDecode<tHeader>::operator = ( const C7kHeaderDecode<tHeader> &rRhs )
{
    ASSERT( rRhs.m_pHeader != NULL );

    m_bIsValid     = rRhs.m_bIsValid;
    m_pHeader      = rRhs.m_pHeader;
    m_ulStreamSize = rRhs.m_ulStreamSize;

    return *this;
}

template <class tHeader>
C7kHeaderDecode<tHeader>::~C7kHeaderDecode( void )
{
    m_pHeader       = NULL;
    m_ulStreamSize  = 0UL;
    m_bIsValid      = false;
}

template <class tHeader>
bool C7kHeaderDecode<tHeader>::SetStream(   const unsigned char  *pbyStream,
                                            const unsigned long  &rulBytes )
{
    m_bIsValid     = false;
    m_pHeader      = NULL;
    m_ulStreamSize = 0UL;

    try
    {
        if ( ( rulBytes > 0UL ) && ( pbyStream != NULL ) )
        {
            tHeader *pHeader = reinterpret_cast<tHeader *>( const_cast<unsigned char *>( pbyStream ) );

            if ( pHeader->IsValid() )
            {
                m_pHeader      = pHeader;
                m_bIsValid     = true;
                m_ulStreamSize = rulBytes;
            }
        }
    }
    catch ( ... )
    {
        m_bIsValid = false;
        m_pHeader  = NULL;
        m_ulStreamSize = 0UL;
    }

    return m_bIsValid;
}

template <class tHeader>
unsigned long C7kHeaderDecode<tHeader>::Size( void )
{
    return ( sizeof( tHeader ) );
}

template <class tHeader>
void C7kHeaderDecode<tHeader>::Reset( void )
{
    ASSERT( m_pHeader != NULL );

    ::memset( m_pHeader, 0x00, sizeof( tHeader ) );
}

template <class tHeader>
bool C7kHeaderDecode<tHeader>::IsValid( void ) const
{
    return m_bIsValid;
}

template <class tHeader>
bool C7kHeaderDecode<tHeader>::IsValidChecksum( void ) const
{
    // Per the 7k data format document, the checksum is the sum of the bytes in the record from the 
    // beginning of the version field to the end of the data section. This field is optional and depends on bit
    // 1 of the Flags field. The checksum should be computed as a 64 bit unsigned integer with the least 
    // significant 32 bits used to populate this field.

    bool bIsValid = false;

    try
    {
        ASSERT( m_pHeader != NULL );
        ASSERT( m_ulStreamSize >= sizeof( DATARECORDFRAME ) );

        DATARECORDFRAME const *pDRF = reinterpret_cast<DATARECORDFRAME *>( m_pHeader );
        BYTE const         *pbyByte = reinterpret_cast<BYTE *>( m_pHeader );

        if ( ( pDRF->m_unFlags & unBitFlagCheckSum_c ) == 0 )
        {
            bIsValid = true;            // Checksum flag inndicated checksum field in record is not used so assume ok for now.
        }
        else
        {
            const DWORD dwComputedChecksum   = C7kProtocol::ComputeChecksum( reinterpret_cast<unsigned char *>( m_pHeader ), m_ulStreamSize - Checksum7kSize_m() );
            const DWORD dwEmbeddedChecksum   = *reinterpret_cast<DWORD *>( const_cast<BYTE *>( &pbyByte[ m_ulStreamSize - Checksum7kSize_m() ] ) );

            bIsValid = ( dwComputedChecksum == dwEmbeddedChecksum );
        }
    }
    catch ( ... )
    {
        bIsValid = false;
        TRACE( _T( "C7kHeaderDecode<tHeader>::IsValidChecksum(), Unspecified exception caught!\n" ) );
    }

    return bIsValid;
}

template <class tHeader>
bool C7kHeaderDecode<tHeader>::IsFragmented( void ) const
{
#if ( DRF_VERSION_7K < 4 )

    return false;

#else

    ASSERT( m_pHeader != NULL );
    return ( ( reinterpret_cast<DATARECORDFRAME const *>( m_pHeader )->m_unFlags & unBitFlagFragmentedSequence_c ) != 0 );

#endif
}

template <class tHeader>
unsigned long C7kHeaderDecode<tHeader>::DynamicBytes( void ) const
{
    unsigned long ulDynamicBytes = 0UL;

    ASSERT( m_pHeader != NULL );

    if ( ( m_pHeader != NULL ) && IsValid() )
    {
        ulDynamicBytes = m_pHeader->DynamicBytes();
    }

    return ulDynamicBytes;
}

template <class tHeader>
C7kHeaderDecode<tHeader>::operator tHeader * ( void ) const
{
    return m_pHeader;
}

template <class tHeader>
C7kHeaderDecode<tHeader>::operator unsigned char * ( void ) const
{
    unsigned char *pucBytes = NULL;

    ASSERT( m_pHeader != NULL );

    if ( DynamicBytes() > 0UL )
    {
        pucBytes = (unsigned char *) (( (unsigned char *) m_pHeader) + Size() );
    }

    return pucBytes;
}

///////////////////////////////////////////////////////////////////////////////
// Exportable C7kProtocol base class helper for C7kRecordEncode and C7kRecordDecode.

class EXPORT_DLL C7kProtocol
{
public:

    ///////////////
    // Services.

    // Construction, destruction and assignment.

                            C7kProtocol         (   void );
                            C7kProtocol         (   const C7kProtocol                      &rRhs );

    C7kProtocol &           operator =          (   const C7kProtocol                      &rRhs );

    virtual                ~C7kProtocol         (   void );

    // Encoding helpers.

    bool                    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.

                            operator unsigned char * ()     const;                                                  // Pointer to the encoded stream.

    unsigned long           EncodedBytes        (   void )  const;                                                  // Encoded stream size.

    // Decoding helpers.

    bool                    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.


    static
    bool                    IsValid7kRecord     (   const BYTE                             *pby7kRecord,
                                                    const unsigned long                    &rulBytes );

    static
    unsigned long           RecordType          (   const BYTE                             *pby7kRecord,
                                                    const unsigned long                    &rulBytes );

    static
    unsigned long           ComputeChecksum     (   const unsigned char                    *pucStream,
                                                    const unsigned long                    &rulStreamBytes );

protected:

    ///////////////
    // Defintions.
    
    typedef CDynamicBuffer<unsigned char>           Buffer_t;                                                       // Internal stream object type for encoding a new record.
    
    ///////////////
    // Attributes.
    
    std::auto_ptr<Buffer_t>                         m_pRecord;                                                      // Internal stream object itself. NB: it's an auto pointer ensure it's not leaked.
    
    ///////////////
    // Services.

    virtual void            SetDateTime         (   RECORDHEADER7K                         *psRecordHeader )    const;

    void                    SetChecksumFlag     (   bool                                    bChecksumValid )    const;

};

///////////////////////////////////////////////////////////////////////////////
// Helper class definitions to assist with decoding 7k header types encountered.

typedef C7kHeaderDecode<RECORDHEADER7K> CRecordHeader7k;                                            // 7k Record header.


#endif // !defined(AFX_7KPROTOCOL_H__6035DD58_0036_4BBE_9923_529330B122F2__INCLUDED_)

