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

#if HAVE_CONFIG_H
#include <framecpp_config.h>
#endif /* HAVE_CONFIG_H */

#include <algorithm>
#include <memory>
#include <sstream>
#include <type_traits>

#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>

#include <boost/scoped_array.hpp>
#include <boost/shared_ptr.hpp>

#include "framecpp/FrAdcData.hh"
#include "framecpp/FrMsg.hh"
#include "framecpp/FrVect.hh"
#include "framecpp/GPSTime.hh"

#include "ramp.hh"

#include "FrStruct.hh"
#include "FrStruct3.tcc"
#include "FrStruct4.tcc"
#include "FrStruct6.tcc"
#include "FrStruct7.tcc"
#include "FrStruct8.tcc"
#include "FrStruct9.tcc"

namespace FrameCPP
{
    namespace Universal
    {
        inline void
        dosomething( )
        {
        }
    } // namespace Universal

    namespace Version_3
    {
        inline void
        dosomething( )
        {
        }
    } // namespace Version_3
} // namespace FrameCPP

bool
do_init( )
{
    FrameCPP::Initialize( );
    return ( 1 );
}

static const bool HAS_BEEN_INITIALIZED = do_init( );

template < int T_FRAME_SPEC_VERSION >
using frame_spec_constant = std::integral_constant< int, T_FRAME_SPEC_VERSION >;

template < int T_VALUE >
using base_version_constant = std::integral_constant< int, T_VALUE >;

/// \brief Markers for transitions
enum class CONVERT
{
    ROOT, ///< Representation of when the class was introduced.
    SAME, ///< No difference from the previous version of the frame
          /// specification
    V4, ///< Differs from the previous version of the frame specication
    V5, ///< Differs from the previous version of the frame specication
    V6, ///< Differs from the previous version of the frame specication
    V7, ///< Differs from the previous version of the frame specication
    V8, ///< Differs from the previous version of the frame specication
    V9 ///< Differs from the previous version of the frame specication
};

template < CONVERT T_CONVERT >
using convert_constant = std::integral_constant< CONVERT, T_CONVERT >;

template < int T_FRAME_SPEC_VERSION >
constexpr CONVERT
          frame_spec_to_convert_mapper( )
{
    return ( std::conditional<
             T_FRAME_SPEC_VERSION == 9,
             convert_constant< CONVERT::V9 >,
             typename std::conditional<
                 T_FRAME_SPEC_VERSION == 8,
                 convert_constant< CONVERT::V8 >,
                 typename std::conditional<
                     T_FRAME_SPEC_VERSION == 7,
                     convert_constant< CONVERT::V7 >,
                     typename std::conditional<
                         T_FRAME_SPEC_VERSION == 6,
                         convert_constant< CONVERT::V6 >,
                         typename std::conditional<
                             T_FRAME_SPEC_VERSION == 5,
                             convert_constant< CONVERT::V5 >,
                             typename std::enable_if<
                                 ( T_FRAME_SPEC_VERSION == 4 ),
                                 convert_constant< CONVERT::V4 >::type >
                             // conditional - 5
                             >::type
                         // conditional - 6
                         >::type
                     // conditional - 7
                     >::type
                 // conditional - 8
                 >::type
             // conditional - 9
             >::type
             // calculate return value
             ::type::value );
}

template < CONVERT T_CONVERT >
constexpr int
convert_to_frame_spec_mapper( )
{
    return (
        std::conditional< T_CONVERT == CONVERT::V9,
                          frame_spec_constant< 9 >,
                          typename std::conditional<
                              T_CONVERT == CONVERT::V8,
                              frame_spec_constant< 8 >,
                              typename std::conditional<
                                  T_CONVERT == CONVERT::V7,
                                  frame_spec_constant< 7 >,
                                  typename std::conditional<
                                      T_CONVERT == CONVERT::V6,
                                      frame_spec_constant< 6 >,
                                      typename std::conditional<
                                          T_CONVERT == CONVERT::V5,
                                          frame_spec_constant< 5 >,
                                          typename std::enable_if<
                                              ( T_CONVERT == CONVERT::V4 ),
                                              frame_spec_constant< 4 >::type >
                                          // conditional - 5
                                          >::type
                                      // conditional - 6
                                      >::type
                                  // conditional - 7
                                  >::type
                              // conditional - 8
                              >::type
                          // conditional - 9
                          >::type
        // calculate return value
        ::type::value );
}

