//
//  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:   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 "DynamicBuffer.h"

typedef unsigned long       Checksum7k_t;
#define Checksum7kSize_m()  sizeof( Checksum7k_t )

typedef enum tagERECORDTYPE
{
    recordTypeEdgeTechFSDWSSS   = 3000,
    recordTypeEdgeTechFSDWSBP   = 3001,
    recordTypeBlueFinVC         = 3100
}
ERECORDTYPE;

typedef enum tagEDEVICEID
{
    deviceIdEdgeTechFSDW        = 11000,
    deviceIdBlueFinVC           = 11100
}
EDEVICEID;

#pragma pack( push, _7KPROTOCOL_H_PACK_, 1 )                    // Single byte alignment.

typedef struct tagTIME7K
{
    unsigned short  m_unYear;                                   // Year                 u16 0 - 65535
    unsigned short  m_unDay;                                    // Day                  u16 1 - 366

    float           m_fSeconds;                                 // Seconds              f32 0.000000 - 59.000000

    unsigned char   m_ucHours;                                  // Hours                u8  0 - 23
    unsigned char   m_ucMinutes;                                // Minutes              u8  0 - 59

    bool IsValid( void ) const                                  // Header validity check.
    {
        return ( ( m_unDay     >= 1    ) &&
                 ( m_unDay     <= 366  ) &&
                 ( m_fSeconds  >= 0.0f ) &&
                 ( m_fSeconds  < 60.0f ) &&
                 ( m_ucHours   <= 23   ) &&
                 ( m_ucMinutes <= 59   )  );
    }

    unsigned long DynamicBytes( void ) const                    // Dynamic bytes to follow this record
    {
        return 0UL;                                             // ... not relevant to this header.
    }
}
TIME7K;

typedef struct tagRECORDHEADER7K
{
    unsigned short  m_unVersion;                                // Version              u16 Version of this frame (e.g.: 1, 2, …)
    unsigned short  m_unOffset;                                 // Offset               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.

    unsigned long   m_ulSyncPattern;                            // Sync pattern         u32 0x0000FFFF
    unsigned long   m_ulSize;                                   // Size                 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.
    unsigned long   m_ulOffsetToOptionalData;                   // Data offset          u32 Offset in bytes to optional data field from start of record. Zero implies no optional data.
    unsigned long   m_ulOptionalDataIdentifier;                 // Data idenfitifer     u32 Identifier for optional data field. Zero for no optional field. This identifier is described with each record type.

    TIME7K          m_sTime7k;                                  // 7KTIME               u8*10   UTC.

    unsigned long   m_ulRecordType;                             // Record type          u32 Unique identifier of indicating the type of data embedded in this record.
    unsigned long   m_ulDeviceId;                               // Device identifier    u32 Identifier of the device that this data pertains.
    unsigned long   m_ulSubsystemId;                            // Subsystem identifier u32 Identifier for the device subsystem
    unsigned long   m_ulDataSetNumber;                          // Data set             u32 Data set number.
    unsigned long   m_ulRecordNumber;                           // Record count         u32 Sequential record counter.

    __int64         m_i64PreviousRecord;                        // Previous record      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.
    __int64         m_i64NextRecord;                            // Next record          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.

    unsigned short  m_unFlags;                                  // Flags                u16 BIT FIELD: Bit 1 - Valid Checksum
                                                                
    // Following this header is:                                     
    // DATA SECTION                                                                     xx  Dynamic Record type specific data.
    // Checksum                                                                         u32 Sum of bytes in data section (optional, depends on bit 1 of Flags field).

    static unsigned long Size( void )
    {
        return sizeof( struct tagRECORDHEADER7K );
    }

    bool IsValid( void ) const
    {
        return ( ( m_unVersion          >   0                                  ) &&
                 ( m_ulSyncPattern      ==  0x0000ffff                         ) &&
                 ( m_ulSize             >   Size()                             ) &&
                 ( m_sTime7k.IsValid()                                         )  );
    }

    unsigned long DynamicBytes( void ) const
    {
        return ( m_ulSize - ( Size() + Checksum7kSize_m() ) );
    }
}
RECORDHEADER7K,  *PRECORDHEADER7K,
DATARECORDFRAME, *PDATARECORDFRAME;

