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

#if 0
#include "ldastoolsal/mutexlock.hh"
#include "ldastoolsal/gpstime.hh"
#endif /* 0 */
#include "ldastoolsal/MemChecker.hh"
#include "ldastoolsal/Singleton.hh"

#include "framecpp/Universal/FrProcData.hh"

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


using std::ceil;
using std::floor;

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

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

//-----------------------------------------------------------------------
// Local functions and variables
//-----------------------------------------------------------------------

FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( FrProcDataImpl,
                                      FSI_FR_PROC_DATA,
                                      "FrProcData",
                                      "Post-Processed Data Structure" )

//=======================================================================
// Anonymous
//=======================================================================
namespace
{
    typedef FrameCPP::Universal::FrProcData::type_type type_type;

    class IDTypeToStringMapping
    {
        SINGLETON_TS_DECL( IDTypeToStringMapping );

    public:
        const std::string& GetString( type_type Id ) const;

    private:
        std::vector< std::string > m_types;
    };

    class IDSubTypeToStringMappingFrequency
    {
        SINGLETON_TS_DECL( IDSubTypeToStringMappingFrequency );

    public:
        const std::string& GetString( type_type Id ) const;

    private:
        std::vector< std::string > m_types;
    };

} // namespace

namespace FrameCPP
{
    namespace Universal
    {

        namespace FrProcDataImpl
        {
            const std::string&
            Data::IDTypeToString( Data::type_type Type )
            {
                return IDTypeToStringMapping::Instance( ).GetString( Type );
            }

            const std::string&
            Data::IDSubTypeToString( Data::type_type    Type,
                                     Data::subType_type SubType )
            {
                static const std::string empty( "" );
                switch ( Type )
                {
                case FREQUENCY_SERIES:
                    return IDSubTypeToStringMappingFrequency::Instance( )
                        .GetString( Type );
                    break;
                default:
                    return empty;
                    break;
                }
            }

            const Data::name_type&
            Data::GetNameSlow( ) const
            {
                return GetName( );
            }
        } // namespace FrProcDataImpl

        //=======================================================================
        // FrProcData
        //=======================================================================
        FrProcData::FrProcData( const name_type&    Name,
                                const comment_type& Comment,
                                type_type           Type,
                                subType_type        SubType,
                                timeOffset_type     TimeOffset,
                                tRange_type         TRange,
                                fShift_type         FShift,
                                phase_type          Phase,
                                fRange_type         FRange,
                                BW_type             BW )
        {
            Data::name = Name;
            Data::comment = Comment;
            Data::type = Type;
            Data::subType = SubType;
            Data::timeOffset = TimeOffset;
            Data::tRange = TRange;
            Data::fShift = FShift;
            Data::phase = Phase;
            Data::fRange = FRange;
            Data::BW = BW;
        }

        FrProcData::~FrProcData( )
        {
        }

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

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

        FrProcData::subset_ret_type
        FrProcData::Subset( REAL_8 Offset, REAL_8 Dt ) const
        {
            subset_ret_type retval;

            //---------------------------------------------------------------------
            // Process the types that are understood
            //---------------------------------------------------------------------
            switch ( GetType( ) )
            {
            case TIME_SERIES:
                retval = sub_time_series( Offset, Dt );
                break;
            default:
            {
                //-----------------------------------------------------------------
                // This type is not supported so throw an exception
                //-----------------------------------------------------------------
                std::ostringstream msg;
                msg << "Currently cannot take a subsection of type: "
                    << IDTypeToString( GetType( ) );
                const std::string& subtype(
                    IDSubTypeToString( GetType( ), GetSubType( ) ) );
                if ( subtype.length( ) > 0 )
                {
                    msg << " subType: " << subtype;
                }
                throw std::runtime_error( msg.str( ) );
            }
            break;
            }

            return retval;
        }

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

