//
// 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 <algorithm>

#include <boost/shared_ptr.hpp>

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

#include "framecpp/Universal/FrEvent.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;

#define LM_DEBUG 0

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

FR_OBJECT_META_DATA_DEFINE_MAP_UNIVERSAL(
    FrEventImpl,
    FSI_FR_EVENT,
    { { 3, { "FrTrigData", "Trigger Data Structure" } },
      { 5, { "FrEvent", "Event Data Structure" } } } )

namespace FrameCPP
{
    namespace Universal
    {
        //=======================================================================
        // FrEvent
        //=======================================================================
        FrEvent::FrEvent( )
        {
        }

        FrEvent::FrEvent( const name_type&       name,
                          const comment_type&    comment,
                          const inputs_type&     inputs,
                          const time_type&       time,
                          const timeBefore_type  timeBefore,
                          const timeAfter_type   timeAfter,
                          const eventStatus_type eventStatus,
                          const amplitude_type   amplitude,
                          const probability_type probability,
                          const statistics_type& statistics,
                          const ParamList_type&  parameters )
        {
            Data::name = name;
            Data::comment = comment;
            Data::inputs = inputs;
            Data::GTime = time;
            Data::timeBefore = timeBefore;
            Data::timeAfter = timeAfter;
            Data::eventStatus = eventStatus;
            Data::amplitude = amplitude;
            Data::probability = probability;
            Data::statistics = statistics;
            Data::Params = parameters;
        }

        FrEvent::FrEvent( const FrEvent& Source )
        {
            name = Source.name;
            comment = Source.comment;
            inputs = Source.inputs;
            GTime = Source.GTime;
            timeBefore = Source.timeBefore;
            timeAfter = Source.timeAfter;
            eventStatus = Source.eventStatus;
            amplitude = Source.amplitude;
            probability = Source.probability;
            statistics = Source.statistics;
            Params = Source.Params;
        }

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

        FrEvent&
        FrEvent::Merge( const FrEvent& RHS )
        {
            throw Unimplemented(
                "FrEvent& FrEvent::Merge( const FrEvent& RHS )",
                DATA_FORMAT_VERSION,
                __FILE__,
                __LINE__ );
            return *this;
        }

        bool
        FrEvent::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 FrEventImpl
        {
            inline FrameCPP::Common::OStream&
            operator<<( FrameCPP::Common::OStream&  Stream,
                        const Data::ParamList_type& Data )
            {
                auto frame_stream_version = StreamVersion( Stream );

                if ( frame_stream_version >= 5 )
                {
                    Stream << Data::nParam_type( Data.size( ) );
                    if ( frame_stream_version >= 7 )
                    {
                        for ( auto p : Data )
                        {
                            Stream << p.second; // parameters
                        }
                    }
                    else
                    {
                        for ( auto p : Data )
                        {
                            Stream << Data::v5::paramValue_type(
                                p.second ); // parameters
                        }
                    }
                    for ( auto p : Data )
                    {
                        Stream << STRING( p.first );
                    }
                }
                return ( Stream );
            }

            inline FrameCPP::Common::IStream&
            operator>>( FrameCPP::Common::IStream& Stream,
                        Data::ParamList_type&      Data )
            {
                auto frame_stream_version = StreamVersion( Stream );

                if ( frame_stream_version >= 5 )
                {
                    Data::nParam_type nParam;

                    Stream >> nParam;

                    Data.resize( nParam );
                    if ( frame_stream_version >= 7 )
                    {
                        for ( auto p : Data )
                        {
                            Stream >> p.second; // parameters
                        }
                    }
                    else
                    {
                        for ( auto p : Data )
                        {
                            Data::v5::paramValue_type paramValue;

                            Stream >> paramValue; // parameters
                            p.second = paramValue;
                        }
                    }
                    for ( auto p : Data )
                    {
                        STRING shadow;

                        Stream >> shadow;
                        p.first.swap( shadow );
                    }
                }
                return ( Stream );
            }

            template <>
            const Common::Description*
            ClassicIO< FrEvent >::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::FrameSpecName( 8 ),
                                  Data::FrameSpecId( ),
                                  Data::FrameSpecDescription( 8 ) ) );