// Later on, make these specific to EdgeTech FSDW and rename structs appropriately.

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;

typedef struct tagBLUEFINRECORDTYPEHEADER                       // Data type header for BlueFin data types.
{
    unsigned long   m_ulTimestampOfFirstFrame;                  // Start time of first frame contained in this record (ms).
    unsigned long   m_ulNumberOfFrames;                         // Number of contained data frames in this record.
    unsigned long   m_ulFrameSize;                              // Size in bytes of embedded frames.
    unsigned long   m_ulDataFormat;                             // Contained frame type: 0 = BLUEFININSFRAME. 1 = BLUEFINNAVIGATIONFRAME

    unsigned char   m_ucReserved1[ 16 ];                        // Reserved.

    tagBLUEFINRECORDTYPEHEADER( void )
    {
        ::memset( this, 0x00, sizeof( struct tagBLUEFINRECORDTYPEHEADER ) );
    }
}
BLUEFINRECORDTYPEHEADER, *PBLUEFINRECORDTYPEHEADER;

typedef struct tagBLUEFININSFRAME
{
    double          m_dSystemTimer;                             // System time in s.
    double          m_dGPSTime;                                 // GPS time in s.
    unsigned short  m_ulValidityFlag;                           // Data validity flag.
    double          m_dLatitude;                                // INS-only Radians.
    double          m_dLongitude;                               // INS-only Radians.
    double          m_dAltitude;                                // INS-only Meters.
    float           m_fNorthVelocity;                           // INS-only Meters / second.
    float           m_fEastVelocity;                            // INS-only Meters / second.
    float           m_fVerticalVelocity;                        // INS-only (up => (+) ve), Meters / second.
    float           m_fHeading;                                 // INS-only Radians.
    float           m_fPitch;                                   // INS-only Radians.
    float           m_fRoll;                                    // INS-only Radians.
    float           m_fYaw;                                     // INS-only Radians.
    float           m_fPitchRate;                               // INS-only Radians / second.
    float           m_fRollRate;                                // INS-only Radians / second.
    float           m_fYawRate;                                 // INS-only Radians / second.

    // Note: m_ulQuality metrics bit mask above is defined thus:
    //
    // Attribute                        Bit     Valid state
    // 
    // INS-only position valid          02          1
    // INS-only velocity valid          01          1
    // INS-only attitude valid          00          1  (LSB).

    static bool IsValidFrame( const BYTE *pbyData, const unsigned long &rulBytes )
    {
        bool bValidFrame = false;

        tagBLUEFININSFRAME *pFrame = (tagBLUEFININSFRAME *) pbyData;

        if ( ( pFrame != NULL ) && ( rulBytes >= sizeof( struct tagBLUEFININSFRAME ) ) )
        {
            const unsigned short unValidMask = 0x0007;
            bValidFrame = ( ( pFrame->m_ulValidityFlag & unValidMask ) == unValidMask );
        }

        return bValidFrame;
    }

}
BLUEFININSFRAME, *PBLUEFININSFRAME;