/// \brief Markers for where object is defined
enum class VERSION
{
    V3, ///< Version 3 of the frame specification
    V4, ///< Version 4 of the frame specification
    V5, ///< Version 5 of the frame specification
    V6, ///< Version 6 of the frame specification
    V7, ///< Version 7 of the frame specification
    V8, ///< Version 8 of the frame specification
    V9, ///< Version 9 of the frame specification
};

template < VERSION T_VERSION >
using version_constant = std::integral_constant< VERSION, T_VERSION >;

template < int T_FRAME_SPEC_VERSION >
constexpr VERSION
          frame_spec_to_version_mapper( )
{
    return ( std::conditional<
             T_FRAME_SPEC_VERSION == 9,
             version_constant< VERSION::V9 >,
             typename std::conditional<
                 T_FRAME_SPEC_VERSION == 8,
                 version_constant< VERSION::V8 >,
                 typename std::conditional<
                     T_FRAME_SPEC_VERSION == 7,
                     version_constant< VERSION::V7 >,
                     typename std::conditional<
                         T_FRAME_SPEC_VERSION == 6,
                         version_constant< VERSION::V6 >,
                         typename std::conditional<
                             T_FRAME_SPEC_VERSION == 5,
                             version_constant< VERSION::V5 >,
                             typename std::conditional<
                                 T_FRAME_SPEC_VERSION == 4,
                                 version_constant< VERSION::V4 >,
                                 typename std::enable_if<
                                     ( T_FRAME_SPEC_VERSION == 3 ),
                                     version_constant< VERSION::V3 >::type >
                                 // conditional - 4
                                 >::type
                             // conditional - 5
                             >::type
                         // conditional - 6
                         >::type
                     // conditional - 7
                     >::type
                 // conditional - 8
                 >::type
             // conditional - 9
             >::type
             // calculate return value
             ::type::value );
}

template < VERSION T_VERSION >
constexpr int
version_to_frame_spec_to_version_mapper( )
{
    return ( std::conditional<
             T_VERSION == VERSION::V9,
             frame_spec_constant< 9 >,
             typename std::conditional<
                 T_VERSION == VERSION::V8,
                 frame_spec_constant< 8 >,
                 typename std::conditional<
                     T_VERSION == VERSION::V7,
                     frame_spec_constant< 7 >,
                     typename std::conditional<
                         T_VERSION == VERSION::V6,
                         frame_spec_constant< 6 >,
                         typename std::conditional<
                             T_VERSION == VERSION::V5,
                             frame_spec_constant< 5 >,
                             typename std::conditional<
                                 T_VERSION == VERSION::V4,
                                 frame_spec_constant< 4 >,
                                 typename std::enable_if<
                                     ( T_VERSION == VERSION::V3 ),
                                     frame_spec_constant< 3 >::type >
                                 // conditional - 4
                                 >::type
                             // conditional - 5
                             >::type
                         // conditional - 6
                         >::type
                     // conditional - 7
                     >::type
                 // conditional - 8
                 >::type
             // conditional - 9
             >::type
             // calculate return value
             ::type::value );
}

#define DECLARE_STRUCT( STRUCT )                                               \
    template < int T_FRAME_SPEC_VERSION >                                      \
    struct STRUCT

