//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2018-2020 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 "framecpp/Universal/FrSimData.hh"

#include "framecpp/Common/Description.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
//=======================================================================
//-----------------------------------------------------------------------
// Local functions and variables
//-----------------------------------------------------------------------

FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( FrSimDataImpl,
                                      FSI_FR_SIM_DATA,
                                      "FrSimData",
                                      "Simulated Data Structure" )

namespace FrameCPP
{
    namespace Universal
    {
        //=======================================================================
        // FrSimData
        //=======================================================================
        FrSimData::FrSimData( const name_type&      Name,
                              const comment_type&   Comment,
                              const sampleRate_type SampleRate,
                              const fShift_type     FShift,
                              const phase_type      Phase,
                              const timeOffset_type TimeOffset )
        {
            Data::name = Name;
            Data::comment = Comment;
            Data::sampleRate = SampleRate;
            Data::fShift = FShift;
            Data::phase = Phase;
            Data::timeOffset = TimeOffset;
        }

        const std::string&
        FrSimData::GetNameSlow( ) const
        {
            return GetName( );
        }

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

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

        bool
        FrSimData::operator==( const Common::FrameSpec::Object& Obj ) const
        {
            return FrameCPP::compare( *this, Obj );
        }

    } // namespace Universal
} // namespace FrameCPP

//=======================================================================
//               C L A S S I C   I O
//=======================================================================
namespace FrameCPP
{
    namespace Universal
    {
        namespace FrSimDataImpl
        {
            //-----------------------------------------------------------------
            /// @brief The data structure description
            ///
            /// @return
            ///     The description of the data structure used to represent
            ///     this object when written to a GWF stream
            //-----------------------------------------------------------------
            template <>
            const Common::Description*
            ClassicIO< FrSimData >::GetDescription( unsigned int frame_stream_version ) const
            {
                using Common::Description;


                Description* description{ nullptr };

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

                    if ( v8_description.size( ) == 0 )
                    {
                        //-----------------------------------------------
                        v8_description( FrSH( Data::StructName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        v8_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of simulated data." ) );
                        v8_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Comment" ) );
                        v8_description(
                            FrSE( "sampleRate",
                                  Description::Ascii< v8::sampleRate_type >( ),
                                  "Simulated data sample rate, samples/s" ) );
                        v8_description(
                            FrSE( "timeOffset",
                                  Description::Ascii< v8::timeOffset_type >( ),
                                  "Offset of 1sst sample relative to the "
                                  "frame start time (seconds)."
                                  "Must be positive and smaller than the "
                                  "frame length." ) );
                        v8_description( FrSE(
                            "fShift",
                            Description::Ascii< v8::fShift_type >( ),
                            "fShift is the frequency in the original data "
                            "that corresponds to 0 Hz in teh heterodyned "
                            "series." ) );
                        v8_description(
                            FrSE( "phase",
                                  Description::Ascii< v8::phase_type >( ),
                                  "Phase of heterodyning signal at start of "
                                  "dataset." ) );

                        v8_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier of array of simulated data." ) );
                        v8_description(
                            FrSE( "input",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier for input parameters for "
                                  "simulation." ) );
                        v8_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "Identifier for table data structure." ) );

                        v8_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( ) ),
                                  "Identifier for next simulated data "
                                  "structure in the linked list" ) );

