//
// 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 <boost/shared_ptr.hpp>

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

#include "framecpp/Universal/impl/FrameSpec.hh"
#include "framecpp/Universal/FrHistory.hh"
#include "framecpp/Universal/FrSE.hh"
#include "framecpp/Universal/FrSH.hh"
#include "framecpp/Universal/PTR_STRUCT.hh"


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

//=======================================================================
// Static
//=======================================================================
FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( FrHistoryImpl,
                                      FSI_FR_HISTORY,
                                      "FrHistory",
                                      "History Structure" )

namespace FrameCPP
{
    namespace Universal
    {
        //=======================================================================
        // FrHistory::fr_history_data_type
        //=======================================================================

        constexpr FrHistory::time_type DEFAULT_TIME = 0;

        //=======================================================================
        // FrHistory
        /// The constuction of the default FrHistory.
        //=======================================================================
        FrHistory::FrHistory( )
        {
            time = DEFAULT_TIME;
        }

        //-----------------------------------------------------------------------
        /// Constructs a new FrHistory based on a previously initialized
        /// FrHistory instance.
        //-----------------------------------------------------------------------
        FrHistory::FrHistory( const FrHistory& Source )
        {
            name = Source.name;
            time = Source.time;
            comment = Source.comment;
        }

        //-----------------------------------------------------------------------
        /// Construct a new instance with values for all important
        //-----------------------------------------------------------------------
        FrHistory::FrHistory( const std::string& Name,
                              time_type          Time,
                              const std::string& Comment )
        {
            name = Name;
            time = Time;
            comment = Comment;
        }

        std::string
        FrHistory::ErrorInfo( ) const
        {
            std::ostringstream result;

            result << "FrameCPP::Universal::FrHistory: {Name: " << name
                   << " Time: " << time << "} ";
            return result.str( );
        }

        //-----------------------------------------------------------------
        /// \brief Return the name associate with the FrHistory
        /// structure.
        ///
        /// \return
        ///     The name associated with the FrHistory structure
        //-----------------------------------------------------------------
        const FrHistory::name_type&
        FrHistory::GetName( ) const
        {
            return Data::GetName( );
        }

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

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

        const char*
        FrHistory::ObjectStructName( ) const
        {
            return StructName( );
        }

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

                Description* description = nullptr;


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

                    if ( description_v8.size( ) == 0 )
                    {
                        description_v8( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );
                        description_v8(
                            FrSE( "name",
                                  Description::Ascii< v8::io_name_type >( ),
                                  "Name of history record" ) );
                        description_v8(
                            FrSE( "time",
                                  Description::Ascii< Data::v8::time_type >( ),
                                  "Time of post-processing." ) );
                        description_v8(
                            FrSE( "comment",
                                  Description::Ascii< v8::io_comment_type >( ),
                                  "Program name and relevant comments needed to"
                                  " define post-processing" ) );

                        description_v8( FrSE(
                            "next",
                            PTR_STRUCT::Desc( Data::FrameSpecName( ) ),
                            "Identifier for next history structure in the "
                            "linked list" ) );

                        description_v8( FrSE( "chkSum",
                                              CheckSumDataClass( ),
                                              CheckSumDataComment( ) ) );
                    }

                    description = &description_v8;
                }
                else if ( frame_stream_version >= 3 )
                {
                    static Description description_v3;

                    if ( description_v3.size( ) == 0 )
                    {
                        description_v3( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              "History Structure" ) );
                        description_v3(
                            FrSE( "name",
                                  Description::Ascii< v3::io_name_type >( ),
                                  "Name of history record" ) );
                        description_v3(
                            FrSE( "time",
                                  Description::Ascii< Data::v3::time_type >( ),
                                  "Time of post-processing." ) );
                        description_v3(
                            FrSE( "comment",
                                  Description::Ascii< v3::io_comment_type >( ),
                                  "Program name and relevant comments needed to"
                                  " define post-processing" ) );

                        description_v3( FrSE(
                            "next",
                            PTR_STRUCT::Desc( FrHistory::StructName( ) ),
                            "Identifier for next history structure in the "
                            "linked list" ) );
                    }

                    description = &description_v3;
                }
                return ( description );
            }

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

    } // namespace Universal
} // namespace FrameCPP