#define INSTANTIATE_STRUCT( VER, VER_NAMESPACE, FR_OBJECT, STRUCT )            \
    template <>                                                                \
    struct STRUCT< VER >                                                       \
    {                                                                          \
        using type = VER_NAMESPACE::FR_OBJECT;                                 \
    }

#define INSTANTIATE_STRUCT_ALL( FR_OBJECT, STRUCT )                            \
    DECLARE_STRUCT( STRUCT );                                                  \
    INSTANTIATE_STRUCT( 3, FrameCPP::Version_3, FR_OBJECT, STRUCT );           \
    INSTANTIATE_STRUCT( 4, FrameCPP::Version_4, FR_OBJECT, STRUCT );           \
    INSTANTIATE_STRUCT( 6, FrameCPP::Version_6, FR_OBJECT, STRUCT );           \
    INSTANTIATE_STRUCT( 7, FrameCPP::Version_7, FR_OBJECT, STRUCT );           \
    INSTANTIATE_STRUCT( 8, FrameCPP::Version_8, FR_OBJECT, STRUCT );           \
    INSTANTIATE_STRUCT( 9, FrameCPP::Version_9, FR_OBJECT, STRUCT );

inline constexpr INT_4U
                 previous_version( const INT_4U Current )
{
    return ( ( Current == 6 ) ? ( Current - 2 ) : ( Current - 1 ) );
}

template < typename STRING_TYPE, typename A_TYPE >
bool
string_type_compare( const A_TYPE& a )
{
    return ( ( typeid( STRING_TYPE ) == typeid( a ) ) ||
             ( typeid( std::string ) == typeid( a ) ) );
}

namespace testing
{
    template < int T_FRAME_SPEC_VERSION, template < int > class T_FR_OBJECT >
    struct fr_object_impl
    {
        typedef typename T_FR_OBJECT< T_FRAME_SPEC_VERSION >::type type;
    };

    template < int T_FRAME_SPEC_VERSION, template < int > class T_FR_OBJECT >
    struct fr_object_previous_impl
    {
        typedef typename T_FR_OBJECT< previous_version(
            T_FRAME_SPEC_VERSION ) >::type type;
    };

    template < int T_FRAME_SPEC_VERSION >
    using gpstime_type = FrameCPP::GPSTime;

    template < int T_FRAME_SPEC_VERSION >
    using string_type = FrameCPP::Universal::STRING;

    template < int T_FRAME_SPEC_VERSION >
    using object_type =
        FrameCPP::Common::FrameSpec::ObjectInterface::object_type;

#if 0
    // NOTE: convert_check_root disabled - uses deprecated DemoteObject/PromoteObject
    // Universal objects are version-agnostic, all version adaptation happens at I/O layer
    template < int T_FRAME_SPEC_VERSION, typename T_CURRENT_TYPE >
    bool
    convert_check_root( T_CURRENT_TYPE* ( *mk_current )( ) )
    {
        bool retval{ false };

        // -----
        // Down convert
        // -----
        try
        {
            object_type< T_FRAME_SPEC_VERSION > source_msg{ (
                *mk_current )( ) };
            auto derived_obj = source_msg->DemoteObject(
                previous_version( T_FRAME_SPEC_VERSION ), source_msg, nullptr );
        }
        catch ( const FrameCPP::Unimplemented& Error )
        {
            retval = true;
        }
        catch ( ... )
        {
        }

        // -----
        // Up convert
        //   Nothing to test as this is the base version
        // -----

        return ( retval );
    }
#endif

#if 0
    // NOTE: convert_check_same disabled - uses deprecated DemoteObject/PromoteObject
    // Universal objects are version-agnostic, all version adaptation happens at I/O layer
    template < int T_FRAME_SPEC_VERSION,
               typename T_CURRENT_TYPE,
               typename T_PREVIOUS_TYPE >
    bool
    convert_check_same( T_CURRENT_TYPE* ( *mk_current )( ),
                        T_PREVIOUS_TYPE* ( *mk_previous )( ) )
    {
        bool retval{ false };

        constexpr INT_4U current{ T_FRAME_SPEC_VERSION };
        constexpr auto   previous = previous_version( current );

        // -----
        // Down convert
        // -----
        try
        {
            // With no conversion, the object returned should be at the
            // same address
            object_type< T_FRAME_SPEC_VERSION > source_msg{ (
                *mk_current )( ) };
            auto                                derived_obj =
                source_msg->DemoteObject( previous, source_msg, nullptr );
            retval = ( derived_obj == source_msg );
        }
        catch ( ... )
        {
            retval = false;
        }

        // -----
        // Up convert
        // -----
        try
        {
            // With no conversion, the object returned should be at the
            // same address
            object_type< T_FRAME_SPEC_VERSION > source_msg{ (
                *mk_previous )( ) };
            auto derived_obj = source_msg->PromoteObject(
                current, previous, source_msg, nullptr );
            retval = ( retval && ( derived_obj == source_msg ) );
        }
        catch ( ... )
        {
            retval = false;
        }
        return ( retval );
    }
#endif

