// -*- mode: c++ -*-
//
// 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
//

/* -*- mode: C++; c-basic-offset: 2; -*- */
#ifndef FRAME_CPP__TEST__FR_STRUCT_4_TCC
#define FRAME_CPP__TEST__FR_STRUCT_4_TCC

#include "framecpp/Universal/FrameH.hh"
#include "framecpp/Universal/FrStatData.hh"

#define TEMPLATE_SPEC 4
#define NAMESPACE FrameCPP::Version_4
#define PREVIOUS_TEMPLATE_SPEC 3
#define PREVIOUS_NAMESPACE FrameCPP::Version_3

#undef CHECK_NUMBER_V1
#define CHECK_NUMBER_V1( P, C, A, N )                                          \
    BOOST_TEST_MESSAGE( Leader << "field: " #N );                              \
    BOOST_CHECK( ( INT_4U )( C->A( ) ) == P->A( ) )

//=======================================================================
// FrTable
//=======================================================================
template <>
mk_frame_object_ret_type
mk_frame_object< TEMPLATE_SPEC >( FrameObjectTypes Type )
{
    using FrameCPP::Common::FrameSpec;

    using FrameCPP::Universal::FrameH;

    using FrameCPP::Universal::FrAdcData;
    using FrTrigData = FrameCPP::Universal::FrEvent;
    using FrameCPP::Universal::FrProcData;
    using FrameCPP::Universal::FrSimData;
    using FrameCPP::Universal::FrSimEvent;
    using FrameCPP::Universal::FrStatData;
    using FrameCPP::Universal::FrSummary;

    mk_frame_object_ret_type retval;

    switch ( Type )
    {
    case FrameSpec::Info::FSI_FRAME_H:
        retval.reset(
            new FrameH( "frame_h", 1, 8, GPSTime( 10, 20 ), 3.0, 2 ) );
        break;
    case FrameSpec::Info::FSI_FR_ADC_DATA:
        retval.reset( new FrAdcData( "fr_adc_data",
                                     3,
                                     2,
                                     8,
                                     1024.,
                                     2.0,
                                     1.0,
                                     "meters",
                                     30.,
                                     10,
                                     20,
                                     4 ) );

        static_cast< FrAdcData* >( retval.get( ) )
            ->AppendComment( "test data" );
        break;
    case FrameSpec::Info::FSI_FR_DETECTOR:
    {
        using FrameCPP::Universal::FrDetector;

        static const char pre[ 2 ] = { 'a', 'b' };
        retval.reset( new FrDetector( "fr_detector_name",
                                      pre,
                                      1.1,
                                      2.2,
                                      200.3,
                                      3.3,
                                      4.4,
                                      5.5,
                                      6.6,
                                      7.7,
                                      8.8 ) );
    }
    break;
    case FrameSpec::Info::FSI_FR_HISTORY:
    {
        using FrameCPP::Universal::FrHistory;

        std::unique_ptr< FrHistory > fr_history(
            new FrHistory( "fr_history_name", 10, "fr_hsitory_comment" ) );
        retval.reset( fr_history.release( ) );
    }
    break;
    case FrameSpec::Info::FSI_FR_MSG:
    {
        using FrameCPP::Universal::FrMsg;

        std::unique_ptr< FrMsg > fr_msg( new FrMsg( "alarm", "message", 10 ) );
        retval.reset( fr_msg.release( ) );
    }
    break;
    case FrameSpec::Info::FSI_FR_PROC_DATA:
        //-------------------------------------------------------------------
        // FrProcData
        //-------------------------------------------------------------------
        retval.reset( new FrProcData( "fr_proc_data",
                                      "fr_proc_data_comment",
                                      1,
                                      0,
                                      16.0,
                                      2048.0,
                                      1024.0,
                                      4.0,
                                      4096.0,
                                      0 ) );

        break;
    case FrameSpec::Info::FSI_FR_RAW_DATA:
    {
        using FrameCPP::Universal::FrRawData;

        retval.reset( new FrRawData( "fr_raw_data" ) );
    }
    break;
    case FrameSpec::Info::FSI_FR_SER_DATA:
        //-------------------------------------------------------------------
        // FrSerData
        //-------------------------------------------------------------------
        using FrameCPP::Universal::FrSerData;

        retval.reset(
            new FrSerData( "fr_ser_data", GPSTime( 10, 20 ), 1024. ) );

        static_cast< FrSerData* >( retval.get( ) )->SetData( "test data" );
        break;
    case FrameSpec::Info::FSI_FR_SIM_DATA:
        //-------------------------------------------------------------------
        // FrSimData
        //-------------------------------------------------------------------
        retval.reset( new FrSimData(
            "fr_sim_data", "fr_sim_data_comment", 1024.0, 2048.0, 1024.0 ) );
        break;
    case FrameSpec::Info::FSI_FR_SIM_EVENT:
        //-------------------------------------------------------------------
        // FrSimEvent
        //-------------------------------------------------------------------
        {
            FrSimEvent::ParamList_type params;

            params.push_back( FrSimEvent::Param_type( "param1", 64.0 ) );

            retval.reset( new FrSimEvent( "fr_sim_event",
                                          "fr_sim_event_comment",
                                          "fr_sim_event_inputs",
                                          GPSTime( 10, 20000000 ),
                                          1024.0,
                                          2048.0,
                                          16.0,
                                          params ) );
        }
        break;
    case FrameSpec::Info::FSI_FR_STAT_DATA:
        //-------------------------------------------------------------------
        // FrStatData
        //-------------------------------------------------------------------
        retval.reset( new FrStatData( "fr_stat_data",
                                      "fr_stat_data_comment",
                                      "fr_stat_data_representation",
                                      1,
                                      2,
                                      4 ) );
        break;
    case FrameSpec::Info::FSI_FR_SUMMARY:
        //-------------------------------------------------------------------
        // FrSummary
        //-------------------------------------------------------------------
        retval.reset( new FrSummary( "fr_summary",
                                     "fr_summary_comment",
                                     "fr_summary_test",
                                     GPSTime( 10, 20 ) ) );

        break;
    case FrameSpec::Info::FSI_FR_TABLE:
    {
        using FrameCPP::Universal::FrTable;

        std::unique_ptr< FrTable > fr_table( new FrTable( "testTable", 0 ) );
        fr_table->AppendComment( "hello world" );
        retval.reset( fr_table.release( ) );
    }
    break;
    case FrameSpec::Info::FSI_FR_TRIG_DATA:
        //-------------------------------------------------------------------
        // FrTrigData
        //-------------------------------------------------------------------
        {
            FrTrigData::ParamList_type params;

            retval.reset( new FrTrigData( "fr_event",
                                          "fr_event_comment",
                                          "fr_event_inputs",
                                          GPSTime( 10, 20000000 ),
                                          1024.0,
                                          2048.0,
                                          1,
                                          16.0,
                                          32.0,
                                          "fr_event_statistics",
                                          params ) );
        }
        break;
    case FrameSpec::Info::FSI_FR_VECT:
    {
        using FrameCPP::Universal::FrVect;

        typedef REAL_8 vect_data_type;

        static const int            SAMPLES = 4;
        static const vect_data_type START = 16.0;
        static const vect_data_type INC = 0.5;

        FrVect::dimension_type dim( SAMPLES );
        vect_data_type         data[ SAMPLES ];

        vect_data_type cur_val = START;

        for ( int cur = 0; cur != SAMPLES; ++cur )
        {
            data[ cur ] = cur_val;
            cur_val += INC;
        }

        retval.reset( new FrVect( "fr_vect", 1, &dim, data, "fr_vect_unitY" ) );
    }
    break;
    default:
    {
        std::ostringstream msg;

        msg << "mk_frame_obj<" << TEMPLATE_SPEC
            << ">: Unsupported type: " << Type;
        throw FrameCPP::Unimplemented(
            msg.str( ), TEMPLATE_SPEC, __FILE__, __LINE__ );
    }
    break;
    }
    return retval;
}

//=======================================================================

template <>
void
verify_downconvert< TEMPLATE_SPEC >( frame_object_type  FrameObj,
                                     const std::string& Leader )
{
    using FrameCPP::Common::FrameSpec;

    if ( !FrameObj )
    {
        throw std::runtime_error( "NULL frame object" );
    }
    const FrameObjectTypes object_id =
        FrameObjectTypes( FrameObj->GetClass( ) );

    switch ( object_id )
    {
    default:
    {
        std::ostringstream msg;

        msg << "verify_downconvert<" << TEMPLATE_SPEC
            << ">: Unsupported type: " << object_id;
        throw FrameCPP::Unimplemented(
            msg.str( ), TEMPLATE_SPEC, __FILE__, __LINE__ );
    }
    break;
    }
}

template <>
void
verify_upconvert< TEMPLATE_SPEC >( frame_object_type  FrameObj,
                                   const std::string& Leader )
{
    using FrameCPP::Common::FrameSpec;

    if ( !FrameObj )
    {
        throw std::runtime_error( "NULL frame object" );
    }

    const FrameObjectTypes object_id =
        FrameObjectTypes( FrameObj->GetClass( ) );

    switch ( object_id )
    {
    default:
    {
        std::ostringstream msg;

        msg << "verify_upconvert<" << TEMPLATE_SPEC
            << ">: Unsupported type: " << object_id;
        throw FrameCPP::Unimplemented(
            msg.str( ), TEMPLATE_SPEC, __FILE__, __LINE__ );
    }
    break;
    }
}

//=======================================================================
#undef PREVIOUS_NAMESPACE
#undef PREVIOUS_TEMPLATE_SPEC
#undef NAMESPACE
#undef TEMPLATE_SPEC

#endif /* FRAME_CPP__TEST__FR_STRUCT_4_TCC */
