//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2025 California Institute of Technology
//
// LDASTools frameCPP is free software; you may redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools frameCPP is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.
//
// Neither the names of the California Institute of Technology (Caltech),
// The Massachusetts Institute of Technology (M.I.T), The Laser
// Interferometer Gravitational-Wave Observatory (LIGO), nor the names
// of its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// You should have received a copy of the licensing terms for this
// software included in the file LICENSE located in the top-level
// directory of this package. If you did not, you can view a copy at
// http://dcc.ligo.org/M1500244/LICENSE
//

#ifndef FrameCPP__UNIVERSAL__IMPL__FrHeaderData_HH
#define FrameCPP__UNIVERSAL__IMPL__FrHeaderData_HH

#include "framecpp/Common/FrHeader.hh"
#include "framecpp/Common/FrameSpec.hh"
#include "framecpp/Universal/impl/FrObjectMetaData.hh"

namespace FrameCPP
{
    namespace Universal
    {
        namespace FrHeaderImpl
        {
#define FRAMECPP_PI 3.1415926535897932384
            class Data : public Common::FrHeader,
                         public Impl::FrObjectMetaData< Data >
            {
            public:
                // Version-specific type definitions
                struct v3
                {
                    // Bytes 0-6: Provided by Common::FrHeader base
                    // Bytes 7-39: Frame-specific fields
                    using SizeOf_INT_2_type = CHAR_U;
                    using SizeOf_INT_4_type = CHAR_U;
                    using SizeOf_INT_8_type = CHAR_U;
                    using SizeOf_REAL_4_type = CHAR_U;
                    using SizeOf_REAL_8_type = CHAR_U;
                    using Endian_2_byte_type = INT_2U;
                    using Endian_4_byte_type = INT_4U;
                    using Endian_8_byte_type = INT_8U;
                    using PI_SINGLE_type = float;
                    using PI_DOUBLE_type = double;
                    // Bytes 38-39: Legacy constant 'A''Z'
                    using legacy_marker_type = CHAR[ 2 ];
                };

                using v4 = v3;
                using v5 = v3;
                using v6 = v3;
                using v7 = v3;

                struct v8
                {
                    // Bytes 0-6: Provided by Common::FrHeader base
                    // Bytes 7-39: Frame-specific fields (same as v3-v7)
                    using SizeOf_INT_2_type = typename v3::SizeOf_INT_2_type;
                    using SizeOf_INT_4_type = typename v3::SizeOf_INT_4_type;
                    using SizeOf_INT_8_type = typename v3::SizeOf_INT_8_type;
                    using SizeOf_REAL_4_type = typename v3::SizeOf_REAL_4_type;
                    using SizeOf_REAL_8_type = typename v3::SizeOf_REAL_8_type;
                    using Endian_2_byte_type = typename v3::Endian_2_byte_type;
                    using Endian_4_byte_type = typename v3::Endian_4_byte_type;
                    using Endian_8_byte_type = typename v3::Endian_8_byte_type;
                    using PI_SINGLE_type = typename v3::PI_SINGLE_type;
                    using PI_DOUBLE_type = typename v3::PI_DOUBLE_type;
                    // Bytes 38-39: New fields for v8+
                    using frameLibrary_type = CHAR;
                    using checksumScheme_type = CHAR;
                };

                using v9 = v8;
                using current_version = v9;

                // Current version types
                using SizeOf_INT_2_type =
                    typename current_version::SizeOf_INT_2_type;
                using SizeOf_INT_4_type =
                    typename current_version::SizeOf_INT_4_type;
                using SizeOf_INT_8_type =
                    typename current_version::SizeOf_INT_8_type;
                using SizeOf_REAL_4_type =
                    typename current_version::SizeOf_REAL_4_type;
                using SizeOf_REAL_8_type =
                    typename current_version::SizeOf_REAL_8_type;
                using Endian_2_byte_type =
                    typename current_version::Endian_2_byte_type;
                using Endian_4_byte_type =
                    typename current_version::Endian_4_byte_type;
                using Endian_8_byte_type =
                    typename current_version::Endian_8_byte_type;
                using PI_SINGLE_type = typename current_version::PI_SINGLE_type;
                using PI_DOUBLE_type = typename current_version::PI_DOUBLE_type;
                using frameLibrary_type =
                    typename current_version::frameLibrary_type;
                using checksumScheme_type =
                    typename current_version::checksumScheme_type;

                //
                bool   m_reorder_bytes;
                INT_4U m_byte_order;

                // Constructors
                Data( )
                    : Common::FrHeader( ), m_reorder_bytes( false ),
                      m_byte_order( FrameCPP::HostByteOrder ),
                      SizeOf_INT_2( sizeof( INT_2U ) ),
                      SizeOf_INT_4( sizeof( INT_4U ) ),
                      SizeOf_INT_8( sizeof( INT_8U ) ),
                      SizeOf_REAL_4( sizeof( REAL_4 ) ),
                      SizeOf_REAL_8( sizeof( REAL_8 ) ),
                      Endian_2_byte( 0x1234 ), Endian_4_byte( 0x12345678 ),
                      Endian_8_byte( 0x123456789abcdefULL ),
                      PI_SINGLE( static_cast< float >( FRAMECPP_PI ) ),
                      PI_DOUBLE( static_cast< double >( FRAMECPP_PI ) ),
                      m_frameLibrary(
                          Common::FrHeader::FRAME_LIBRARY_FRAMECPP ),
                      m_checksumScheme( 0 )
                {
                }

                // Getters
                inline SizeOf_INT_2_type
                GetSizeOf_INT_2( ) const
                {
                    return SizeOf_INT_2;
                }
                inline SizeOf_INT_4_type
                GetSizeOf_INT_4( ) const
                {
                    return SizeOf_INT_4;
                }
                inline SizeOf_INT_8_type
                GetSizeOf_INT_8( ) const
                {
                    return SizeOf_INT_8;
                }
                inline SizeOf_REAL_4_type
                GetSizeOf_REAL_4( ) const
                {
                    return SizeOf_REAL_4;
                }
                inline SizeOf_REAL_8_type
                GetSizeOf_REAL_8( ) const
                {
                    return SizeOf_REAL_8;
                }
                inline Endian_2_byte_type
                GetEndian_2_byte( ) const
                {
                    return Endian_2_byte;
                }
                inline Endian_4_byte_type
                GetEndian_4_byte( ) const
                {
                    return Endian_4_byte;
                }
                inline Endian_8_byte_type
                GetEndian_8_byte( ) const
                {
                    return Endian_8_byte;
                }
                inline PI_SINGLE_type
                GetPI_SINGLE( ) const
                {
                    return PI_SINGLE;
                }
                inline PI_DOUBLE_type
                GetPI_DOUBLE( ) const
                {
                    return PI_DOUBLE;
                }
                inline frameLibrary_type
                GetFrameLibrary( ) const
                {
                    return m_frameLibrary;
                }
                inline checksumScheme_type
                GetChecksumScheme( ) const
                {
                    return m_checksumScheme;
                }

                // Setters
                inline void
                SetSizeOf_INT_2( SizeOf_INT_2_type value )
                {
                    SizeOf_INT_2 = value;
                }
                inline void
                SetSizeOf_INT_4( SizeOf_INT_4_type value )
                {
                    SizeOf_INT_4 = value;
                }
                inline void
                SetSizeOf_INT_8( SizeOf_INT_8_type value )
                {
                    SizeOf_INT_8 = value;
                }
                inline void
                SetSizeOf_REAL_4( SizeOf_REAL_4_type value )
                {
                    SizeOf_REAL_4 = value;
                }
                inline void
                SetSizeOf_REAL_8( SizeOf_REAL_8_type value )
                {
                    SizeOf_REAL_8 = value;
                }
                inline void
                SetEndian_2_byte( Endian_2_byte_type value )
                {
                    Endian_2_byte = value;
                }
                inline void
                SetEndian_4_byte( Endian_4_byte_type value )
                {
                    Endian_4_byte = value;
                }
                inline void
                SetEndian_8_byte( Endian_8_byte_type value )
                {
                    Endian_8_byte = value;
                }
                inline void
                SetPI_SINGLE( PI_SINGLE_type value )
                {
                    PI_SINGLE = value;
                }
                inline void
                SetPI_DOUBLE( PI_DOUBLE_type value )
                {
                    PI_DOUBLE = value;
                }
                inline void
                SetFrameLibrary( frameLibrary_type value )
                {
                    m_frameLibrary = value;
                }
                inline void
                SetChecksumScheme( checksumScheme_type value )
                {
                    m_checksumScheme = value;
                }

                // Equality operator
                bool
                operator==( const Data& RHS ) const
                {
                    // Compare our frame-specific fields (bytes 7-39)
                    // Note: Base class Common::FrHeader (bytes 0-6) comparison
                    // handled separately
                    return ( SizeOf_INT_2 == RHS.SizeOf_INT_2 ) &&
                        ( SizeOf_INT_4 == RHS.SizeOf_INT_4 ) &&
                        ( SizeOf_INT_8 == RHS.SizeOf_INT_8 ) &&
                        ( SizeOf_REAL_4 == RHS.SizeOf_REAL_4 ) &&
                        ( SizeOf_REAL_8 == RHS.SizeOf_REAL_8 ) &&
                        ( Endian_2_byte == RHS.Endian_2_byte ) &&
                        ( Endian_4_byte == RHS.Endian_4_byte ) &&
                        ( Endian_8_byte == RHS.Endian_8_byte ) &&
                        ( PI_SINGLE == RHS.PI_SINGLE ) &&
                        ( PI_DOUBLE == RHS.PI_DOUBLE ) &&
                        ( m_frameLibrary == RHS.m_frameLibrary ) &&
                        ( m_checksumScheme == RHS.m_checksumScheme ) &&
                        ( m_reorder_bytes == RHS.m_reorder_bytes ) &&
                        ( m_byte_order == RHS.m_byte_order );
                }

            protected:
                // Byte 7: Size of INT_2
                SizeOf_INT_2_type SizeOf_INT_2;

                // Byte 8: Size of INT_4
                SizeOf_INT_4_type SizeOf_INT_4;

                // Byte 9: Size of INT_8
                SizeOf_INT_8_type SizeOf_INT_8;

                // Byte 10: Size of REAL_4
                SizeOf_REAL_4_type SizeOf_REAL_4;

                // Byte 11: Size of REAL_8
                SizeOf_REAL_8_type SizeOf_REAL_8;

                // Bytes 12-13: Endian test for 2-byte
                Endian_2_byte_type Endian_2_byte;

                // Bytes 14-17: Endian test for 4-byte
                Endian_4_byte_type Endian_4_byte;

                // Bytes 18-25: Endian test for 8-byte
                Endian_8_byte_type Endian_8_byte;

                // Bytes 26-29: PI single precision
                PI_SINGLE_type PI_SINGLE;

                // Bytes 30-37: PI double precision
                PI_DOUBLE_type PI_DOUBLE;

                // Bytes 38-39: frameLibrary and checksumScheme (handled below)

                // Byte 38: Frame library (v8+) or 'A' (v3-v7)
                frameLibrary_type m_frameLibrary;

                // Byte 39: Checksum scheme (v8+) or 'Z' (v3-v7)
                checksumScheme_type m_checksumScheme;

            protected:
                //---------------------------------------------------------------
                /// \brief Detect byte order and set m_reorder_bytes and
                /// m_byte_order
                ///
                /// This method compares Endian_4_byte to the expected value
                /// 0x12345678 to determine if byte swapping is needed. It sets
                /// m_reorder_bytes and m_byte_order accordingly.
                ///
                /// Should be called after reading all header fields from
                /// stream.
                //---------------------------------------------------------------
                void detectAndSetByteOrder( );
            };
#undef FRAMECPP_PI
        } // namespace FrHeaderImpl
    } // namespace Universal
} // namespace FrameCPP

FR_OBJECT_META_DATA_DECLARE_UNIVERSAL( FrHeaderImpl )

#endif /* FrameCPP__UNIVERSAL__IMPL__FrHeaderData_HH */
