//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2018 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_config.h>

#include <memory>

#include <boost/shared_ptr.hpp>

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

#include "framecpp/Universal/FrMsg.hh"
#include "framecpp/Universal/FrSH.hh"
#include "framecpp/Universal/FrSE.hh"
#include "framecpp/Universal/PTR_STRUCT.hh"


using FrameCPP::Common::Description;
using FrameCPP::Common::FrameSpec;

//=======================================================================
// Static
//=======================================================================

FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( FrMsgImpl,
                                      FSI_FR_MSG,
                                      "FrMsg",
                                      "Message Data Structure" )

namespace FrameCPP
{
    namespace Universal
    {
        //=======================================================================
        // FrMsg
        //=======================================================================
        FrMsg::FrMsg( )
        {
            severity = DEFAULT_SEVERITY;
        }

        FrMsg::FrMsg( const FrMsg& Source )
        {
            alarm = Source.GetAlarm( );
            message = Source.GetMessage( );
            severity = Source.GetSeverity( );
            GTime = Source.GetGTime( );
        }

        FrMsg::FrMsg( const std::string& Alarm,
                      const std::string& Message,
                      INT_4U             Severity,
                      const GPSTime&     GTimeValue )
        {
            alarm = Alarm;
            message = Message;
            severity = Severity;
            GTime = GTimeValue;
        }

        FrMsg::~FrMsg( )
        {
        }

        const FrMsg::alarm_type&
        FrMsg::GetAlarm( ) const
        {

            return FrMsgImpl::Data::GetAlarm( );
        }

        FrMsg&
        FrMsg::Merge( const FrMsg& RHS )
        {
            /// \todo
            ///   Need to implement Merge routine
            std::string msg( "Merge currently not implemented for " );
            msg += FrameSpecName( );

            throw std::domain_error( msg );
            return *this;
        }

        namespace FrMsgImpl
        {
            template <>
            const Common::Description*
            ClassicIO< FrMsg >::GetDescription( unsigned int frame_stream_version ) const
            {
                using Common::Description;
                using Universal::PTR_STRUCT;

                Description* retval = nullptr;


                if ( frame_stream_version >= 8 )
                {
                    static Description description_v8;

                    if ( description_v8.size( ) == 0 )
                    {
                        description_v8( FrSH( FrameSpecName( ),
                                              FrameSpecId( ),
                                              "Msg Data Structure" ) );

                        description_v8(
                            FrSE( "alarm",
                                  Description::Ascii< io_alarm_type >( ),
                                  "Name of message, error flag or alarm "
                                  "state" ) );
                        description_v8(
                            FrSE( "message",
                                  Description::Ascii< io_message_type >( ),
                                  "Message body" ) );
                        description_v8(
                            FrSE( "severity",
                                  Description::Ascii< severity_type >( ),
                                  "Message severify level (To Be Defined); "
                                  "default = 0" ) );
                        description_v8(
                            FrSE( "GTimeS",
                                  Description::Ascii< io_gtimes_type >( ),
                                  "GPS time in secondes corresponding to this "
                                  "FrMsg" ) );
                        description_v8(
                            FrSE( "GTimeN",
                                  Description::Ascii< io_gtimen_type >( ),
                                  "GPS time in residual nanoseconds relative "
                                  "to GTimeS" ) );
                        description_v8(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( FrMsg::StructName( ) ),
                                  "" ) );
                        description_v8( FrSE( "chkSum",
                                              CheckSumDataClass( ),
                                              CheckSumDataComment( ) ) );
                    }
                    retval = &description_v8;
                }
                else if ( frame_stream_version >= 5 )
                {
                    static Description description_v5;

                    if ( description_v5.size( ) == 0 )
                    {
                        description_v5( FrSH( FrMsg::StructName( ),
                                              FrMsg::FrameSpecId( ),
                                              "Msg Data Structure" ) );

                        description_v5(
                            FrSE( "alarm",
                                  Description::Ascii< io_alarm_type >( ),
                                  "Name of message, error flag or alarm "
                                  "state" ) );
                        description_v5(
                            FrSE( "message",
                                  Description::Ascii< io_message_type >( ),
                                  "Message body" ) );
                        description_v5(
                            FrSE( "severity",
                                  Description::Ascii< severity_type >( ),
                                  "Message severify level (To Be Defined); "
                                  "default = 0" ) );
                        description_v5(
                            FrSE( "GTimeS",
                                  Description::Ascii< io_gtimes_type >( ),
                                  "GPS time in secondes corresponding to this "
                                  "FrMsg" ) );
                        description_v5(
                            FrSE( "GTimeN",
                                  Description::Ascii< io_gtimen_type >( ),
                                  "GPS time in residual nanoseconds relative "
                                  "to GTimeS" ) );
                        description_v5(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( FrMsg::StructName( ) ),
                                  "" ) );
                    }
                    retval = &description_v5;
                }
                else if ( frame_stream_version >= 3 )
                {
                    static Description description_v3;

                    if ( description_v3.size( ) == 0 )
                    {
                        description_v3( FrSH( FrMsg::StructName( ),
                                              FrMsg::FrameSpecId( ),
                                              "Msg Data Structure" ) );

                        description_v3(
                            FrSE( "alarm",
                                  Description::Ascii< io_alarm_type >( ),
                                  "Name of message, error flag or alarm "
                                  "state" ) );
                        description_v3(
                            FrSE( "message",
                                  Description::Ascii< io_message_type >( ),
                                  "Message body" ) );
                        description_v3(
                            FrSE( "severity",
                                  Description::Ascii< severity_type >( ),
                                  "Message severify level (To Be Defined); "
                                  "default = 0" ) );

                        description_v3(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( FrMsg::StructName( ) ),
                                  "" ) );
                    }
                    retval = &description_v3;
                }
                return ( retval );
            }

        } // namespace FrMsgImpl

        namespace FrMsgImpl
        {
            template <>
            bool
            ClassicIO< FrMsg >::
            operator==( const Common::FrameSpec::Object& Obj ) const
            {
                // Cast *this to FrMsg so the FrameCPP::compare<T>
                // template deduces T = FrMsg and ADL on FrMsg finds
                // the typed hidden-friend operator==.
                return FrameCPP::compare(
                    static_cast< const FrMsg& >( *this ), Obj );
            }
        } // namespace FrMsgImpl
    } // namespace Universal
} // namespace FrameCPP