typedef struct tagBLUEFINNAVIGATIONFRAME
{
    double          m_dLatitude;             // Radians
    double          m_dLongitude;            // Radians
    float           m_fSpeed;                // Meters / sec
    double          m_dDepth;                // Meters
    double          m_dAltitude;             // Meters
    float           m_fRoll;                 // Radians
    float           m_fPitch;                // Radians
    float           m_fYaw;                  // Radians
    float           m_fNorthingRate;         // Meters / sec
    float           m_fEastingRate;          // Meters / sec
    float           m_fDepthRate;            // Meters / sec
    float           m_fAltitudeRate;         // Meters / sec
    float           m_fRollRate;             // Raidans / sec
    float           m_fPitchRate;            // Radians / sec
    float           m_fYawRate;              // Radians / sec
    double          m_dTimeOfPosition;       // Seconds
    double          m_dTimeOfAttitude;       // Seconds
    unsigned short  m_ulQualityMetrics;      // Bit mask as defined below.

    // Note: m_ulQuality metrics bit mask above is defined thus:
    //    
    // Attribute                    Bit     Valid state
    //
    // Latitude                     15          1           (MSB)
    // Longitude                    14          1
    // Speed                        13          1
    // Depth                        12          1
    // Altitude                     11          1
    // Roll                         10          1
    // Pitch                        09          1
    // Yaw                          08          1
    // Northing rate                07          1
    // Easting rate                 06          1
    // Depth rate                   05          1
    // Altitude rate                04          1
    // Roll rate                    03          1
    // Pitch rate                   02          1
    // Yaw rate                     01          1
    // Position and Attitude time   00          1           (LSB)

    static bool IsValidFrame( const BYTE *pbyData, const unsigned long &rulBytes )
    {
        bool bValidFrame = false;

        tagBLUEFINNAVIGATIONFRAME *pFrame = (tagBLUEFINNAVIGATIONFRAME *) pbyData;

        if ( ( pFrame != NULL ) && ( rulBytes >= sizeof( struct tagBLUEFINNAVIGATIONFRAME ) ) )
        {
            const unsigned short unValidMask = 0xffff;
            bValidFrame = ( ( pFrame->m_ulQualityMetrics & unValidMask ) == unValidMask );
        }

        return bValidFrame;
    }
}
BLUEFINNAVIGATIONFRAME, *PBLUEFINNAVIGATIONFRAME;

#pragma pack( pop, _7KPROTOCOL_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;

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.
    bool                    IsValidChecksum             (   void )      const;

    // 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_pHeader  = NULL;
    m_bIsValid = false;

    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 )
{
    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 )
{
    ASSERT( m_pHeader != NULL );
    m_pHeader = rRhs.m_pHeader;
}

template <class tHeader>
C7kHeaderDecode<tHeader> & C7kHeaderDecode<tHeader>::operator = ( const C7kHeaderDecode<tHeader> &rRhs )
{
    ASSERT( m_pHeader != NULL );
    m_pHeader = rRhs.m_pHeader;
    return *this;
}

template <class tHeader>
C7kHeaderDecode<tHeader>::~C7kHeaderDecode( void )
{
    m_pHeader = NULL;
}

template <class tHeader>
bool C7kHeaderDecode<tHeader>::SetStream(   const unsigned char  *pbyStream,
                                            const unsigned long  &rulBytes )
{
    m_pHeader  = NULL;
    m_bIsValid = false;

    try
    {
        if ( ( rulBytes > 0UL ) && ( pbyStream != NULL ) )
        {
            tHeader * pHeader = (tHeader *) pbyStream;

            if ( pHeader->IsValid() )
            {
                m_pHeader = pHeader;
                m_bIsValid = true;
            }
        }
    }
    catch ( ... )
    {
        m_pHeader  = NULL;
        m_bIsValid = false;
    }

    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
{
    ASSERT( m_pHeader != NULL );

#ifdef DEVELOPMENT_PHASE_2
#pragma CompileMessage_m( "TODO: C7kHeader<tHeader>::IsValidChecksum() needs implementing." )
#endif

    return true;
}

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;
}

///////////////////////////////////////////////////////////////////////////////
// Helper class definitions to assist with decoding 7k header types encountered.

typedef C7kHeaderDecode<RECORDHEADER7K> CRecordHeader7k;                                            // 7k Record header.
typedef C7kHeaderDecode<TIME7K>         CTimeHeader7k;                                              // 7KTIME helper.

///////////////////////////////////////////////////////////////////////////////
// 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.
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.

    void                    SetChecksumFlag     (   bool                                    bChecksumValid );

    unsigned long           ComputeChecksum     (   const unsigned char                    *pucDynamicStream,
                                                    const unsigned long                    &rulDynamicStreamBytes );

};


#endif // !defined(AFX_7KPROTOCOL_H__6035DD58_0036_4BBE_9923_529330B122F2__INCLUDED_)

