#ifndef fr_detector_hh
#define fr_detector_hh

#include <cmath>
#include <typeinfo>
#include <type_traits>

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

#include "ldastoolsal/ldas_types.h"

#include "framecpp/FrDetector.hh"

namespace testing
{
    namespace fr_detector
    {
        INSTANTIATE_STRUCT_ALL( FrDetector, FR_DETECTOR_TYPE );

        template < int T_FRAME_SPEC_VERSION >
        using fr_detector_type =
            typename fr_object_impl< T_FRAME_SPEC_VERSION,
                                     FR_DETECTOR_TYPE >::type;
        template < int T_FRAME_SPEC_VERSION >
        using fr_detector_previous_type =
            typename fr_object_previous_impl< T_FRAME_SPEC_VERSION,
                                              FR_DETECTOR_TYPE >::type;

        //===============================
        // F U N C T I O N: DMSToRadians
        //===============================
        static inline REAL_8
        DMSToRadians( INT_2S Degrees, INT_2S Minutes, REAL_4 Seconds )
        {
            REAL_8 retval = 0.0;

            if ( Degrees >= 0 )
            {
                retval = (REAL_8)Degrees + (REAL_8)Minutes / 60. +
                    (REAL_8)Seconds / 3600.;
            }
            else
            {
                retval = (REAL_8)Degrees - (REAL_8)Minutes / 60. -
                    (REAL_8)Seconds / 3600.;
            }
            retval = ( retval * M_PI ) / 180.;

            return retval;
        }

        //======================================
        // F U N C T I O N:  version
        //======================================
        // -----
        /// \brief Track where the transitions take place
        // -----
        typedef base_version_constant< 3 > version_root_type;

        template < int T_FRAME_SPEC_VERSION >
        constexpr bool
        is_root( )
        {
            return ( T_FRAME_SPEC_VERSION == version_root_type::value );
        }

        // ---------------
        // version_changes
        //
        // Track where changes to this data structure appear in the frame
        // specification
        // ---------------
        template < int T_FRAME_SPEC_VERSION >
        constexpr int
        version_changes( )
        {
            return ( std::conditional< T_FRAME_SPEC_VERSION >= 8,
                                       base_version_constant< 8 >::type,
                                       typename std::conditional<
                                           T_FRAME_SPEC_VERSION >= 6,
                                           base_version_constant< 6 >::type,
                                           typename std::conditional<
                                               T_FRAME_SPEC_VERSION >= 4,
                                               base_version_constant< 4 >::type,
                                               typename std::enable_if<
                                                   T_FRAME_SPEC_VERSION >=
                                                       version_root_type::value,
                                                   version_root_type::type >
                                               // conditional - 4
                                               >::type
                                           // conditional - 6
                                           >::type
                                       // conditional - 8
                                       >::type
                     // return value
                     ::type::value );
        }

        template < int T_FRAME_SPEC_VERSION >
        constexpr VERSION
                  version( )
        {
            return ( frame_spec_to_version_mapper<
                     version_changes< T_FRAME_SPEC_VERSION >( ) >( ) );
        }

        template < int T_FRAME_SPEC_VERSION >
        constexpr bool
        is_change_version( )
        {
            return ( testing::is_change_version<
                     T_FRAME_SPEC_VERSION,
                     version_changes< T_FRAME_SPEC_VERSION >( ) >( ) );
        }

        // ============
        // version_info
        //
        // Custumization point on how each change to the spec should be handled
        // ============
        template < int T_FRAME_SPEC_VERSION, VERSION T_VERSION >
        struct version_info;

        template < int T_FRAME_SPEC_VERSION >
        struct version_info< T_FRAME_SPEC_VERSION, VERSION::V3 >
        {
            constexpr static int frame_spec_version{ T_FRAME_SPEC_VERSION };

            typedef std::string name_type;
            typedef INT_2S      longitudeD_type;
            typedef INT_2S      longitudeM_type;
            typedef REAL_4      longitudeS_type;
            typedef INT_2S      latitudeD_type;
            typedef INT_2S      latitudeM_type;
            typedef REAL_4      latitudeS_type;
            typedef REAL_4      elevation_type;
            typedef REAL_4      armXazimuth_type;
            typedef REAL_4      armYazimuth_type;
            typedef REAL_4      armLength_type;

            struct expected_type
            {
                // -----------
                // Seed values
                // -----------
                // FrDetector
                name_type        name{ name_type( "name" ) };
                longitudeD_type  longitudeD{ 1 };
                longitudeM_type  longitudeM{ 2 };
                longitudeS_type  longitudeS{ 3.0 };
                latitudeD_type   latitudeD{ 4 };
                latitudeM_type   latitudeM{ 5 };
                latitudeS_type   latitudeS{ 6.0 };
                elevation_type   elevation{ 7.0 };
                armXazimuth_type armXazimuth{ 8.0 };
                armYazimuth_type armYazimuth{ 8.0 };
                armLength_type   armLength{ 9.0 };

                bool
                compare(
                    const fr_detector_type< T_FRAME_SPEC_VERSION >& Actual )
                {
                    return (
                        // FrDetector
                        ( Actual.GetName( ).compare( name ) == 0 ) &&
                        ( Actual.GetLongitudeD( ) == longitudeD ) &&
                        ( Actual.GetLongitudeM( ) == longitudeM ) &&
                        ( Actual.GetLongitudeS( ) == longitudeS ) &&
                        ( Actual.GetLatitudeD( ) == latitudeD ) &&
                        ( Actual.GetLatitudeM( ) == latitudeM ) &&
                        ( Actual.GetLatitudeS( ) == latitudeS ) &&
                        ( Actual.GetElevation( ) == elevation ) &&
                        ( Actual.GetArmXazimuth( ) == armXazimuth ) &&
                        ( Actual.GetArmYazimuth( ) == armYazimuth ) &&
                        ( Actual.GetArmLength( ) == armLength )
                        // FrDetector end
                    );
                }
                void
                set_default_constructor_values( )
                {
                    // local overrides for corner case of default constructor
                    name = name_type( "" );
                    longitudeD = 0;
                    longitudeM = 0;
                    longitudeS = 0.0;
                    latitudeD = 0;
                    latitudeM = 0;
                    latitudeS = 0.0;
                    elevation = 0.0;
                    armXazimuth = 0.0;
                    armYazimuth = 0.0;
                    armLength = 0.0;
                }
            };