    template < typename T_EXPECTED, typename T_ACTUAL >
    bool
    check_data_type( const T_ACTUAL& actual )
    {
        return ( typeid( T_EXPECTED ) == typeid( T_ACTUAL ) );
    }

    template < int T_FRAME_SPEC_VERSION, typename T_ACTUAL >
    bool
    check_data_type_string( const T_ACTUAL& actual )
    {
        return ( ( typeid( string_type< T_FRAME_SPEC_VERSION > ) ==
                   typeid( actual ) ) ||
                 ( typeid( std::string ) == typeid( actual ) ) );
    }

    template < int T_FRAME_SPEC_VERSION, int T_NEAREST_BASE >
    constexpr bool
    is_change_version( )
    {
        return ( T_FRAME_SPEC_VERSION == T_NEAREST_BASE );
    }

    template < int  T_FRAME_SPEC_VERSION,
               bool T_IS_ROOT,
               bool T_IS_CHANGE_VERSION >
    constexpr CONVERT conversion( );

    template <>
    constexpr CONVERT
              conversion< 3, true, true >( )
    {
        return ( CONVERT::ROOT );
    }

    template <>
    constexpr CONVERT
              conversion< 3, true, false >( )
    {
        return ( CONVERT::ROOT );
    }

    template < int  T_FRAME_SPEC_VERSION,
               bool T_IS_ROOT,
               bool T_IS_CHANGE_VERSION >
    constexpr CONVERT
              conversion( )
    {
        return ( std::conditional<
                 T_IS_ROOT,
                 convert_constant< CONVERT::ROOT >::type,
                 typename std::conditional<
                     T_IS_CHANGE_VERSION,
                     typename convert_constant< frame_spec_to_convert_mapper<
                         T_FRAME_SPEC_VERSION >( ) >::type,
                     convert_constant< CONVERT::SAME >::type
                     // conditional - ! is_change_version
                     >::type
                 // conditional - is_root
                 >::type
                 // return value
                 ::value );
    }

} // namespace testing

// #include "fr_detector.hh"
// #include "fr_end_of_frame.hh"
// #include "fr_vect.hh"

using namespace FrameCPP;
using FrameCPP::Common::FrameSpec;

typedef FrameCPP::Common::FrameSpec::Info::frame_object_types class_id_type;

static const INT_2U SAMPLES = 256;

static void verify_downconvert( std::ostringstream& Leader,
                                const int           Version,
                                frame_object_type   FrameObj,
                                const std::string&  FrameObjName );

static void verify_upconvert( std::ostringstream& Leader,
                              const int           Version,
                              frame_object_type   FrameObj,
                              const std::string&  FrameObjName );