        // Routine to take a subsection when the data type is TimeSeries data
        FrProcData::subset_ret_type
        FrProcData::sub_time_series( REAL_8 Offset, REAL_8 Dt ) const
        {
            //---------------------------------------------------------------------
            // Sanity checks
            //---------------------------------------------------------------------
            // Verify the existance of only a single FrVect component
            //---------------------------------------------------------------------
            if ( RefData( ).size( ) != 1 )
            {
                std::ostringstream msg;

                msg << "METHOD: frameAPI::FrProcData::SubFrProcData: "
                    << "data vectors of length " << RefData( ).size( )
                    << " currently are not supported";
                throw std::logic_error( msg.str( ) );
            }
            INT_4U num_elements = RefData( )[ 0 ]->GetDim( 0 ).GetNx( );
            //---------------------------------------------------------------------
            /// \todo
            ///   Ensure the dimension of the FrVect is 1
            //---------------------------------------------------------------------
            //---------------------------------------------------------------------
            /// \todo
            ///   Ensure the requested range is valid
            //---------------------------------------------------------------------
            REAL_8 sample_rate = 1.0 / RefData( )[ 0 ]->GetDim( 0 ).GetDx( );
            if ( Offset >
                 ( GetTimeOffset( ) + ( num_elements / sample_rate ) ) )
            {
                std::ostringstream msg;
                msg << "METHOD: frameAPI::FrProcData::SubFrProcData: "
                    << "The value for the parameter Offset (" << Offset
                    << ") is out of range ([0-"
                    << ( GetTimeOffset( ) + ( num_elements / sample_rate ) )
                    << "))";
                throw std::range_error( msg.str( ) );
            }
            //---------------------------------------------------------------------
            // Start moving of data
            //---------------------------------------------------------------------
            subset_ret_type retval( new FrProcData );
            // Copy in the header information
            retval->copy_core( *this );

            /// \todo
            ///   Get the slice of FrVect
            REAL_8 startSampleReal = ( Offset * sample_rate ) -
                ( retval->GetTimeOffset( ) * sample_rate );

            INT_4U endSample =
                INT_4U( ceil( startSampleReal + ( Dt * sample_rate ) ) );
            INT_4U startSample = ( startSampleReal > REAL_8( 0 ) )
                ? INT_4U( floor( startSampleReal ) )
                : INT_4U( 0 );

            {
                boost::shared_ptr< FrVect > subvect(
                    RefData( )[ 0 ]
                        ->SubFrVect( startSample, endSample )
                        .release( ) );
                retval->RefData( ).append( subvect );
            }

            //---------------------------------------------------------------------
            // Shift the offset
            //---------------------------------------------------------------------
            if ( startSampleReal > 0 )
            {
                // There is no gap
                retval->SetTimeOffset( REAL_8( 0 ) );
            }
            else
            {
                retval->timeOffset += Offset;
            }
            //---------------------------------------------------------------------
            // :TODO: Recalculate tRange
            //---------------------------------------------------------------------
            retval->SetTRange( retval->RefData( )[ 0 ]->GetDim( 0 ).GetNx( ) *
                               retval->RefData( )[ 0 ]->GetDim( 0 ).GetDx( ) );
            //---------------------------------------------------------------------
            // Return the new FrProcData
            //---------------------------------------------------------------------
            return retval;
        }

    } // namespace Universal
} // namespace FrameCPP

//=======================================================================
// Anonymous
//=======================================================================
namespace
{
    //---------------------------------------------------------------------
    // Initialization of singleton
    //---------------------------------------------------------------------
    SINGLETON_TS_INST( IDTypeToStringMapping );
    //---------------------------------------------------------------------
    // Default constructor
    //---------------------------------------------------------------------
    IDTypeToStringMapping::IDTypeToStringMapping( )
    {
        LDASTools::AL::MemChecker::Append(
            singleton_suicide,
            "<anonymous>::IDTypeToStringMapping::singleton_suicide",
            5 );
        m_types.push_back( "Unknown/user defined" );
        m_types.push_back( "Time Series" );
        m_types.push_back( "Frequency Series" );
        m_types.push_back( "Other 1D Series data" );
        m_types.push_back( "Time-Frequency" );
        m_types.push_back( "Wavelets" );
        m_types.push_back( "Multi-dimensional" );
    }