            static bool
            validate_constructors( )
            {
                bool retval{ true };

                try
                {
                    // -------------------
                    // Default constructor
                    // -------------------
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj;

                    expected_type expected;
                    expected.set_default_constructor_values( );

                    std::cerr << "DEBUG: Default constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }
                // -----
                // Explicit constructor
                // -----
                try
                {
                    expected_type                            expected;
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj(
                        expected.name,
                        expected.longitudeD,
                        expected.longitudeM,
                        expected.longitudeS,
                        expected.latitudeD,
                        expected.latitudeM,
                        expected.latitudeS,
                        expected.elevation,
                        expected.armXazimuth,
                        expected.armYazimuth,
                        expected.armLength );

                    std::cerr << "DEBUG: Explicit constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }
                // -----
                // Copy constructor
                // -----
                try
                {
                    expected_type                            expected;
                    fr_detector_type< T_FRAME_SPEC_VERSION > source_obj(
                        expected.name,
                        expected.longitudeD,
                        expected.longitudeM,
                        expected.longitudeS,
                        expected.latitudeD,
                        expected.latitudeM,
                        expected.latitudeS,
                        expected.elevation,
                        expected.armXazimuth,
                        expected.armYazimuth,
                        expected.armLength );
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj( source_obj );

                    std::cerr << "DEBUG: Copy constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }

                return ( retval );
            }

            static bool
            validate_data_types( )
            {
                fr_detector_type< frame_spec_version > obj;

                return ( // FrDetector
                    ( check_data_type_string< frame_spec_version >(
                        obj.GetName( ) ) ) &&
                    ( check_data_type< longitudeD_type >(
                        obj.GetLongitudeD( ) ) ) &&
                    ( check_data_type< longitudeM_type >(
                        obj.GetLongitudeM( ) ) ) &&
                    ( check_data_type< longitudeS_type >(
                        obj.GetLongitudeS( ) ) ) &&
                    ( check_data_type< latitudeD_type >(
                        obj.GetLatitudeD( ) ) ) &&
                    ( check_data_type< latitudeM_type >(
                        obj.GetLatitudeM( ) ) ) &&
                    ( check_data_type< latitudeS_type >(
                        obj.GetLatitudeS( ) ) ) &&
                    ( check_data_type< elevation_type >(
                        obj.GetElevation( ) ) ) &&
                    ( check_data_type< armXazimuth_type >(
                        obj.GetArmXazimuth( ) ) ) &&
                    ( check_data_type< armYazimuth_type >(
                        obj.GetArmYazimuth( ) ) ) &&
                    ( check_data_type< armLength_type >( obj.GetArmLength( ) ) )
                    // FrDetector - end
                );
            }
        };

        template < int T_FRAME_SPEC_VERSION >
        struct version_info< T_FRAME_SPEC_VERSION, VERSION::V4 >
        {
            constexpr static int frame_spec_version{ T_FRAME_SPEC_VERSION };

            using current_type = fr_detector_type< frame_spec_version >;
            using previous_type =
                fr_detector_previous_type< frame_spec_version >;

            typedef std::string name_type;
            typedef INT_2S      longitudeD_type;
            typedef INT_2S      longitudeM_type;
            typedef REAL_4      longitudeS_type;
            typedef INT_2S      latitudeD_type;
            typedef INT_2S      latitudeM_type;
            typedef REAL_4      latitudeS_type;
            typedef REAL_4      elevation_type;
            typedef REAL_4      armXazimuth_type;
            typedef REAL_4      armYazimuth_type;

            struct expected_type
            {
                // -----------
                // Seed values
                // -----------
                // FrDetector
                name_type        name{ name_type( "name" ) };
                longitudeD_type  longitudeD{ 1 };
                longitudeM_type  longitudeM{ 2 };
                longitudeS_type  longitudeS{ 3.0 };
                latitudeD_type   latitudeD{ 4 };
                latitudeM_type   latitudeM{ 5 };
                latitudeS_type   latitudeS{ 6.0 };
                elevation_type   elevation{ 7.0 };
                armXazimuth_type armXazimuth{ 8.0 };
                armYazimuth_type armYazimuth{ 8.0 };

                bool
                compare(
                    const fr_detector_type< T_FRAME_SPEC_VERSION >& Actual )
                {
                    return (
                        // FrDetector
                        ( Actual.GetName( ).compare( name ) == 0 ) &&
                        ( Actual.GetLongitudeD( ) == longitudeD ) &&
                        ( Actual.GetLongitudeM( ) == longitudeM ) &&
                        ( Actual.GetLongitudeS( ) == longitudeS ) &&
                        ( Actual.GetLatitudeD( ) == latitudeD ) &&
                        ( Actual.GetLatitudeM( ) == latitudeM ) &&
                        ( Actual.GetLatitudeS( ) == latitudeS ) &&
                        ( Actual.GetArmXazimuth( ) == armXazimuth ) &&
                        ( Actual.GetArmYazimuth( ) == armYazimuth )
                        // FrDetector end
                    );
                }
                void
                set_default_constructor_values( )
                {
                    // local overrides for corner case of default constructor
                    name = name_type( "" );
                    longitudeD = 0;
                    longitudeM = 0;
                    longitudeS = 0.0;
                    latitudeD = 0;
                    latitudeM = 0;
                    latitudeS = 0.0;
                    elevation = 0.0;
                    armXazimuth = 0.0;
                    armYazimuth = 0.0;
                }
            };

            static bool
            validate_constructors( )
            {
                bool retval{ true };

                try
                {
                    // -------------------
                    // Default constructor
                    // -------------------
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj;

                    expected_type expected;
                    expected.set_default_constructor_values( );

                    std::cerr << "DEBUG: Default constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }
                // -----
                // Explicit constructor
                // -----
                try
                {
                    expected_type                            expected;
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj(
                        expected.name,
                        expected.longitudeD,
                        expected.longitudeM,
                        expected.longitudeS,
                        expected.latitudeD,
                        expected.latitudeM,
                        expected.latitudeS,
                        expected.elevation,
                        expected.armXazimuth,
                        expected.armYazimuth );

                    std::cerr << "DEBUG: Explicit constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }
                // -----
                // Copy constructor
                // -----
                try
                {
                    expected_type                            expected;
                    fr_detector_type< T_FRAME_SPEC_VERSION > source_obj(
                        expected.name,
                        expected.longitudeD,
                        expected.longitudeM,
                        expected.longitudeS,
                        expected.latitudeD,
                        expected.latitudeM,
                        expected.latitudeS,
                        expected.elevation,
                        expected.armXazimuth,
                        expected.armYazimuth

                    );
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj( source_obj );

                    std::cerr << "DEBUG: Copy constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }

                return ( retval );
            }

            static bool
            validate_data_types( )
            {
                fr_detector_type< frame_spec_version > obj;

                return ( // FrDetector
                    ( check_data_type_string< frame_spec_version >(
                        obj.GetName( ) ) ) &&
                    ( check_data_type< longitudeD_type >(
                        obj.GetLongitudeD( ) ) ) &&
                    ( check_data_type< longitudeM_type >(
                        obj.GetLongitudeM( ) ) ) &&
                    ( check_data_type< longitudeS_type >(
                        obj.GetLongitudeS( ) ) ) &&
                    ( check_data_type< latitudeD_type >(
                        obj.GetLatitudeD( ) ) ) &&
                    ( check_data_type< latitudeM_type >(
                        obj.GetLatitudeM( ) ) ) &&
                    ( check_data_type< latitudeS_type >(
                        obj.GetLatitudeS( ) ) ) &&
                    ( check_data_type< elevation_type >(
                        obj.GetElevation( ) ) ) &&
                    ( check_data_type< armXazimuth_type >(
                        obj.GetArmXazimuth( ) ) ) &&
                    ( check_data_type< armYazimuth_type >(
                        obj.GetArmYazimuth( ) ) )
                    // FrDetector - end
                );
            }

            static bool
            compare_down_convert( object_type< frame_spec_version > Source,
                                  object_type< frame_spec_version > Derived )
            {
                bool retval{ true };

                try
                {
                    // -----
                    // Cast to concreate type
                    // -----
                    auto source_obj =
                        boost::dynamic_pointer_cast< current_type >( Source );
                    auto derived_obj =
                        boost::dynamic_pointer_cast< previous_type >( Derived );
                    // -----
                    // Do comparison
                    // -----
                    return (
                        // Must be different addresses
                        ( static_cast< const void* >( source_obj.get( ) ) !=
                          static_cast< const void* >( derived_obj.get( ) ) ) &&
                        // FrDetector
                        ( source_obj->GetName( ).compare(
                              derived_obj->GetName( ) ) == 0 ) &&
                        ( source_obj->GetLongitudeD( ) ==
                          derived_obj->GetLongitudeD( ) ) &&
                        ( source_obj->GetLongitudeM( ) ==
                          derived_obj->GetLongitudeM( ) ) &&
                        ( source_obj->GetLongitudeS( ) ==
                          derived_obj->GetLongitudeS( ) ) &&
                        ( source_obj->GetLatitudeD( ) ==
                          derived_obj->GetLatitudeD( ) ) &&
                        ( source_obj->GetLatitudeM( ) ==
                          derived_obj->GetLatitudeM( ) ) &&
                        ( source_obj->GetLatitudeS( ) ==
                          derived_obj->GetLatitudeS( ) ) &&
                        ( source_obj->GetElevation( ) ==
                          derived_obj->GetElevation( ) ) &&
                        ( source_obj->GetArmXazimuth( ) ==
                          derived_obj->GetArmXazimuth( ) ) &&
                        ( source_obj->GetArmYazimuth( ) ==
                          derived_obj->GetArmYazimuth( ) ) &&
                        ( 0 == derived_obj->GetArmLength( ) )
                        // FrDetector end
                    );
                }
                catch ( ... )
                {
                    retval = false;
                }
                return ( retval );
            }

            static bool
            compare_up_convert( object_type< frame_spec_version > Source,
                                object_type< frame_spec_version > Derived )
            {
                bool retval{ true };

                try
                {
                    // -----
                    // Cast to concreate type
                    // -----
                    auto source_obj =
                        boost::dynamic_pointer_cast< previous_type >( Source );
                    auto derived_obj =
                        boost::dynamic_pointer_cast< current_type >( Derived );

                    return (
                        // FrDetector - begin
                        ( source_obj->GetName( ).compare(
                              derived_obj->GetName( ) ) == 0 ) &&
                        ( source_obj->GetLongitudeD( ) ==
                          derived_obj->GetLongitudeD( ) ) &&
                        ( source_obj->GetLongitudeM( ) ==
                          derived_obj->GetLongitudeM( ) ) &&
                        ( source_obj->GetLongitudeS( ) ==
                          derived_obj->GetLongitudeS( ) ) &&
                        ( source_obj->GetLatitudeD( ) ==
                          derived_obj->GetLatitudeD( ) ) &&
                        ( source_obj->GetLatitudeM( ) ==
                          derived_obj->GetLatitudeM( ) ) &&
                        ( source_obj->GetLatitudeS( ) ==
                          derived_obj->GetLatitudeS( ) ) &&
                        ( source_obj->GetElevation( ) ==
                          derived_obj->GetElevation( ) ) &&
                        ( source_obj->GetArmXazimuth( ) ==
                          derived_obj->GetArmXazimuth( ) ) &&
                        ( source_obj->GetArmYazimuth( ) ==
                          derived_obj->GetArmYazimuth( ) )
                        // FrDetector - end
                    );
                }
                catch ( ... )
                {
                    retval = false;
                }
                return ( retval );
            }
        };

        template < int T_FRAME_SPEC_VERSION >
        struct version_info< T_FRAME_SPEC_VERSION, VERSION::V6 >
        {
            constexpr static int frame_spec_version{ T_FRAME_SPEC_VERSION };

            using current_type = fr_detector_type< frame_spec_version >;
            using previous_type =
                fr_detector_previous_type< frame_spec_version >;

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

            typedef std::string name_type;
            typedef REAL_8      longitude_type;
            typedef REAL_8      latitude_type;
            typedef REAL_4      elevation_type;
            typedef REAL_4      armXazimuth_type;
            typedef REAL_4      armYazimuth_type;
            typedef REAL_4      armXaltitude_type;
            typedef REAL_4      armYaltitude_type;
            typedef REAL_4      armXmidpoint_type;
            typedef REAL_4      armYmidpoint_type;
            typedef INT_4S      localTime_type;

            struct expected_type
            {
                // -----------
                // Seed values
                // -----------
                // FrDetector
                name_type         name{ name_type( "name" ) };
                char              prefix[ 2 ]{ 'a', 'b' };
                longitude_type    longitude{ 1.0 };
                latitude_type     latitude{ 2.0 };
                elevation_type    elevation{ 3.0 };
                armXazimuth_type  armXazimuth{ 4.0 };
                armYazimuth_type  armYazimuth{ 5.0 };
                armXaltitude_type armXaltitude{ 6.0 };
                armYaltitude_type armYaltitude{ 7.0 };
                armXmidpoint_type armXmidpoint{ 8.0 };
                armYmidpoint_type armYmidpoint{ 9.0 };
                localTime_type    localTime{ 1800 };

                bool
                compare(
                    const fr_detector_type< T_FRAME_SPEC_VERSION >& Actual )
                {
                    return (
                        // FrDetector
                        ( Actual.GetName( ).compare( name ) == 0 ) &&
                        ( Actual.GetLongitude( ) == longitude ) &&
                        ( Actual.GetLatitude( ) == latitude ) &&
                        ( Actual.GetElevation( ) == elevation ) &&
                        ( Actual.GetArmXazimuth( ) == armXazimuth ) &&
                        ( Actual.GetArmYazimuth( ) == armYazimuth ) &&
                        ( Actual.GetArmXaltitude( ) == armXaltitude ) &&
                        ( Actual.GetArmYaltitude( ) == armYaltitude ) &&
                        ( Actual.GetArmXmidpoint( ) == armXmidpoint ) &&
                        ( Actual.GetArmYmidpoint( ) == armYmidpoint ) &&
                        ( Actual.GetLocalTime( ) == localTime )
                        // FrDetector end
                    );
                }
                void
                set_default_constructor_values( )
                {
                    // local overrides for corner case of default constructor
                    name = name_type( "" );
                    prefix[ 0 ] = ' ';
                    prefix[ 1 ] = ' ';
                    longitude = 0.0;
                    latitude = 0.0;
                    elevation = 0.0;
                    armXazimuth = 0.0;
                    armYazimuth = 0.0;
                    armXaltitude = 0.0;
                    armYaltitude = 0.0;
                    armXmidpoint = 0.0;
                    armYmidpoint = 0.0;
                    localTime = 0;
                }
            };

            static bool
            validate_constructors( )
            {
                bool retval{ true };

                try
                {
                    // -------------------
                    // Default constructor
                    // -------------------
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj;

                    expected_type expected;
                    expected.set_default_constructor_values( );

                    std::cerr << "DEBUG: Default constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }
                // -----
                // Explicit constructor
                // -----
                try
                {
                    expected_type                            expected;
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj(
                        expected.name,
                        expected.prefix,
                        expected.longitude,
                        expected.latitude,
                        expected.elevation,
                        expected.armXazimuth,
                        expected.armYazimuth,
                        expected.armXaltitude,
                        expected.armYaltitude,
                        expected.armXmidpoint,
                        expected.armYmidpoint,
                        expected.localTime );

                    std::cerr << "DEBUG: Explicit constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }
                // -----
                // Copy constructor
                // -----
                try
                {
                    expected_type                            expected;
                    fr_detector_type< T_FRAME_SPEC_VERSION > source_obj(
                        expected.name,
                        expected.prefix,
                        expected.longitude,
                        expected.latitude,
                        expected.elevation,
                        expected.armXazimuth,
                        expected.armYazimuth,
                        expected.armXaltitude,
                        expected.armYaltitude,
                        expected.armXmidpoint,
                        expected.armYmidpoint,
                        expected.localTime );
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj( source_obj );

                    std::cerr << "DEBUG: Copy constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }

                return ( retval );
            }

            static bool
            validate_data_types( )
            {
                fr_detector_type< frame_spec_version > obj;

                return ( // FrDetector
                    ( check_data_type_string< frame_spec_version >(
                        obj.GetName( ) ) ) &&
                    ( check_data_type< longitude_type >(
                        obj.GetLongitude( ) ) ) &&
                    ( check_data_type< latitude_type >(
                        obj.GetLatitude( ) ) ) &&
                    ( check_data_type< elevation_type >(
                        obj.GetElevation( ) ) ) &&
                    ( check_data_type< armXazimuth_type >(
                        obj.GetArmXazimuth( ) ) ) &&
                    ( check_data_type< armYazimuth_type >(
                        obj.GetArmYazimuth( ) ) ) &&
                    ( check_data_type< armXaltitude_type >(
                        obj.GetArmXaltitude( ) ) ) &&
                    ( check_data_type< armYaltitude_type >(
                        obj.GetArmYaltitude( ) ) ) &&
                    ( check_data_type< armXmidpoint_type >(
                        obj.GetArmXmidpoint( ) ) ) &&
                    ( check_data_type< armYmidpoint_type >(
                        obj.GetArmYmidpoint( ) ) ) &&
                    ( check_data_type< localTime_type >( obj.GetLocalTime( ) ) )
                    // FrDetector - end
                );
            }

            static bool
            compare_down_convert( object_type< frame_spec_version > Source,
                                  object_type< frame_spec_version > Derived,
                                  REAL_8 Multiplier = 1.0 )
            {
                bool retval{ true };

                try
                {
                    // -----
                    // Cast to concreate type
                    // -----
                    std::cerr << "DEBUG: compare_down_convert: "
                              << (void*)( Source.get( ) )
                              << " previous: " << (void*)( Derived.get( ) )
                              << std::endl;
                    auto source_obj =
                        boost::dynamic_pointer_cast< current_type >( Source );
                    auto derived_obj =
                        boost::dynamic_pointer_cast< previous_type >( Derived );
                    auto expected_derived_armXazimuth =
                        ( ( Multiplier * source_obj->GetArmXazimuth( ) ) +
                          M_PI / 2.0 );
                    auto expected_derived_armYazimuth =
                        ( ( Multiplier * source_obj->GetArmYazimuth( ) ) +
                          M_PI / 2.0 );

                    // -----
                    // Do comparison
                    // -----
                    std::cerr
                        << "DEBUG: armXazimuth " << expected_derived_armXazimuth
                        << " =?= " << derived_obj->GetArmXazimuth( ) << " ("
                        << std::abs( expected_derived_armXazimuth -
                                     derived_obj->GetArmXazimuth( ) )
                        << ")" << std::endl;
                    std::cerr
                        << "DEBUG: armYazimuth " << expected_derived_armYazimuth
                        << " =?= " << derived_obj->GetArmYazimuth( ) << " ("
                        << std::abs( expected_derived_armYazimuth -
                                     derived_obj->GetArmYazimuth( ) )
                        << ")" << std::endl;
                    return (
                        // Must be different addresses
                        ( static_cast< const void* >( source_obj.get( ) ) !=
                          static_cast< const void* >( derived_obj.get( ) ) )
                        // Address check - end
                        &&
                        // FrDetector
                        ( source_obj->GetName( ).compare(
                              derived_obj->GetName( ) ) == 0 ) &&
                        ( std::abs(
                              source_obj->GetLongitude( ) -
                              DMSToRadians( derived_obj->GetLongitudeD( ),
                                            derived_obj->GetLongitudeM( ),
                                            derived_obj->GetLongitudeS( ) ) ) <=
                          0.0001 ) &&
                        ( std::abs(
                              source_obj->GetLatitude( ) -
                              DMSToRadians( derived_obj->GetLatitudeD( ),
                                            derived_obj->GetLatitudeM( ),
                                            derived_obj->GetLatitudeS( ) ) ) <=
                          0.0001 ) &&
                        ( source_obj->GetElevation( ) ==
                          derived_obj->GetElevation( ) ) &&
                        ( std::abs( expected_derived_armXazimuth -
                                    derived_obj->GetArmXazimuth( ) ) <=
                          0.001 ) &&
                        ( std::abs( expected_derived_armYazimuth -
                                    derived_obj->GetArmYazimuth( ) ) <= 0.001 )
                        // FrDetector end
                    );
                }
                catch ( ... )
                {
                    retval = false;
                }
                return ( retval );
            }

            static bool
            compare_up_convert( object_type< frame_spec_version > Source,
                                object_type< frame_spec_version > Derived )
            {
                bool retval{ true };

                try
                {
                    // -----
                    // Cast to concreate type
                    // -----
                    auto derived_obj =
                        boost::dynamic_pointer_cast< current_type >( Derived );

                    {
                        auto source_obj =
                            boost::dynamic_pointer_cast< previous_type >(
                                Source );

                        std::cerr << "DEBUG: upconvert: source: armXazimuth: "
                                  << source_obj->GetArmXazimuth( ) << std::endl;
                        std::cerr << "DEBUG: upconvert: derived: armXazimuth: "
                                  << derived_obj->GetArmXazimuth( )
                                  << std::endl;
                    }

                    return ( compare_down_convert( Derived, Source, -1.0 )
                             // Previous - end
                             &&
                             // New fields - begin
                             ( derived_obj->GetArmXaltitude( ) == 0.0 ) &&
                             ( derived_obj->GetArmYaltitude( ) == 0.0 ) &&
                             ( derived_obj->GetArmXmidpoint( ) == 0.0 ) &&
                             ( derived_obj->GetArmYmidpoint( ) == 0.0 ) &&
                             ( derived_obj->GetLocalTime( ) == 0 )
                             // New fields - end
                    );
                }
                catch ( ... )
                {
                    retval = false;
                }
                return ( retval );
            }
        };

        template < int T_FRAME_SPEC_VERSION >
        struct version_info< T_FRAME_SPEC_VERSION, VERSION::V8 >
        {
            constexpr static int frame_spec_version{ T_FRAME_SPEC_VERSION };

            using current_type = fr_detector_type< frame_spec_version >;
            using previous_type =
                fr_detector_previous_type< frame_spec_version >;

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

            typedef std::string name_type;
            typedef char*       prefix_type;
            typedef REAL_8      longitude_type;
            typedef REAL_8      latitude_type;
            typedef REAL_4      elevation_type;
            typedef REAL_4      armXazimuth_type;
            typedef REAL_4      armYazimuth_type;
            typedef REAL_4      armXaltitude_type;
            typedef REAL_4      armYaltitude_type;
            typedef REAL_4      armXmidpoint_type;
            typedef REAL_4      armYmidpoint_type;
            typedef INT_4S      localTime_type;

            struct expected_type
            {
                // -----------
                // Seed values
                // -----------
                // FrDetector
                name_type         name{ name_type( "name" ) };
                char              prefix[ 2 ]{ 'a', 'b' };
                longitude_type    longitude{ 1.0 };
                latitude_type     latitude{ 2.0 };
                elevation_type    elevation{ 3.0 };
                armXazimuth_type  armXazimuth{ 4.0 };
                armYazimuth_type  armYazimuth{ 5.0 };
                armXaltitude_type armXaltitude{ 6.0 };
                armYaltitude_type armYaltitude{ 7.0 };
                armXmidpoint_type armXmidpoint{ 8.0 };
                armYmidpoint_type armYmidpoint{ 9.0 };
                localTime_type    localTime{ 1800 };

                bool
                compare(
                    const fr_detector_type< T_FRAME_SPEC_VERSION >& Actual )
                {
                    return (
                        // FrDetector
                        ( Actual.GetName( ).compare( name ) == 0 ) &&
                        ( Actual.GetLongitude( ) == longitude ) &&
                        ( Actual.GetLatitude( ) == latitude ) &&
                        ( Actual.GetElevation( ) == elevation ) &&
                        ( Actual.GetArmXazimuth( ) == armXazimuth ) &&
                        ( Actual.GetArmYazimuth( ) == armYazimuth ) &&
                        ( Actual.GetArmXaltitude( ) == armXaltitude ) &&
                        ( Actual.GetArmYaltitude( ) == armYaltitude ) &&
                        ( Actual.GetArmXmidpoint( ) == armXmidpoint ) &&
                        ( Actual.GetArmYmidpoint( ) == armYmidpoint ) &&
                        ( Actual.GetLocalTime( ) == localTime )
                        // FrDetector end
                    );
                }
                void
                set_default_constructor_values( )
                {
                    // local overrides for corner case of default
                    // constructor
                    name = name_type( "" );
                    longitude = 0.0;
                    latitude = 0.0;
                    elevation = 0.0;
                    armXazimuth = 0.0;
                    armYazimuth = 0.0;
                    armXaltitude = 0.0;
                    armYaltitude = 0.0;
                    armXmidpoint = 0.0;
                    armYmidpoint = 0.0;
                    localTime = 0;
                }
            };

            static bool
            validate_constructors( )
            {
                bool retval{ true };

                try
                {
                    // -------------------
                    // Default constructor
                    // -------------------
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj;

                    expected_type expected;
                    expected.set_default_constructor_values( );

                    std::cerr << "DEBUG: Default constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }
                // -----
                // Explicit constructor
                // -----
                try
                {
                    expected_type                            expected;
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj(
                        expected.name,
                        expected.prefix,
                        expected.longitude,
                        expected.latitude,
                        expected.elevation,
                        expected.armXazimuth,
                        expected.armYazimuth,
                        expected.armXaltitude,
                        expected.armYaltitude,
                        expected.armXmidpoint,
                        expected.armYmidpoint,
                        expected.localTime );

                    std::cerr << "DEBUG: Explicit constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }
                // -----
                // Copy constructor
                // -----
                try
                {
                    expected_type                            expected;
                    fr_detector_type< T_FRAME_SPEC_VERSION > source_obj(
                        expected.name,
                        expected.prefix,
                        expected.longitude,
                        expected.latitude,
                        expected.elevation,
                        expected.armXazimuth,
                        expected.armYazimuth,
                        expected.armXaltitude,
                        expected.armYaltitude,
                        expected.armXmidpoint,
                        expected.armYmidpoint,
                        expected.localTime );
                    fr_detector_type< T_FRAME_SPEC_VERSION > obj( source_obj );

                    std::cerr << "DEBUG: Copy constructor "
                              << expected.compare( obj ) << std::endl;
                    retval = retval && expected.compare( obj );
                }
                catch ( ... )
                {
                    retval = false;
                }

                return ( retval );
            }

            static bool
            validate_data_types( )
            {
                fr_detector_type< frame_spec_version > obj;

                return ( // FrDetector
                    ( check_data_type_string< frame_spec_version >(
                        obj.GetName( ) ) ) &&
                    ( check_data_type< longitude_type >(
                        obj.GetLongitude( ) ) ) &&
                    ( check_data_type< latitude_type >(
                        obj.GetLatitude( ) ) ) &&
                    ( check_data_type< elevation_type >(
                        obj.GetElevation( ) ) ) &&
                    ( check_data_type< armXazimuth_type >(
                        obj.GetArmXazimuth( ) ) ) &&
                    ( check_data_type< armYazimuth_type >(
                        obj.GetArmYazimuth( ) ) ) &&
                    ( check_data_type< armXaltitude_type >(
                        obj.GetArmXaltitude( ) ) ) &&
                    ( check_data_type< armYaltitude_type >(
                        obj.GetArmYaltitude( ) ) ) &&
                    ( check_data_type< armXmidpoint_type >(
                        obj.GetArmXmidpoint( ) ) ) &&
                    ( check_data_type< armYmidpoint_type >(
                        obj.GetArmYmidpoint( ) ) ) &&
                    ( check_data_type< localTime_type >( obj.GetLocalTime( ) ) )
                    // FrDetector - end
                );
            }

            static bool
            compare_down_convert( object_type< frame_spec_version > Source,
                                  object_type< frame_spec_version > Derived )
            {
                bool retval{ true };

                try
                {
                    // -----
                    // Cast to concreate type
                    // -----
                    auto source_obj =
                        boost::dynamic_pointer_cast< current_type >( Source );
                    auto derived_obj =
                        boost::dynamic_pointer_cast< previous_type >( Derived );
                    // -----
                    // Do comparison
                    // -----
                    return (
                        // Must be different addresses
                        ( static_cast< const void* >( source_obj.get( ) ) !=
                          static_cast< const void* >( derived_obj.get( ) ) ) &&
                        // FrDetector
                        ( source_obj->GetName( ).compare(
                              derived_obj->GetName( ) ) == 0 ) &&
                        ( source_obj->GetLongitude( ) ==
                          derived_obj->GetLongitude( ) ) &&
                        ( source_obj->GetLatitude( ) ==
                          derived_obj->GetLatitude( ) ) &&
                        ( source_obj->GetElevation( ) ==
                          derived_obj->GetElevation( ) ) &&
                        ( source_obj->GetArmXazimuth( ) ==
                          derived_obj->GetArmXazimuth( ) ) &&
                        ( source_obj->GetArmYazimuth( ) ==
                          derived_obj->GetArmYazimuth( ) ) &&
                        ( source_obj->GetArmXaltitude( ) ==
                          derived_obj->GetArmXaltitude( ) ) &&
                        ( source_obj->GetArmYaltitude( ) ==
                          derived_obj->GetArmYaltitude( ) ) &&
                        ( source_obj->GetArmXmidpoint( ) ==
                          derived_obj->GetArmXmidpoint( ) ) &&
                        ( source_obj->GetArmYmidpoint( ) ==
                          derived_obj->GetArmYmidpoint( ) ) &&
                        ( source_obj->GetLocalTime( ) ==
                          derived_obj->GetLocalTime( ) )
                        // FrDetector end
                    );
                }
                catch ( ... )
                {
                    retval = false;
                }
                return ( retval );
            }

            static bool
            compare_up_convert( object_type< frame_spec_version > Source,
                                object_type< frame_spec_version > Derived )
            {
                bool retval{ true };

                try
                {
                    // -----
                    // Cast to concreate type
                    // -----
                    return ( compare_down_convert( Derived, Source ) );
                }
                catch ( ... )
                {
                    retval = false;
                }
                return ( retval );
            }
        };

        //======================================
        // F U N C T I O N:  mk_fr_detector
        //======================================

        template < int T_FRAME_SPEC_VERSION, VERSION T_VERSION >
        struct mk_fr_detector_helper;

        template < int T_FRAME_SPEC_VERSION >
        struct mk_fr_detector_helper< T_FRAME_SPEC_VERSION, VERSION::V3 >
        {
            using retval_type = fr_detector_type< T_FRAME_SPEC_VERSION >;

            static retval_type*
            create( )
            {
                return ( new retval_type( "name_v3", // name
                                          15, // longitudeD
                                          30, // longitudeM
                                          45.0, // longitudeS
                                          45, // latitudeD
                                          15, // latitudeM
                                          36.0, // latitudeS
                                          100.0, // elevation
                                          0.5, // armXazimuth
                                          0.75, // armYazimuth,
                                          400.0 // armLength
                                          ) );
            };
        };

        template < int T_FRAME_SPEC_VERSION >
        struct mk_fr_detector_helper< T_FRAME_SPEC_VERSION, VERSION::V4 >
        {
            using retval_type = fr_detector_type< T_FRAME_SPEC_VERSION >;

            static retval_type*
            create( )
            {
                return ( new retval_type( "name_v4", // name
                                          15, // longitudeD
                                          30, // longitudeM
                                          45.0, // longitudeS
                                          45, // latitudeD
                                          15, // latitudeM
                                          36.0, // latitudeS
                                          100.0, // elevation
                                          0.5, // armXazimuth
                                          0.75 // armYazimuth
                                          ) );
            };
        };

        template < int T_FRAME_SPEC_VERSION >
        struct mk_fr_detector_helper< T_FRAME_SPEC_VERSION, VERSION::V6 >
        {
            using retval_type = fr_detector_type< T_FRAME_SPEC_VERSION >;

            static retval_type*
            create( )
            {
                return ( new retval_type( "name_v6", // name
                                          "ab", // prefix
                                          15.5125, // longitude
                                          45.26, // latitude
                                          100.0, // elevation
                                          0.5, // armXazimuth
                                          0.75, // armYazimuth
                                          0.25, // armXaltitude
                                          0.5, // armYaltitude
                                          200, // armXmidpoint
                                          300, // armYmidpoint
                                          1800 // localtime
                                          ) );
            };
        };

        template < int T_FRAME_SPEC_VERSION >
        struct mk_fr_detector_helper< T_FRAME_SPEC_VERSION, VERSION::V8 >
        {
            using retval_type = fr_detector_type< T_FRAME_SPEC_VERSION >;

            static retval_type*
            create( )
            {
                return ( new retval_type( "name_v8", // name
                                          "ab", // prefix
                                          15.5125, // longitude
                                          45.26, // latitude
                                          100.0, // elevation
                                          0.5, // armXazimuth
                                          0.75, // armYazimuth
                                          0.25, // armXaltitude
                                          0.5, // armYaltitude
                                          200, // armXmidpoint
                                          300, // armYmidpoint
                                          1800 // localtime
                                          ) );
            };
        };

        template < int T_FRAME_SPEC_VERSION >
        typename FR_DETECTOR_TYPE< T_FRAME_SPEC_VERSION >::type*
        mk_fr_detector( )
        {
            return ( mk_fr_detector_helper<
                     T_FRAME_SPEC_VERSION,
                     version< T_FRAME_SPEC_VERSION >( ) >::create( ) );
        }

        //======================================
        // F U N C T I O N:  verify_data_types
        //======================================

        template < int T_FRAME_SPEC_VERSION >
        bool
        verify_data_types( )
        {
            return (
                version_info<
                    T_FRAME_SPEC_VERSION,
                    version< version_changes< T_FRAME_SPEC_VERSION >( ) >( ) >::
                    validate_data_types( ) );
        }

        //========================================
        // F U N C T I O N:   verify_constructors
        //========================================

        template < int T_FRAME_SPEC_VERSION >
        bool
        verify_constructors( )
        {
            return ( version_info< T_FRAME_SPEC_VERSION,
                                   version< T_FRAME_SPEC_VERSION >( ) >::
                         validate_constructors( ) );
        }

        //======================================
        // F U N C T I O N:   verify_convert
        //======================================

        // -----
        // -----
        template < int T_FRAME_SPEC_VERSION, CONVERT T_CONVERSION >
        struct verify_convert_helper;

        // -----
        /// Specialization to handle T_FRAME_SPEC_VERSION 3 as this is the root
        /// of the frame specification
        // -----
        template < int T_FRAME_SPEC_VERSION >
        struct verify_convert_helper< T_FRAME_SPEC_VERSION, CONVERT::ROOT >
        {
            static bool
            check( )
            {
                return ( convert_check_root< T_FRAME_SPEC_VERSION >(
                    mk_fr_detector< T_FRAME_SPEC_VERSION > ) );
            }
        };

        // -----
        /// Specialization to handle no conversion as the base and derived
        /// versions are the same
        // -----
        template < int T_FRAME_SPEC_VERSION >
        struct verify_convert_helper< T_FRAME_SPEC_VERSION, CONVERT::SAME >
        {
            static bool
            check( )
            {
                return ( convert_check_same< T_FRAME_SPEC_VERSION >(
                    mk_fr_detector< T_FRAME_SPEC_VERSION >,
                    mk_fr_detector< previous_version(
                        T_FRAME_SPEC_VERSION ) > ) );
            }
        };

        // -----
        /// Specialization to handle no conversion as the base and derived
        /// versions are the same
        // -----
        template < int T_FRAME_SPEC_VERSION, CONVERT T_CONVERSION >
        struct verify_convert_helper
        {
            static bool
            check( )
            {
                constexpr INT_4U current{ T_FRAME_SPEC_VERSION };
                constexpr auto   previous = previous_version( current );

                bool retval{ true };

                // -----
                // Down convert
                // -----
                try
                {
                    std::cerr << "DEBUG: down convert: current: " << current
                              << " previous: " << previous << std::endl;
                    // With no conversion, the object returned should be at the
                    // same address
                    object_type< T_FRAME_SPEC_VERSION > source_obj{
                        mk_fr_detector< current >( )
                    };
                    auto derived_obj = source_obj->DemoteObject(
                        previous, source_obj, nullptr );
                    retval = retval &&
                        version_info< T_FRAME_SPEC_VERSION,
                                      frame_spec_to_version_mapper<
                                          convert_to_frame_spec_mapper<
                                              T_CONVERSION >( ) >( ) >::
                            compare_down_convert( source_obj, derived_obj );
                }
                catch ( ... )
                {
                    retval = false;
                }

                // -----
                // Up convert
                // -----
                try
                {
                    std::cerr << "DEBUG: up convert: current: " << current
                              << " previous: " << previous << std::endl;

                    // With no conversion, the object returned should be at the
                    // same address
                    object_type< T_FRAME_SPEC_VERSION > source_obj{
                        mk_fr_detector< previous >( )
                    };
                    std::cerr << "DEBUG: source_obj: " << (void*)(source_obj.get( ))<< std::endl;
                    auto derived_obj = source_obj->PromoteObject(
                        current, previous, source_obj, nullptr );
                    std::cerr << "DEBUG: derived_obj: " << (void*)(derived_obj.get( ))<< std::endl;
                    retval = retval &&
                        version_info< T_FRAME_SPEC_VERSION,
                                      frame_spec_to_version_mapper<
                                          convert_to_frame_spec_mapper<
                                              T_CONVERSION >( ) >( ) >::
                            compare_up_convert( source_obj, derived_obj );
                }
                catch ( ... )
                {
                    retval = false;
                }

                std::cerr << "DEBUG: conversion test status: " << retval << std::endl;
                return ( retval );
            }
        };

        template < int T_FRAME_SPEC_VERSION >
        bool
        verify_convert( )
        {
            return ( verify_convert_helper<
                     T_FRAME_SPEC_VERSION,
                     conversion<
                         T_FRAME_SPEC_VERSION,
                         is_root< T_FRAME_SPEC_VERSION >( ),
                         is_change_version< T_FRAME_SPEC_VERSION >( ) >( ) >::
                         check( ) );
        }

        //======================================
        // F U N C T I O N:   do_standard_tests
        //======================================
        template < int FrameSpecT >
        void
        do_standard_tests( )
        {
            BOOST_TEST_MESSAGE( "Checking basic correctness of frame structure "
                                "FrDetector at version "
                                << FrameSpecT );
            BOOST_CHECK( verify_data_types< FrameSpecT >( ) );
            BOOST_CHECK( verify_constructors< FrameSpecT >( ) );
            BOOST_CHECK( verify_convert< FrameSpecT >( ) );
        }
    } // namespace fr_detector
} // namespace testing

void
fr_detector( )
{
    testing::fr_detector::do_standard_tests< 3 >( );
    testing::fr_detector::do_standard_tests< 4 >( );
    testing::fr_detector::do_standard_tests< 6 >( );
    testing::fr_detector::do_standard_tests< 7 >( );
    testing::fr_detector::do_standard_tests< 8 >( );
}

#endif /* fr_detector_hh */