static inline void
verify_convert( std::ostringstream& Leader,
                const int           Version,
                frame_object_type   FrameObj,
                const std::string&  FrameObjName )
{
    std::ostringstream leader;
    leader.str( "" );
    leader << Leader.str( );
    verify_downconvert( leader, Version, FrameObj, FrameObjName );
    leader.str( "" );
    leader << Leader.str( );
    verify_upconvert( leader, Version, FrameObj, FrameObjName );
}

namespace
{
    template < class T >
    class Riser
    {
    public:
        Riser( ) : m_counter( 0 )
        {
        }

        T
        operator( )( )
        {
            return m_counter++;
        }

    private:
        T m_counter;
    };

    template < typename Type, typename Generator >
    void
    data_generate( FrameCPP::FrVect::data_type& Data,
                   INT_4U                       Size,
                   Generator                    Gen )
    {
        Data.reset(
            new FrameCPP::FrVect::data_type::element_type[ Size *
                                                           sizeof( Type ) ] );
        std::generate(
            reinterpret_cast< Type* >( &( Data[ 0 ] ) ),
            reinterpret_cast< Type* >( &( Data[ Size * sizeof( Type ) ] ) ),
            Gen );
    }
} // namespace

void
fr_adc_data( )
{
    //---------------------------------------------------------------------
    // Create a Single Dimensional Vector
    //---------------------------------------------------------------------
    FrVect::data_type      data;
    FrVect::dimension_type dim( SAMPLES );
    Riser< INT_2U >        riser;

    data_generate< INT_2U >( data, SAMPLES, riser );

    boost::shared_ptr< FrVect > v(
        new FrVect( "fr_vect",
                    FrVect::RAW, /* Compress */
                    FrVect::FR_VECT_2U, /* Type */
                    1, /* nDim */
                    &dim, /* dims */
                    SAMPLES, /* NData */
                    SAMPLES * sizeof( INT_2U ), /* NBytes */
                    data, /* Data */
                    "unitY" /* unitY */
                    ) );

    //---------------------------------------------------------------------
    // Create the adc
    //---------------------------------------------------------------------
    std::unique_ptr< FrAdcData > a(
        new FrAdcData( "adc_data",
                       INT_4U( 0 ),
                       INT_4U( 0 ),
                       INT_4U( 0 ),
                       REAL_8( SAMPLES ),
                       FrameCPP::FrAdcData::DEFAULT_BIAS,
                       FrameCPP::FrAdcData::DEFAULT_SLOPE,
                       FrameCPP::FrAdcData::DEFAULT_UNITS( ),
                       FrameCPP::FrAdcData::DEFAULT_FSHIFT,
                       FrameCPP::FrAdcData::DEFAULT_TIME_OFFSET,
                       FrameCPP::FrAdcData::DEFAULT_DATA_VALID,
                       FrameCPP::FrAdcData::DEFAULT_PHASE ) );
    a->RefData( ).append( v );

    //---------------------------------------------------------------------
    // Check the creation of sub FrAdcData
    //---------------------------------------------------------------------
    FrAdcData::subset_ret_type sub_adc( a->Subset( 0, 1 ) );
    BOOST_TEST_MESSAGE( "Creation of subadc of equal length" );
    BOOST_CHECK( ( *a == *sub_adc ) );
}