                        v8_description( FrSE( "chkSum",
                                              CheckSumDataClass( ),
                                              CheckSumDataComment( ) ) );
                    }
                    description = &v8_description;
                }
                else if ( frame_stream_version >= 7 )
                {
                    static Description v7_description;

                    if ( v7_description.size( ) == 0 )
                    {
                        //-----------------------------------------------
                        v7_description( FrSH( Data::StructName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        v7_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of simulated data." ) );
                        v7_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Comment" ) );
                        v7_description(
                            FrSE( "sampleRate",
                                  Description::Ascii< v7::sampleRate_type >( ),
                                  "Simulated data sample rate, samples/s" ) );
                        v7_description(
                            FrSE( "timeOffset",
                                  Description::Ascii< v7::timeOffset_type >( ),
                                  "Offset of 1sst sample relative to the "
                                  "frame start time (seconds)."
                                  "Must be positive and smaller than the "
                                  "frame length." ) );
                        v7_description( FrSE(
                            "fShift",
                            Description::Ascii< v7::fShift_type >( ),
                            "fShift is the frequency in the original data "
                            "that corresponds to 0 Hz in teh heterodyned "
                            "series." ) );
                        v7_description(
                            FrSE( "phase",
                                  Description::Ascii< v7::phase_type >( ),
                                  "Phase of heterodyning signal at start of "
                                  "dataset." ) );

                        v7_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier of array of simulated data." ) );
                        v7_description(
                            FrSE( "input",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier for input parameters for "
                                  "simulation." ) );
                        v7_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "Identifier for table data structure." ) );

                        v7_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( ) ),
                                  "Identifier for next simulated data "
                                  "structure in the linked list" ) );
                    }
                    description = &v7_description;
                }
                else if ( frame_stream_version >= 5 )
                {
                    static Description v5_description;

                    if ( v5_description.size( ) == 0 )
                    {
                        //-----------------------------------------------
                        v5_description( FrSH( Data::StructName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        v5_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of simulated data." ) );
                        v5_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Comment" ) );
                        v5_description(
                            FrSE( "sampleRate",
                                  Description::Ascii< v5::sampleRate_type >( ),
                                  "Simulated data sample rate, samples/s" ) );
                        v5_description(
                            FrSE( "timeOffset",
                                  Description::Ascii< v5::timeOffset_type >( ),
                                  "Offset of 1sst sample relative to the "
                                  "frame start time (seconds)."
                                  "Must be positive and smaller than the "
                                  "frame length." ) );
                        v5_description( FrSE(
                            "fShift",
                            Description::Ascii< v5::fShift_type >( ),
                            "fShift is the frequency in the original data "
                            "that corresponds to 0 Hz in teh heterodyned "
                            "series." ) );
                        v5_description(
                            FrSE( "phase",
                                  Description::Ascii< v5::phase_type >( ),
                                  "Phase of heterodyning signal at start of "
                                  "dataset." ) );

                        v5_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier of array of simulated data." ) );
                        v5_description(
                            FrSE( "input",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier for input parameters for "
                                  "simulation." ) );
                        v5_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "Identifier for table data structure." ) );

                        v5_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( ) ),
                                  "Identifier for next simulated data "
                                  "structure in the linked list" ) );
                    }
                    description = &v5_description;
                }
                else if ( frame_stream_version >= 4 )
                {
                    static Description v4_description;

                    if ( v4_description.size( ) == 0 )
                    {
                        //-----------------------------------------------
                        v4_description( FrSH( Data::StructName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        v4_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of simulated data." ) );
                        v4_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Comment" ) );
                        v4_description(
                            FrSE( "sampleRate",
                                  Description::Ascii< v4::sampleRate_type >( ),
                                  "Simulated data sample rate, samples/s" ) );

                        v4_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier of array of simulated data." ) );
                        v4_description(
                            FrSE( "input",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier for input parameters for "
                                  "simulation." ) );
                        v4_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "Identifier for table data structure." ) );

                        v4_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( ) ),
                                  "Identifier for next simulated data "
                                  "structure in the linked list" ) );
                    }
                    description = &v4_description;
                }
                else if ( frame_stream_version >= 3 )
                {
                    static Description v3_description;

                    if ( v3_description.size( ) == 0 )
                    {
                        //-----------------------------------------------
                        v3_description( FrSH( Data::StructName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        v3_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of simulated data." ) );
                        v3_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Comment" ) );
                        v3_description(
                            FrSE( "sampleRate",
                                  Description::Ascii< v3::sampleRate_type >( ),
                                  "Simulated data sample rate, samples/s" ) );

                        v3_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier of array of simulated data." ) );
                        v3_description(
                            FrSE( "input",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier for input parameters for "
                                  "simulation." ) );

                        v3_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( ) ),
                                  "Identifier for next simulated data "
                                  "structure in the linked list" ) );
                    }
                    description = &v3_description;
                }
                return description;
            }

            template <>
            cmn_streamsize_type
            ClassicIO< FrSimData >::pBytes(
                const Common::StreamBase& Stream ) const
            {
                auto frame_stream_version = StreamVersion( Stream );

                return ( io_name_type::Bytes( name ) +
                         io_comment_type::Bytes( comment ) +
                         ( ( frame_stream_version >= 7 ) // sampleRate
                               ? sizeof( v7::sampleRate_type )
                               : sizeof( v3::sampleRate_type ) ) +
                         ( ( frame_stream_version >= 5 ) //
                               ? ( sizeof( v5::timeOffset_type ) + //
                                   sizeof( v5::fShift_type ) + //
                                   sizeof( v5::phase_type ) )
                               : 0 ) +
                         Stream.PtrStructBytes( ) // data
                         + Stream.PtrStructBytes( ) // input
                         + ( ( frame_stream_version >= 4 ) // table
                                 ? Stream.PtrStructBytes( )
                                 : 0 ) // table
                         + Stream.PtrStructBytes( ) // next
                );
            }

            template <>
            ClassicIO< FrSimData >::self_type*
            ClassicIO< FrSimData >::pCreate( istream_type& Stream ) const
            {
                auto frame_stream_version = StreamVersion( Stream );

                FrSimData* obj( new FrSimData( ) );

                io_name_type    io_name;
                io_comment_type io_comment;

                Stream >> io_name >> io_comment;
                if ( frame_stream_version >= 7 )
                {
                    Stream >> obj->sampleRate;
                }
                else if ( frame_stream_version >= 3 )
                {
                    v3::sampleRate_type sampleRate;

                    Stream >> sampleRate;
                    obj->sampleRate = sampleRate;
                }
                if ( frame_stream_version >= 5 )
                {
                    Stream >> obj->timeOffset >> obj->fShift >> obj->phase;
                }

                Stream >> obj->data >> obj->input;
                if ( frame_stream_version >= 4 )
                {
                    Stream >> obj->table;
                }

                Stream.Next( obj );

                obj->name = io_name;
                obj->comment = io_comment;

                return ( obj );
            }

            template <>
            void
            ClassicIO< FrSimData >::pWrite( ostream_type& Stream ) const
            {
                auto frame_stream_version = StreamVersion( Stream );

                Stream << io_name_type( name ) << io_comment_type( comment );
                if ( frame_stream_version >= 7 )
                {
                    Stream << sampleRate;
                }
                else if ( frame_stream_version >= 3 )
                {
                    Stream << v3::sampleRate_type( sampleRate );
                }
                if ( frame_stream_version >= 5 )
                {
                    Stream << timeOffset << fShift << phase;
                }
                Stream << data << input;
                if ( frame_stream_version >= 4 )
                {
                    Stream << table;
                }
                WriteNext( Stream );
            }
        } // namespace FrSimDataImpl
    } // namespace Universal
} // namespace FrameCPP