    const std::string&
    IDTypeToStringMapping::GetString( type_type Type ) const
    {
        if ( Type >= m_types.size( ) )
        {
            std::ostringstream msg;
            msg << "Cannot map FrProcData type value of " << Type
                << " to string value because it is out of range";
            throw std::range_error( msg.str( ) );
        }
        return m_types[ Type ];
    }

    //---------------------------------------------------------------------
    // Initialization of singleton
    //---------------------------------------------------------------------
    SINGLETON_TS_INST( IDSubTypeToStringMappingFrequency );
    //---------------------------------------------------------------------
    // Default constructor
    //---------------------------------------------------------------------
    IDSubTypeToStringMappingFrequency::IDSubTypeToStringMappingFrequency( )
    {
        LDASTools::AL::MemChecker::Append(
            singleton_suicide,
            "<anonymous>::IDTypeToStringMappingFrequency::singleton_suicide",
            5 );
        m_types.push_back( "Unknown/user defined" );
        m_types.push_back( "Amplitude Spectral Density" );
        m_types.push_back( "Power Spectral Density" );
        m_types.push_back( "Cross Spectral Density" );
        m_types.push_back( "Coherence" );
        m_types.push_back( "Transfer Function" );
    }

    const std::string&
    IDSubTypeToStringMappingFrequency::GetString( INT_2U Type ) const
    {
        if ( Type >= m_types.size( ) )
        {
            std::ostringstream msg;
            msg << "Cannot map FrProcData type value of " << Type
                << " to string value because it is out of range";
            throw std::range_error( msg.str( ) );
        }
        return m_types[ Type ];
    }
} // namespace

namespace FrameCPP
{
    namespace Universal
    {
    } // namespace Universal
} // namespace FrameCPP

//=======================================================================
//               C L A S S I C   I O
//=======================================================================
namespace FrameCPP
{
    namespace Universal
    {
        namespace FrProcDataImpl
        {
#if 0
            ClassicIO( Previous::FrProcData& Source, stream_base_type* Stream )
                : ObjectWithChecksum< Common::FrameSpec::Object >(
                      FrameSpecId( ), StructDescription( ) )
            {
                name = Source.GetName( );
                comment = Source.GetComment( );
                type = Source.GetType( );
                subType = Source.GetSubType( );
                timeOffset = Source.GetTimeOffset( );
                tRange = Source.GetTRange( );
                fShift = Source.GetFShift( );
                phase = Source.GetPhase( );
                fRange = Source.GetFRange( );
                BW = Source.GetBW( );
                auxParam.resize( Source.GetAuxParam( ).size( ) );
                auto nextParam = auxParam.begin( );
                for ( auto param : Source.GetAuxParam( ) )
                {
                    // auxParam = Source.GetAuxParam( );
                    nextParam->auxParamName = param.auxParamName;
                    nextParam->auxParam = param.auxParam;
                    ++nextParam;
                }

                auto istream = FrameCPP::Common::IsIStream( Stream );

                if ( istream )
                {
                    istream->ReplaceRef(
                        RefData( ), Source.RefData( ), Data::MAX_REF );
                    istream->ReplaceRef(
                        RefAux( ), Source.RefAux( ), Data::MAX_REF );
                    istream->ReplaceRef(
                        RefTable( ), Source.RefTable( ), Data::MAX_REF );
                    istream->ReplaceRef(
                        RefHistory( ), Source.RefHistory( ), Data::MAX_REF );
                }
            }
#endif /* 0 */