void
fr_vect( )
{
#if WORKING
    testing::fr_vect::do_standard_tests< 3 >( );
    testing::fr_vect::do_standard_tests< 4 >( );
    testing::fr_vect::do_standard_tests< 6 >( );
    testing::fr_vect::do_standard_tests< 7 >( );
    testing::fr_vect::do_standard_tests< 8 >( );
    testing::fr_vect::do_standard_tests< 9 >( );

    //---------------------------------------------------------------------
    // Create a Single Dimensional Vector
    //---------------------------------------------------------------------
    FrVect::data_type data;

    boost::scoped_array< INT_2U > data_2u( new INT_2U[ SAMPLES ] );
    FrVect::dimension_type        dim( SAMPLES );
    Riser< INT_2U >               riser;

    data_generate< INT_2U >( data, SAMPLES, riser );

    boost::shared_ptr< FrVect > v(
        new FrVect( "fr_vect",
                    FrVect::RAW, /* Compress */
                    FrVect::FR_VECT_2U, /* Type */
                    1, /* nDim */
                    &dim, /* dims */
                    SAMPLES, /* NData */
                    SAMPLES * sizeof( INT_2U ), /* NBytes */
                    data, /* Data */
                    "unitY" /* unitY */
                    ) );
    //---------------------------------------------------------------------
    // Check the creation of sub vectors
    //---------------------------------------------------------------------
    FrVect::subfrvect_type sub_vect( v->SubFrVect( 0, SAMPLES ) );
    BOOST_TEST_MESSAGE( "Creation of subvector of equal length" );
#if EXTRA_VERBOSE || 0
    //-------------------------------------------------------------------
    // Extra debugging information when trying to trouble shoot
    //   an error with the FrVect structure.
    //-------------------------------------------------------------------
#define display_info_( TAG, ACCESSOR )                                         \
    BOOST_TEST_MESSAGE( TAG << ": " << v->ACCESSOR( )                          \
                            << " =?= " << sub_vect->ACCESSOR( ) )
#define display_info_hex_( TAG, ACCESSOR )                                     \
    BOOST_TEST_MESSAGE( TAG << ": 0x" << std::hex << v->ACCESSOR( )            \
                            << " =?= 0x" << sub_vect->ACCESSOR( ) )

    display_info_( "Name", GetName );
    display_info_hex_( "Compress", GetCompress );
    display_info_( "Type", GetType );
    display_info_( "NData", GetNData );
    display_info_( "NBytes", GetNBytes );
    display_info_( "NDim", GetNDim );
    display_info_( "UnitY", GetUnitY );
    BOOST_TEST_MESSAGE( "Compare Dim info: "
                        << std::boolalpha
                        << ( v->GetDims( ) == sub_vect->GetDims( ) ) );
    BOOST_TEST_MESSAGE(
        "Compare raw data: "
        << std::boolalpha
        << std::equal( v->GetDataRaw( ).get( ),
                       v->GetDataRaw( ).get( ) + v->GetNBytes( ),
                       sub_vect->GetDataRaw( ).get( ) ) );

#undef display_info_
#endif /* EXTRA_VERBOSE */

    BOOST_CHECK( ( *v == *sub_vect ) );
#endif /* WORKING */
}

BOOST_AUTO_TEST_CASE( FrStructs )
{
    //---------------------------------------------------------------------
    // Testing of basic data types
    //---------------------------------------------------------------------
    // fr_detector( ); // FrDetector
    fr_vect( ); // FrVect
    fr_adc_data( ); // FrAdcData

#if DEBUG
    std::cerr << "DEBUG: After fr_adc_data: " << __LINE__ << std::endl;
#endif /* 0 */
    //---------------------------------------------------------------------
    // Test upconverting of frame structures.
    //---------------------------------------------------------------------
    for ( int current_spec = FRAME_SPEC_MIN; current_spec <= FRAME_SPEC_MAX;
          ++current_spec )
    {
        if ( current_spec == 5 )
        {
            //-----------------------------------------------------------------
            // These versions are not supported.
            //-----------------------------------------------------------------
            continue;
        }
        //-------------------------------------------------------------------
        //
        //-------------------------------------------------------------------

        std::ostringstream       leader;
        mk_frame_object_ret_type obj;
    }
}

//=======================================================================
// General routines
//=======================================================================

