//
// 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
//

#include "framecpp/Universal/FrHeader.hh"

#include "framecpp/Common/Description.hh"
#include "framecpp/Common/FrameSpec.hh"

#include <map>

namespace FrameCPP
{
    namespace Universal
    {
        //-----------------------------------------------------------------
        /// \brief Default constructor
        //-----------------------------------------------------------------
        FrHeader::FrHeader( )
        {
            // Initialize Common::FrHeader base with proper originator
            SetOriginator( "IGWD" );
            // Set default values for v8+ fields (handled by Data constructor)
            SetFrameLibrary( Common::FrHeader::FRAME_LIBRARY_FRAMECPP );
            SetChecksumScheme( 0 );
        }

        //-----------------------------------------------------------------
        /// \brief Explicit construction
        //-----------------------------------------------------------------
        FrHeader::FrHeader( frameLibrary_type   frameLibrary,
                            checksumScheme_type checksumScheme )
        {
            // Initialize Common::FrHeader base with proper originator
            SetOriginator( "IGWD" );
            SetFrameLibrary( frameLibrary );
            SetChecksumScheme( checksumScheme );
        }

        //-----------------------------------------------------------------
        /// \brief Copy constructor
        //-----------------------------------------------------------------
        FrHeader::FrHeader( const FrHeader& header )
            : FrHeaderImpl::ClassicIO< FrHeader >( header ),
              FrHeaderImpl::SerialIO< FrHeader >( header )
        {
        }

        //-----------------------------------------------------------------
        /// \brief Equality operator
        //-----------------------------------------------------------------
        bool
        FrHeader::operator==( const Common::FrameSpec::Object& Obj ) const
        {
            const FrHeader* rhs = dynamic_cast< const FrHeader* >( &Obj );
            if ( !rhs )
                return false;

            // Cast both to Data for comparison
            const Data*      lhs_data = static_cast< const Data* >( this );
            const Data*      rhs_data = static_cast< const Data* >( rhs );
            return lhs_data->operator==( *rhs_data );
        }

        namespace FrHeaderImpl
        {
            //-----------------------------------------------------------------
            /// \brief Detect byte order and set m_reorder_bytes and
            /// m_byte_order
            //-----------------------------------------------------------------
            void
            Data::detectAndSetByteOrder( )
            {
                // Expected value for Endian_2_byte test field
                const Endian_2_byte_type expected_endian = 0x1234;

                // Compare actual vs expected to detect byte order mismatch
                if ( Endian_2_byte != expected_endian )
                {
                    m_reorder_bytes = true;

                    // Determine target byte order based on host byte order
                    if ( FrameCPP::HostByteOrder ==
                         FrameCPP::BYTE_ORDER_BIG_ENDIAN )
                    {
                        m_byte_order = FrameCPP::BYTE_ORDER_LITTLE_ENDIAN;
                    }
                    else
                    {
                        m_byte_order = FrameCPP::BYTE_ORDER_BIG_ENDIAN;
                    }
                }
                else
                {
                    m_reorder_bytes = false;
                    m_byte_order = FrameCPP::HostByteOrder;
                }

                // Log the byte order detection results
                static SimpleLogger* logger_instance =
                    get_channel_logger( "framecpp.frheader" );
                logger_log_debug(
                    logger_instance,
                    25,
                    "detectAndSetByteOrder() - Expected Endian_2_byte: 0x",
                    std::hex,
                    expected_endian,
                    std::dec,
                    ", Actual: 0x",
                    std::hex,
                    Endian_2_byte,
                    std::dec,
                    ", m_reorder_bytes: ",
                    m_reorder_bytes,
                    ", m_byte_order: ",
                    m_byte_order );
            }

            //-----------------------------------------------------------------
            /// \brief Get description for ClassicIO
            //-----------------------------------------------------------------
            template < typename T >
            const Common::Description*
            ClassicIO< T >::GetDescription( unsigned int version ) const
            {
                // Return static description for FrHeader structure
                static Common::Description desc;
                return &desc;
            }

            //-----------------------------------------------------------------
            /// \brief Equality operator for ClassicIO
            //-----------------------------------------------------------------
            template < typename T >
            bool
            ClassicIO< T >::operator==( const FrHeader& RHS ) const
            {
                // Cast both objects to Data for comparison
                // FrHeader inherits from ClassicIO which inherits from Data
                const Data*      lhs_data = static_cast< const Data* >( this );
                const Data*      rhs_data = static_cast< const Data* >( &RHS );
                return lhs_data->operator==( *rhs_data );
            }

            //-----------------------------------------------------------------
            /// \brief Equality operator for base object
            //-----------------------------------------------------------------
            template < typename T >
            bool
            ClassicIO< T >::
            operator==( const Common::FrameSpec::Object& RHS ) const
            {
                const FrHeader* rhs = dynamic_cast< const FrHeader* >( &RHS );
                if ( !rhs )
                    return false;

                return ( *this ) ==
                    ( *rhs ); // Use the FrHeader operator== above
            }

            // Explicit template instantiation
            template class ClassicIO< FrHeader >;
        } // namespace FrHeaderImpl

    } // namespace Universal
} // namespace FrameCPP

//=======================================================================
//               F R   O B J E C T   M E T A   D A T A
//=======================================================================
namespace FrameCPP
{
    namespace Universal
    {
        namespace Impl
        {
            // FrHeader metadata implementation
            template <>
            FrObjectMetaData< FrHeaderImpl::Data >::versioned_info_type const
                FrObjectMetaData< FrHeaderImpl::Data >::versioned_info =
                    std::map< int,
                              FrObjectMetaData< FrHeaderImpl::Data >::Info >{
                        { 3, { "FrHeader", "Frame header structure" } }
                    };

            template <>
            FrObjectMetaData< FrHeaderImpl::Data >::struct_id_type const
                FrObjectMetaData< FrHeaderImpl::Data >::struct_id =
                    Common::FrameSpec::Info::FSI_FR_HEADER;
        } // namespace Impl
    } // namespace Universal
} // namespace FrameCPP