                        v8_description(
                            FrSE( "name",
                                  Description::Ascii< v8::name_type >( ),
                                  "Name of event." ) );
                        v8_description(
                            FrSE( "comment",
                                  Description::Ascii< v8::comment_type >( ),
                                  "Descriptor of event" ) );
                        v8_description( FrSE(
                            "inputs",
                            Description::Ascii< v8::inputs_type >( ),
                            "Input channels and filter parameters to event "
                            "process." ) );
                        v8_description(
                            FrSE( "GTimeS",
                                  Description::Ascii< v8::GTimeS_type >( ),
                                  "GPS time in seconds corresponding to "
                                  "reference "
                                  "vale of event,"
                                  " as defined by the search algorigthm." ) );
                        v8_description(
                            FrSE( "GTimeN",
                                  Description::Ascii< v8::GTimeN_type >( ),
                                  "GPS time in residual nanoseconds relative "
                                  "to GTimeS" ) );
                        v8_description(
                            FrSE( "timeBefore",
                                  Description::Ascii< v8::timeBefore_type >( ),
                                  "Signal duration before "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v8_description(
                            FrSE( "timeAfter",
                                  Description::Ascii< v8::timeAfter_type >( ),
                                  "Signal duration after "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v8_description(
                            FrSE( "eventStatus",
                                  Description::Ascii< v8::eventStatus_type >( ),
                                  "Defined bby event search algorithm" ) );
                        v8_description(
                            FrSE( "amplitude",
                                  Description::Ascii< v8::amplitude_type >( ),
                                  "Continuouis output amplitude returned by "
                                  "event" ) );
                        v8_description(
                            FrSE( "probability",
                                  Description::Ascii< v8::probability_type >( ),
                                  "Likelihood estimate of event, if available"
                                  " (probability = -1 if cannot be "
                                  "estimated)" ) );
                        v8_description(
                            FrSE( "statistics",
                                  Description::Ascii< v8::statistics_type >( ),
                                  "Statistical description of "
                                  "event, if relevant or "
                                  "available" ) );
                        v8_description(
                            FrSE( "nParam",
                                  Description::Ascii< v8::nParam_type >( ),
                                  "Number of additional event parameters" ) );
                        v8_description( FrSE(
                            "parameters",
                            Description::AsciiArray< v8::paramValue_type >(
                                "nParam" ),
                            "Array of additional event paraameters(size "
                            "of nParam)" ) );
                        v8_description(
                            FrSE( "parameterNames",
                                  Description::AsciiArray< v8::paramName_type >(
                                      "nParam" ),
                                  "Array of parameter names "
                                  "(size of nParam)." ) );
                        v8_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Identifier for vector containing additional "
                                  "event results." ) );
                        v8_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "Identifier for table structure containing "
                                  "additional event information." ) );
                        v8_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( 8 ) ),
                                  "Identifier for another event." ) );

                        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::FrameSpecName( 7 ),
                                  Data::FrameSpecId( ),
                                  Data::FrameSpecDescription( 7 ) ) );

                        v7_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of event." ) );
                        v7_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Descriptor of event" ) );
                        v7_description( FrSE(
                            "inputs",
                            Description::Ascii< io_inputs_type >( ),
                            "Input channels and filter parameters to event "
                            "process." ) );
                        v7_description(
                            FrSE( "GTimeS",
                                  Description::Ascii< v7::GTimeS_type >( ),
                                  "GPS time in seconds corresponding to "
                                  "reference "
                                  "vale of event,"
                                  " as defined by the search algorigthm." ) );
                        v7_description(
                            FrSE( "GTimeN",
                                  Description::Ascii< v7::GTimeN_type >( ),
                                  "GPS time in residual nanoseconds relative "
                                  "to GTimeS" ) );
                        v7_description(
                            FrSE( "timeBefore",
                                  Description::Ascii< v7::timeBefore_type >( ),
                                  "Signal duration before "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v7_description(
                            FrSE( "timeAfter",
                                  Description::Ascii< v7::timeAfter_type >( ),
                                  "Signal duration after "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v7_description(
                            FrSE( "eventStatus",
                                  Description::Ascii< v7::eventStatus_type >( ),
                                  "Defined by event search algorithm" ) );
                        v7_description(
                            FrSE( "amplitude",
                                  Description::Ascii< v7::amplitude_type >( ),
                                  "Continuouis output amplitude returned by "
                                  "event" ) );
                        v7_description(
                            FrSE( "probability",
                                  Description::Ascii< v7::probability_type >( ),
                                  "Likelihood estimate of event, if available"
                                  " (probability = -1 if cannot be "
                                  "estimated)" ) );
                        v7_description( FrSE(
                            "statistics",
                            Description::Ascii< io_statistics_type >( ),
                            "Statistical description of event, if relevant "
                            "or "
                            "available" ) );
                        v7_description(
                            FrSE( "nParam",
                                  Description::Ascii< v7::nParam_type >( ),
                                  "Number of additional event parameters" ) );
                        v7_description( FrSE(
                            "parameters",
                            Description::AsciiArray< v7::paramValue_type >(
                                "nParam" ),
                            "Array of additional event paraameters(size "
                            "of nParam)" ) );
                        v7_description(
                            FrSE( "parameterNames",
                                  Description::AsciiArray< v7::paramName_type >(
                                      "nParam" ),
                                  "Array of parameter names "
                                  "(size of nParam)." ) );
                        v7_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "" ) );
                        v7_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "" ) );
                        v7_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( 7 ) ),
                                  "" ) );
                    }
                    description = &v7_description;
                }
                else if ( frame_stream_version >= 6 )
                {
                    static Description v6_description;

                    if ( v6_description.size( ) == 0 )
                    {
                        v6_description(
                            FrSH( Data::FrameSpecName( 6 ),
                                  Data::FrameSpecId( ),
                                  Data::FrameSpecDescription( 6 ) ) );

                        v6_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of event." ) );
                        v6_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Descriptor of event" ) );
                        v6_description( FrSE(
                            "inputs",
                            Description::Ascii< io_inputs_type >( ),
                            "Input channels and filter parameters to event "
                            "process." ) );
                        v6_description(
                            FrSE( "GTimeS",
                                  Description::Ascii< v6::GTimeS_type >( ),
                                  "GPS time in seconds corresponding to "
                                  "reference "
                                  "vale of event,"
                                  " as defined by the search algorigthm." ) );
                        v6_description(
                            FrSE( "GTimeN",
                                  Description::Ascii< v6::GTimeN_type >( ),
                                  "GPS time in residual nanoseconds relative "
                                  "to GTimeS" ) );
                        v6_description(
                            FrSE( "timeBefore",
                                  Description::Ascii< v6::timeBefore_type >( ),
                                  "Signal duration before "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v6_description(
                            FrSE( "timeAfter",
                                  Description::Ascii< v6::timeAfter_type >( ),
                                  "Signal duration after "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v6_description(
                            FrSE( "eventStatus",
                                  Description::Ascii< v6::eventStatus_type >( ),
                                  "Defined by event search algorithm" ) );
                        v6_description(
                            FrSE( "amplitude",
                                  Description::Ascii< v6::amplitude_type >( ),
                                  "Continuouis output amplitude returned by "
                                  "event" ) );
                        v6_description(
                            FrSE( "probability",
                                  Description::Ascii< v6::probability_type >( ),
                                  "Likelihood estimate of event, if available"
                                  " (probability = -1 if cannot be "
                                  "estimated)" ) );
                        v6_description( FrSE(
                            "statistics",
                            Description::Ascii< io_statistics_type >( ),
                            "Statistical description of event, if relevant "
                            "or "
                            "available" ) );
                        v6_description(
                            FrSE( "nParam",
                                  Description::Ascii< v6::nParam_type >( ),
                                  "Number of additional event parameters" ) );
                        v6_description( FrSE(
                            "parameters",
                            Description::AsciiArray< v6::paramValue_type >(
                                "nParam" ),
                            "Array of additional event paraameters(size "
                            "of nParam)" ) );
                        v6_description(
                            FrSE( "parameterNames",
                                  Description::AsciiArray< v6::paramName_type >(
                                      "nParam" ),
                                  "Array of parameter names "
                                  "(size of nParam)." ) );
                        v6_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "" ) );
                        v6_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "" ) );
                        v6_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( 6 ) ),
                                  "" ) );
                    }
                    description = &v6_description;
                }
                else if ( frame_stream_version >= 5 )
                {
                    static Description v5_description;

                    if ( v5_description.size( ) == 0 )
                    {
                        v5_description(
                            FrSH( Data::FrameSpecName( 5 ),
                                  Data::FrameSpecId( ),
                                  Data::FrameSpecDescription( 5 ) ) );

                        v5_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of event." ) );
                        v5_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Descriptor of event" ) );
                        v5_description( FrSE(
                            "inputs",
                            Description::Ascii< io_inputs_type >( ),
                            "Input channels and filter parameters to event "
                            "process." ) );
                        v5_description(
                            FrSE( "GTimeS",
                                  Description::Ascii< v5::GTimeS_type >( ),
                                  "GPS time in seconds corresponding to "
                                  "reference "
                                  "vale of event,"
                                  " as defined by the search algorigthm." ) );
                        v5_description(
                            FrSE( "GTimeN",
                                  Description::Ascii< v5::GTimeN_type >( ),
                                  "GPS time in residual nanoseconds relative "
                                  "to GTimeS" ) );
                        v5_description(
                            FrSE( "timeBefore",
                                  Description::Ascii< v5::timeBefore_type >( ),
                                  "Signal duration before "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v5_description(
                            FrSE( "timeAfter",
                                  Description::Ascii< v5::timeAfter_type >( ),
                                  "Signal duration after "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v5_description(
                            FrSE( "eventStatus",
                                  Description::Ascii< v5::eventStatus_type >( ),
                                  "Defined by event search algorithm" ) );
                        v5_description(
                            FrSE( "amplitude",
                                  Description::Ascii< v5::amplitude_type >( ),
                                  "Continuouis output amplitude returned by "
                                  "event" ) );
                        v5_description(
                            FrSE( "probability",
                                  Description::Ascii< v5::probability_type >( ),
                                  "Likelihood estimate of event, if available"
                                  " (probability = -1 if cannot be "
                                  "estimated)" ) );
                        v5_description( FrSE(
                            "statistics",
                            Description::Ascii< io_statistics_type >( ),
                            "Statistical description of event, if relevant "
                            "or "
                            "available" ) );
                        v5_description(
                            FrSE( "nParam",
                                  Description::Ascii< v5::nParam_type >( ),
                                  "Number of additional event parameters" ) );
                        v5_description( FrSE(
                            "parameters",
                            Description::AsciiArray< v5::paramValue_type >(
                                "nParam" ),
                            "Array of additional event paraameters(size "
                            "of nParam)" ) );
                        v5_description(
                            FrSE( "parameterNames",
                                  Description::AsciiArray< v5::paramName_type >(
                                      "nParam" ),
                                  "Array of parameter names "
                                  "(size of nParam)." ) );
                        v5_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "" ) );
                        v5_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "" ) );
                        v5_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( 5 ) ),
                                  "" ) );
                    }
                    description = &v5_description;
                }
                else if ( frame_stream_version >= 4 )
                {
                    static Description v4_description;

                    if ( v4_description.size( ) == 0 )
                    {
                        v4_description(
                            FrSH( Data::FrameSpecName( 4 ),
                                  Data::FrameSpecId( ),
                                  Data::FrameSpecDescription( 4 ) ) );

                        v4_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of event." ) );
                        v4_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Descriptor of event" ) );
                        v4_description( FrSE(
                            "inputs",
                            Description::Ascii< io_inputs_type >( ),
                            "Input channels and filter parameters to event "
                            "process." ) );
                        v4_description(
                            FrSE( "GTimeS",
                                  Description::Ascii< v4::GTimeS_type >( ),
                                  "GPS time in seconds corresponding to "
                                  "reference "
                                  "vale of event,"
                                  " as defined by the search algorigthm." ) );
                        v4_description(
                            FrSE( "GTimeN",
                                  Description::Ascii< v4::GTimeN_type >( ),
                                  "GPS time in residual nanoseconds relative "
                                  "to GTimeS" ) );
                        v4_description(
                            FrSE( "timeBefore",
                                  Description::Ascii< v4::timeBefore_type >( ),
                                  "Signal duration before "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v4_description(
                            FrSE( "timeAfter",
                                  Description::Ascii< v4::timeAfter_type >( ),
                                  "Signal duration after "
                                  "(GTimeS.GTimeN)(seconds)" ) );
                        v4_description( FrSE(
                            "triggerStatus",
                            Description::Ascii< v4::triggerStatus_type >( ),
                            "Defined by event search algorithm" ) );
                        v4_description(
                            FrSE( "amplitude",
                                  Description::Ascii< v4::amplitude_type >( ),
                                  "Continuouis output amplitude returned by "
                                  "trigger" ) );
                        v4_description(
                            FrSE( "probability",
                                  Description::Ascii< v4::probability_type >( ),
                                  "Likelihood estimate of event, if available"
                                  " (probability = -1 if cannot be "
                                  "estimated)" ) );
                        v4_description( FrSE(
                            "statistics",
                            Description::Ascii< io_statistics_type >( ),
                            "Statistical description of event, if relevant "
                            "or "
                            "available" ) );
                        v4_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "" ) );
                        v4_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "" ) );
                        v4_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( 4 ) ),
                                  "" ) );
                    }
                    description = &v4_description;
                }
                else if ( frame_stream_version >= 3 )
                {
                    static Description v3_description;

                    if ( v3_description.size( ) == 0 )
                    {
                        v3_description(
                            FrSH( Data::FrameSpecName( 3 ),
                                  Data::FrameSpecId( ),
                                  Data::FrameSpecDescription( 3 ) ) );

                        v3_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Name of trigger." ) );
                        v3_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Descriptor of trigger" ) );
                        v3_description( FrSE(
                            "inputs",
                            Description::Ascii< io_inputs_type >( ),
                            "Input channels and filter parameters to trigger "
                            "process." ) );
                        v3_description(
                            FrSE( "GTimeS",
                                  Description::Ascii< v3::GTimeS_type >( ),
                                  "Event occurrence time, ingeger seconds "
                                  "since beginning of frame." ) );
                        v3_description(
                            FrSE( "GTimeN",
                                  Description::Ascii< v3::GTimeN_type >( ),
                                  "Event occurrence time residual, integer "
                                  "nanoseconds." ) );
                        v3_description(
                            FrSE( "bvalue",
                                  Description::Ascii< v3::bvalue_type >( ),
                                  "Defined bby event search algorithm" ) );
                        v3_description(
                            FrSE( "rvalue",
                                  Description::Ascii< v3::rvalue_type >( ),
                                  "Continuouis output amplitude returned by "
                                  "event" ) );
                        v3_description(
                            FrSE( "probability",
                                  Description::Ascii< v3::probability_type >( ),
                                  "Likelihood estimate of event, if available"
                                  " (probability = -1 if cannot be "
                                  "estimated)" ) );
                        v3_description( FrSE(
                            "statistics",
                            Description::Ascii< io_statistics_type >( ),
                            "Statistical description of event, if relevant "
                            "or "
                            "available" ) );
                        v3_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "" ) );
                        v3_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( 3 ) ),
                                  "" ) );
                    }
                    description = &v3_description;
                }

                return ( description );
            }

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

                cmn_streamsize_type param_size{ 0 };
                if ( frame_stream_version >= 5 )
                {
                    param_size += sizeof( INT_2U );
                    for ( auto i : Params )
                    {
                        param_size += io_name_type::Bytes( i.first ) +
                            ( ( frame_stream_version >= 7 )
                                  ? sizeof( i.second )
                                  : sizeof( v5::paramValue_type ) );
                    }
                }
                return io_name_type::Bytes( name ) //
                    + io_comment_type::Bytes( comment ) //
                    + io_inputs_type::Bytes( inputs ) //
                    + sizeof( INT_4U ) // GTimeS
                    + sizeof( INT_4U ) // GTimeN
                    + ( ( frame_stream_version >= 4 )
                            ? ( sizeof( timeBefore ) + sizeof( timeAfter ) )
                            : 0 ) +
                    sizeof( eventStatus ) + sizeof( amplitude ) +
                    sizeof( probability ) +
                    io_statistics_type::Bytes( statistics ) + param_size +
                    Stream.PtrStructBytes( ) // data
                    + ( ( frame_stream_version >= 4 ) ? Stream.PtrStructBytes( )
                                                      : 0 ) // table
                    + Stream.PtrStructBytes( ) // next
                    ;
            }

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

                FrEvent* obj( new FrEvent( ) );

                io_name_type       io_name;
                io_comment_type    io_comment;
                io_inputs_type     io_inputs;
                io_statistics_type io_statistics;

                Stream >> io_name //
                    >> io_comment //
                    >> io_inputs //
                    >> obj->GTime //
                    ;
                if ( frame_stream_version >= 4 )
                {
                    Stream //
                        >> obj->timeBefore //
                        >> obj->timeAfter //
                        ;
                }
                Stream //
                    >> obj->eventStatus //
                    >> obj->amplitude //
                    >> obj->probability //
                    >> io_statistics //
                    ;
                if ( frame_stream_version >= 5 )
                {
                    Stream >> obj->Params;
                }
                Stream >> obj->data;
                if ( frame_stream_version >= 4 )
                {
                    Stream >> obj->table;
                };

                Stream.Next( obj );

                obj->name = io_name;
                obj->comment = io_comment;
                obj->inputs = io_inputs;
                obj->statistics = io_statistics;

                return ( obj );
            }

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

                Stream << io_name_type( name ) << io_comment_type( comment )
                       << io_inputs_type( inputs ) << GTime;
                if ( frame_stream_version >= 4 )
                {
                    Stream << timeBefore << timeAfter;
                }
                Stream //
                    << eventStatus //
                    << amplitude //
                    << probability //
                    << io_statistics_type( statistics ) //
                    ;
                if ( frame_stream_version >= 5 )
                {
                    Stream << Params;
                }
                Stream << data;
                if ( frame_stream_version >= 4 )
                {
                    Stream << table;
                }
                WriteNext( Stream );
            }

            template <>
            void
            ClassicIO< FrEvent >::
