//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2019 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__FrMsgSerialIO_HH
#define FrameCPP__UNIVERSAL__IMPL__FrMsgSerialIO_HH

#include <boost/serialization/nvp.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/version.hpp>

#include "framecpp/Common/FrameSpec.hh"

#include "framecpp/Universal/impl/HelperSerialIO.hh"
#include "framecpp/Universal/impl/FrMsgData.hh"

namespace FrameCPP
{
    namespace Universal
    {
        class FrMsg;
        namespace FrMsgImpl
        {
            /// \brief Serialization I/O implementation for FrMsg data
            /// structures
            ///
            /// This class provides versioned serialization support for FrMsg
            /// (Message) objects using Boost.Serialization. It handles the
            /// evolution of the FrMsg data structure across different frame
            /// specification versions (v3 through v9).
            ///
            /// \tparam FR_MSG_T The FrMsg type being serialized (typically
            /// FrMsg)
            template < typename FR_MSG_T >
            class SerialIO : public virtual Data
            {
            public:
                /// \brief Default constructor
                ///
                /// Constructs an empty SerialIO object ready for serialization
                /// operations. The object inherits data members from the Data
                /// base class.
                SerialIO( )
                {
                }
                
                SerialIO( const SerialIO& ) = default;
                SerialIO( SerialIO&& ) = default;
                SerialIO& operator=( const SerialIO& ) = delete;
                SerialIO& operator=( SerialIO&& ) = delete;

            private:
                friend class boost::serialization::access;

                /// \brief Serialize FrMsg data to an archive
                ///
                /// This method saves the FrMsg data to a serialization archive,
                /// handling version-specific data formats. The GTime field was
                /// introduced in v5, so older versions only include the basic
                /// message fields.
                ///
                /// \tparam Archive The Boost.Serialization archive type
                ///
                /// \param[in,out] ar The archive to write data to
                /// \param[in] version The target serialization version (3-9)
                ///
                /// \throws boost::archive::archive_exception on serialization
                /// errors
                ///
                /// Version-specific handling:
                /// - v3-4: Includes alarm, message, severity fields
                /// - v5+: Adds GTime field for GPS time information
                ///
                /// \note GTime field is omitted for versions prior to v5
                /// \note All string fields are serialized consistently across
                /// versions
                template < class Archive >
                void
                save( Archive& ar, const unsigned int version ) const
                {
                    using boost::serialization::make_nvp;

                    if ( version >= 3 )
                    {
                        // Basic message fields (available in all versions)
                        ar& make_nvp( "alarm", alarm ) &
                            make_nvp( "message", message ) &
                            make_nvp( "severity", severity );

                        // GPS time field (v5+ only)
                        if ( version >= 5 )
                        {
                            ar& make_nvp( "GTime", GTime );
                        }
                    }
                    // Note: FrMsg structure was introduced in v3, so no
                    // handling for earlier versions
                }

                /// \brief Deserialize FrMsg data from an archive
                ///
                /// This method loads FrMsg data from a serialization archive,
                /// handling version-specific data formats. For versions prior
                /// to v5, the GTime field is initialized with a default value.
                ///
                /// \tparam Archive The Boost.Serialization archive type
                ///
                /// \param[in,out] ar The archive to read data from
                /// \param[in] version The source serialization version (3-9)
                ///
                /// \throws boost::archive::archive_exception on deserialization
                /// errors
                ///
                /// Version-specific handling:
                /// - v3-4: Loads alarm, message, severity; initializes GTime
                /// to default - v5+: Loads all fields including GTime
                /// - Pre-v3: Initializes all fields with default values
                ///
                /// \note Default GTime is used for versions without GPS time
                /// support \note All fields are properly initialized for
                /// unsupported versions
                ///
                /// \warning The caller must ensure the archive contains valid
                /// FrMsg data
                template < class Archive >
                void
                load( Archive& ar, const unsigned int version )
                {
                    using boost::serialization::make_nvp;

                    if ( version >= 3 )
                    {
                        // Basic message fields (available in all versions)
                        ar& make_nvp( "alarm", alarm ) &
                            make_nvp( "message", message ) &
                            make_nvp( "severity", severity );

                        // GPS time field (v5+ only)
                        if ( version >= 5 )
                        {
                            ar& make_nvp( "GTime", GTime );
                        }
                        else
                        {
                            // v3/v4: Initialize GTime with default value
                            GTime = GTime_type( );
                        }
                    }
                    else
                    {
                        // Pre-v3: Initialize all fields with default values
                        alarm.clear( );
                        message.clear( );
                        severity = 0;
                        GTime = GTime_type( );
                    }
                }

                /// \brief Boost.Serialization split member macro
                ///
                /// This macro splits the serialize() method into separate
                /// save() and load() methods, allowing for different logic
                /// during serialization and deserialization. This is necessary
                /// for handling version differences, particularly the GTime field
                /// addition.
                BOOST_SERIALIZATION_SPLIT_MEMBER( )
            };

        } // namespace FrMsgImpl
    } // namespace Universal
} // namespace FrameCPP

/// \brief Specify the class version for Boost.Serialization
///
/// This macro tells Boost.Serialization that FrMsgImpl::Data is at version 9,
/// which corresponds to the v9 frame specification. The serialization framework
/// uses this version number to determine how to serialize/deserialize the data.
///
/// \note This version number is passed to the save() and load() methods
BOOST_CLASS_VERSION( FrameCPP::Universal::FrMsgImpl::Data, 9 )

#endif /* FrameCPP__UNIVERSAL__IMPL__FrMsgSerialIO_HH */
