//
// 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__FrHeaderSerialIO_HH
#define FrameCPP__UNIVERSAL__IMPL__FrHeaderSerialIO_HH

#include "framecpp/Universal/impl/FrHeaderData.hh"

#if !defined( SWIG )
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/version.hpp>
#endif /* !defined( SWIG ) */

namespace FrameCPP
{
    namespace Universal
    {
        namespace FrHeaderImpl
        {
#if !defined( SWIG )
            template < typename T >
            class SerialIO : public virtual Data
            {
            public:
                // Default constructor
                SerialIO() = default;
                
                // Copy and move operations
                SerialIO(const SerialIO&) = default;
                SerialIO(SerialIO&&) = default;
                SerialIO& operator=(const SerialIO&) = delete;
                SerialIO& operator=(SerialIO&&) = delete;

                template < class Archive >
                void serialize( Archive& archiver, const unsigned int version )
                {
                    using boost::serialization::make_nvp;
                    using boost::serialization::base_object;
                    
                    // Serialize the Common::FrHeader base class first (bytes 0-6)
                    archiver & BOOST_SERIALIZATION_BASE_OBJECT_NVP( Common::FrHeader );
                    
                    // Serialize frame-specific fields (bytes 7-39) with correct names
                    archiver & BOOST_SERIALIZATION_NVP( SizeOf_INT_2 );
                    archiver & BOOST_SERIALIZATION_NVP( SizeOf_INT_4 );
                    archiver & BOOST_SERIALIZATION_NVP( SizeOf_INT_8 );
                    archiver & BOOST_SERIALIZATION_NVP( SizeOf_REAL_4 );
                    archiver & BOOST_SERIALIZATION_NVP( SizeOf_REAL_8 );
                    archiver & BOOST_SERIALIZATION_NVP( Endian_2_byte );
                    archiver & BOOST_SERIALIZATION_NVP( Endian_4_byte );
                    archiver & BOOST_SERIALIZATION_NVP( Endian_8_byte );
                    archiver & BOOST_SERIALIZATION_NVP( PI_SINGLE );
                    archiver & BOOST_SERIALIZATION_NVP( PI_DOUBLE );
                    
                    // Handle version-specific fields for bytes 38-39
                    if ( version >= 8 )
                    {
                        // v8+ uses frameLibrary and checksumScheme
                        archiver & BOOST_SERIALIZATION_NVP( m_frameLibrary );
                        archiver & BOOST_SERIALIZATION_NVP( m_checksumScheme );
                    }
                    else
                    {
                        // v3-v7 uses legacy 'A''Z' constant
                        if ( typename Archive::is_saving() )
                        {
                            // When saving, write the legacy constant
                            CHAR legacyMarker[2] = {'A', 'Z'};
                            archiver & make_nvp( "legacyMarker", legacyMarker );
                        }
                        else
                        {
                            // When loading, read the legacy marker but don't use it
                            CHAR legacyMarker[2];
                            archiver & make_nvp( "legacyMarker", legacyMarker );
                            // Set default values for v8+ fields
                            const_cast<SerialIO*>(this)->m_frameLibrary = Common::FrHeader::FRAME_LIBRARY_FRAMECPP;
                            const_cast<SerialIO*>(this)->m_checksumScheme = 0;
                        }
                    }
                    
                    // Detect byte order after loading all fields (only during load operations)
                    if ( typename Archive::is_loading() )
                    {
                        const_cast<SerialIO*>(this)->detectAndSetByteOrder();
                    }
                }
            };
#endif /* !defined( SWIG ) */
        } // namespace FrHeaderImpl
    } // namespace Universal
} // namespace FrameCPP

#if !defined( SWIG )
// Set the version number for FrHeader serialization
// This determines which version of serialize() is called
BOOST_CLASS_VERSION( FrameCPP::Universal::FrHeaderImpl::Data, 9 )
#endif /* !defined( SWIG ) */

#endif /* FrameCPP__UNIVERSAL__IMPL__FrHeaderSerialIO_HH */