static void
verify_downconvert( std::ostringstream& Leader,
                    const int           Version,
                    frame_object_type   FrameObj,
                    const std::string&  FrameObjName )
{
    Leader << " down convert from version: " << Version << ": ";
    try
    {
        switch ( Version )
        {
        case 3:
            //-----------------------------------------------------------------
            // This is the first supported version of the frame spec
            //-----------------------------------------------------------------
            return;
        case 4:
            verify_downconvert< 4 >( FrameObj, Leader.str( ) );
            return;
        case 6:
            verify_downconvert< 6 >( FrameObj, Leader.str( ) );
            return;
        case 7:
            verify_downconvert< 7 >( FrameObj, Leader.str( ) );
            return;
#if WORKING
        case 8:
            verify_downconvert< 8 >( FrameObj, Leader.str( ) );
            return;
        case 9:
            verify_downconvert< 9 >( FrameObj, Leader.str( ) );
            return;
#else
        case 8:
        case 9:
            return;
#endif /* WORKING  */
        default:
            break;
        }
        //-------------------------------------------------------------------
        // Unsupported version passed
        //-------------------------------------------------------------------
        std::ostringstream msg;

        msg << "verify_downconvert:"
            << " version: " << Version << " struct: " << FrameObjName;
        throw Unimplemented( msg.str( ), 0, __FILE__, __LINE__ );
    }
    catch ( const std::exception& except )
    {
        BOOST_TEST_MESSAGE( Leader.str( )
                            << "struct: " << FrameObjName
                            << " Caught excpetion: " << except.what( ) );
        BOOST_CHECK( false );
        ;
    }
}

static void
verify_upconvert( std::ostringstream& Leader,
                  const int           Version,
                  frame_object_type   FrameObj,
                  const std::string&  FrameObjName )
{
    Leader << " up convert to version: " << Version << ": ";
    try
    {
        switch ( Version )
        {
        case 3:
            //-----------------------------------------------------------------
            // This is the first supported version of the frame spec
            //-----------------------------------------------------------------
            return;
        case 4:
            verify_upconvert< 4 >( FrameObj, Leader.str( ) );
            return;
        case 6:
            verify_upconvert< 6 >( FrameObj, Leader.str( ) );
            return;
        case 7:
            verify_upconvert< 7 >( FrameObj, Leader.str( ) );
            return;
        case 8:
            verify_upconvert< 8 >( FrameObj, Leader.str( ) );
            return;
        case 9:
            verify_upconvert< 9 >( FrameObj, Leader.str( ) );
            return;
        default:
            break;
        }
        //-------------------------------------------------------------------
        // Unsupported version passed
        //-------------------------------------------------------------------
        std::ostringstream msg;

        msg << "verify_upconvert:"
            << " version: " << Version << " struct: " << FrameObjName;
        throw Unimplemented( msg.str( ), 0, __FILE__, __LINE__ );
    }
    catch ( const std::exception& except )
    {
        BOOST_TEST_MESSAGE( Leader.str( )
                            << "struct: " << FrameObjName
                            << " Caught excpetion: " << except.what( ) );
        BOOST_CHECK( false );
        ;
    }
}

//=======================================================================
// Frame Object
//=======================================================================
mk_frame_object_ret_type
mk_frame_object( int SpecVersion, FrameSpec::Info::frame_object_types Type )
{
    try
    {
        switch ( SpecVersion )
        {
        case 3:
            return mk_frame_object< 3 >( Type );
        case 4:
            return mk_frame_object< 4 >( Type );
        case 6:
            return mk_frame_object< 6 >( Type );
        case 7:
            return mk_frame_object< 7 >( Type );
        case 8:
            return mk_frame_object< 8 >( Type );
        case 9:
            return mk_frame_object< 9 >( Type );
        default:
        {
            std::ostringstream msg;

            msg << "mk_frame_object: Unsupported version: " << SpecVersion;
            throw Unimplemented( msg.str( ), 0, __FILE__, __LINE__ );
        }
        break;
        }
    }
    catch ( const std::exception& except )
    {
        BOOST_TEST_MESSAGE( " mk_frame_object: "
                            << " Caught excpetion: " << except.what( ) );
        BOOST_CHECK( false );
        ;
    }
    return mk_frame_object_ret_type( );
}