            //-----------------------------------------------------------------
            /// @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 Description*
            ClassicIO< FrProcData >::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( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        v8_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Data or channel name" ) );
                        v8_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Comment" ) );
                        v8_description(
                            FrSE( "type",
                                  Description::Ascii< type_type >( ),
                                  "Type of data object" ) );
                        v8_description( FrSE(
                            "subType",
                            Description::Ascii< subType_type >( ),
                            "Subtype for f-Series(TDBD for other types)" ) );
                        v8_description(
                            FrSE( "timeOffset",
                                  Description::Ascii< timeOffset_type >( ),
                                  "Offset of 1st sample relative to the frame "
                                  "start time" ) );
                        v8_description(
                            FrSE( "tRange",
                                  Description::Ascii< tRange_type >( ),
                                  "Duration of sampled data (tStop-tStart)" ) );
                        v8_description( FrSE(
                            "fShift",
                            Description::Ascii< fShift_type >( ),
                            "fShift is the requency iin the original data that "
                            "corresponds to 0Hz in the heerodyned series." ) );
                        v8_description(
                            FrSE( "phase",
                                  Description::Ascii< phase_type >( ),
                                  "Phase of heterodyning signal at start of "
                                  "dataset (radians, 0 if unknown)" ) );
                        v8_description( FrSE(
                            "fRange",
                            Description::Ascii< fRange_type >( ),
                            "Frequency range (=fMax-fMin, 0 if unknown)" ) );
                        v8_description( FrSE( "BW",
                                              Description::Ascii< BW_type >( ),
                                              "Resolution bandwidth" ) );
                        v8_description(
                            FrSE( "nAuxParam",
                                  "INT_2U",
                                  "Number of auxiliary parameters" ) );
                        v8_description(
                            FrSE( "auxParam",
                                  Description::AsciiArray< v8::auxParamInfo_type::auxParamValue_type >( "nAuxParam" ),
                                  "Array of auxiliary parameters (size of "
                                  "nAuxParam)." ) );
                        v8_description(
                            FrSE( "auxParamNames",
                                  Description::AsciiArray< v8::auxParamInfo_type::auxParamName_type >( "nAuxParam" ),
                                  "Array of auxiliary parameter names (size "
                                  "of nAuxParam)." ) );

                        v8_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Data vector. The data vector for single "
                                  "dimensional types (t-Series fand f-Series) "
                                  "must have a single dimension" ) );
                        v8_description(
                            FrSE( "aux",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Auxiliary data; use is generic." ) );
                        v8_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "Parameter table" ) );
                        v8_description( FrSE( "history",
                                              PTR_STRUCT::Desc( "FrHistory" ),
                                              "Channel history" ) );

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

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

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

                        v5_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Data or channel name" ) );
                        v5_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Comment" ) );
                        v5_description(
                            FrSE( "type",
                                  Description::Ascii< type_type >( ),
                                  "Type of data object" ) );
                        v5_description( FrSE(
                            "subType",
                            Description::Ascii< subType_type >( ),
                            "Subtype for f-Series(TDBD for other types)" ) );
                        v5_description(
                            FrSE( "timeOffset",
                                  Description::Ascii< timeOffset_type >( ),
                                  "Offset of 1st sample relative to the frame "
                                  "start time" ) );
                        v5_description(
                            FrSE( "tRange",
                                  Description::Ascii< tRange_type >( ),
                                  "Duration of sampled data (tStop-tStart)" ) );
                        v5_description( FrSE(
                            "fShift",
                            Description::Ascii< fShift_type >( ),
                            "fShift is the requency iin the original data that "
                            "corresponds to 0Hz in the heerodyned series." ) );
                        v5_description(
                            FrSE( "phase",
                                  Description::Ascii< phase_type >( ),
                                  "Phase of heterodyning signal at start of "
                                  "dataset (radians, 0 if unknown)" ) );
                        v5_description( FrSE(
                            "fRange",
                            Description::Ascii< fRange_type >( ),
                            "Frequency range (=fMax-fMin, 0 if unknown)" ) );
                        v5_description( FrSE( "BW",
                                              Description::Ascii< BW_type >( ),
                                              "Resolution bandwidth" ) );
                        v5_description(
                            FrSE( "nAuxParam",
                                  "INT_2U",
                                  "Number of auxiliary parameters" ) );
                        v5_description(
                            FrSE( "auxParam",
                                  Description::AsciiArray< v5::auxParamInfo_type::auxParamValue_type >( "nAuxParam" ),
                                  "Array of auxiliary parameters (size of "
                                  "nAuxParam)." ) );
                        v5_description(
                            FrSE( "auxParamNames",
                                  Description::AsciiArray< v5::auxParamInfo_type::auxParamName_type >( "nAuxParam" ),
                                  "Array of auxiliary parameter names (size "
                                  "of nAuxParam)." ) );