#if WORKING_VIRTUAL_TOCQUERY
                TOCQuery( int InfoClass, ... ) const
#else /*  WORKING_VIRTUAL_TOCQUERY */
                vTOCQuery( int InfoClass, va_list vl ) const
#endif /*  WORKING_VIRTUAL_TOCQUERY */
            {
                using Common::TOCInfo;

#if WORKING_VIRTUAL_TOCQUERY
                va_list vl;
                va_start( vl, InfoClass );
#endif /*  WORKING_VIRTUAL_TOCQUERY */

                while ( InfoClass != TOCInfo::IC_EOQ )
                {
                    int data_type = va_arg( vl, int );
                    switch ( data_type )
                    {
                    case TOCInfo::DT_STRING_2:
                    {
                        STRING* data = va_arg( vl, STRING* );
                        switch ( InfoClass )
                        {
                        case TOCInfo::IC_NAME:
                            *data = GetName( );
                            break;
                        default:
                            goto cleanup;
                            break;
                        }
                    }
                    break;
                    case TOCInfo::DT_INT_4U:
                    {
                        INT_4U* data = va_arg( vl, INT_4U* );
                        switch ( InfoClass )
                        {
                        case TOCInfo::IC_GTIME_S:
                            *data = GetGTime( ).GetSeconds( );
                            break;
                        case TOCInfo::IC_GTIME_N:
                            *data = GetGTime( ).GetNanoseconds( );
                            break;
                        default:
                            goto cleanup;
                            break;
                        }
                    }
                    break;
                    case TOCInfo::DT_REAL_4:
                    {
                        REAL_4* data = va_arg( vl, REAL_4* );
                        switch ( InfoClass )
                        {
                        case TOCInfo::IC_AMPLITUDE:
                            *data = GetAmplitude( );
                            break;
                        default:
                            goto cleanup;
                            break;
                        }
                    }
                    break;
                    case TOCInfo::DT_REAL_8:
                    {
                        REAL_8* data = va_arg( vl, REAL_8* );
                        switch ( InfoClass )
                        {
                        case TOCInfo::IC_AMPLITUDE:
                            *data = GetAmplitude( );
                            break;
                        default:
                            goto cleanup;
                            break;
                        }
                    }
                    break;
                    default:
                        // Stop processing
                        goto cleanup;
                    }
                    InfoClass = va_arg( vl, int );
                }
            cleanup:
#if WORKING_VIRTUAL_TOCQUERY
                va_end( vl )
#endif /*  WORKING_VIRTUAL_TOCQUERY */
                    ;
            }

        } // namespace FrEventImpl
    } // namespace Universal
} // namespace FrameCPP