                        v5_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Data vector. The data vector for single "
                                  "dimensional types (t-Series fand f-Series) "
                                  "must have a single dimension" ) );
                        v5_description(
                            FrSE( "aux",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Auxiliary data; use is generic." ) );
                        v5_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "Parameter table" ) );
                        v5_description( FrSE( "history",
                                              PTR_STRUCT::Desc( "FrHistory" ),
                                              "Channel history" ) );

                        v5_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( ) ),
                                  "Identifier for next FrProcData 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::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        v4_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Data or channel name" ) );
                        v4_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Comment" ) );
                        v4_description(
                            FrSE( "sampleRate",
                                  Description::Ascii< v4::sampleRate_type >( ),
                                  "Data acquisition rate, samples/s" ) );
                        v4_description( FrSE(
                            "timeOffsetS",
                            Description::Ascii< v4::timeOffsetS_type >( ),
                            "For triggered data lasting less than one frame,"
                            " integer seconds start time relative to frame "
                            "start" ) );
                        v4_description( FrSE(
                            "timeOffsetN",
                            Description::Ascii< v4::timeOffsetN_type >( ),
                            "For triggered data lasting less than one frame,"
                            " integer residual nanoseconds start time relative"
                            " to frame start" ) );
                        v4_description( FrSE(
                            "fShift",
                            Description::Ascii< fShift_type >( ),
                            "fShift is the requency iin the original data that "
                            "corresponds to 0Hz in the heerodyned series." ) );

                        v4_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Data vector. The data vector for single "
                                  "dimensional types (t-Series fand f-Series) "
                                  "must have a single dimension" ) );
                        v4_description(
                            FrSE( "aux",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Auxiliary data; use is generic." ) );
                        v4_description(
                            FrSE( "table",
                                  PTR_STRUCT::Desc( FrTable::StructName( ) ),
                                  "Identifier for table structure." ) );

                        v4_description(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::StructName( ) ),
                                  "Identifier for next FrProcData 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::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        v3_description(
                            FrSE( "name",
                                  Description::Ascii< io_name_type >( ),
                                  "Data or channel name" ) );
                        v3_description(
                            FrSE( "comment",
                                  Description::Ascii< io_comment_type >( ),
                                  "Comment" ) );
                        v3_description(
                            FrSE( "sampleRate",
                                  Description::Ascii< v3::sampleRate_type >( ),
                                  "Data acquisition rate, samples/s" ) );
                        v3_description( FrSE(
                            "timeOffsetS",
                            Description::Ascii< v3::timeOffsetS_type >( ),
                            "For triggered data lasting less than one frame,"
                            " integer seconds start time relative to frame "
                            "start" ) );
                        v3_description( FrSE(
                            "timeOffsetN",
                            Description::Ascii< v3::timeOffsetN_type >( ),
                            "For triggered data lasting less than one frame,"
                            " integer residual nanoseconds start time relative"
                            " to frame start" ) );
                        v3_description( FrSE(
                            "fShift",
                            Description::Ascii< fShift_type >( ),
                            "fShift is the requency iin the original data that "
                            "corresponds to 0Hz in the heerodyned series." ) );

                        v3_description(
                            FrSE( "data",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Data vector. The data vector for single "
                                  "dimensional types (t-Series fand f-Series) "
                                  "must have a single dimension" ) );
                        v3_description(
                            FrSE( "aux",
                                  PTR_STRUCT::Desc( FrVect::StructName( ) ),
                                  "Auxiliary data; use is generic." ) );

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

                return ( description );
            }

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

                FrProcData* obj( new FrProcData( ) );

                io_name_type    io_name;
                io_comment_type io_comment;

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

                    Stream >> sampleRate;
                }
                if ( frame_stream_version >= 5 )
                {
                    Stream >> obj->timeOffset;
                }
                else if ( frame_stream_version >= 3 )
                {
                    v3::timeOffsetS_type timeOffsetS;
                    v3::timeOffsetN_type timeOffsetN;

                    Stream >> timeOffsetS >> timeOffsetN;
                    obj->timeOffset =
                        ConvertToRealSeconds( timeOffsetS, timeOffsetN );
                }
                if ( frame_stream_version >= 5 )
                {
                    Stream >> obj->tRange;
                }
                Stream >> obj->fShift;
                if ( frame_stream_version >= 5 )
                {
                    Stream >> obj->phase >> obj->fRange >> obj->BW;

                    INT_2U size;
                    Stream >> size;
                    obj->auxParam.resize( size );
                    for ( auto& element : obj->auxParam )
                    {
                        Stream >> element.auxParam;
                    }
                    for ( auto& element : obj->auxParam )
                    {
                        STRING value;

                        Stream >> value;
                        element.auxParamName = value;
                    }
                }
                Stream >> obj->data >> obj->aux;
                if ( frame_stream_version >= 4 )
                {
                    Stream >> obj->table;
                }
                if ( frame_stream_version >= 5 )
                {
                    Stream >> obj->history;
                }

                Stream.Next( obj );

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

                return ( obj );
            }

            template <>
            void
            ClassicIO< FrProcData >::pWrite( ostream_type& Stream ) const
            {
                auto frame_stream_version = StreamVersion( Stream );
#ifdef FRAMECPP_DEBUG
                try
                {
#endif /* FRAMECPP_DEBUG */

                    Stream << io_name_type( name )
                           << io_comment_type( comment );
                    if ( ( frame_stream_version < 5 ) &&
                         ( frame_stream_version >= 3 ) )
                    {
                        Stream << v3::sampleRate_type( 0 );
                    }
                    if ( frame_stream_version >= 5 )
                    {
                        auto out_type( type );
                        auto out_subType( subType );
                        if ( frame_stream_version <= 8 )
                        {
                            switch ( type )
                            {
                            case MIME:
                                out_type = UNKNOWN_TYPE;
                                out_subType = 0;
                                break;
                            default:
                                break;
                            }
                        }
                        Stream << out_type << out_subType;
                    }
                    if ( frame_stream_version >= 5 )
                    {
                        Stream << timeOffset;
                    }
                    else if ( frame_stream_version >= 3 )
                    {
                        GPSTime zero( 0, 0 );
                        zero += timeOffset;

                        Stream
                            << v3::timeOffsetS_type( zero.GetSeconds( ) ) //
                            << v3::timeOffsetN_type( zero.GetNanoseconds( ) ) //
                            ;
                    }
                    if ( frame_stream_version >= 5 )
                    {
                        Stream << tRange;
                    }
                    Stream << fShift;
                    if ( frame_stream_version >= 5 )
                    {
                        Stream << phase << fRange << BW;
                        // auxParam
                        Stream << INT_2U( auxParam.size( ) );
                        for ( auto element : auxParam )
                        {
                            Stream << element.auxParam;
                        }
                        for ( auto element : auxParam )
                        {
                            Stream << STRING( element.auxParamName );
                        }
                    }
                    Stream << data << aux;
                    if ( frame_stream_version >= 4 )
                    {
                        Stream << table;
                    }
                    if ( frame_stream_version >= 5 )
                    {
                        Stream << history;
                    }
                    WriteNext( Stream );
#ifdef FRAMECPP_DEBUG
                }
                catch ( const std::exception& Exception )
                {
                    std::cerr << "DEBUG: Error Writing " << StructName( )
                              << ": " << GetName( )
                              << " exception: " << Exception.what( )
                              << std::endl;
                    throw; // rethrow
                }
#endif /* FRAMECPP_DEBUG */
            }

        } // namespace FrProcDataImpl
    } // namespace Universal
} // namespace FrameCPP
