//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2018 California Institute of Technology
//
// LDASTools frameCPP is free software; you may redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools frameCPP is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.
//
// Neither the names of the California Institute of Technology (Caltech),
// The Massachusetts Institute of Technology (M.I.T), The Laser
// Interferometer Gravitational-Wave Observatory (LIGO), nor the names
// of its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// You should have received a copy of the licensing terms for this
// software included in the file LICENSE located in the top-level
// directory of this package. If you did not, you can view a copy at
// http://dcc.ligo.org/M1500244/LICENSE
//

#include <framecpp_config.h>

#include <cstdlib>

#include <boost/shared_ptr.hpp>

#include "framecpp/FrTOC.hh"
#include "framecpp/FrAdcData.hh"
#include "framecpp/FrProcData.hh"
#include "framecpp/FrSerData.hh"
#include "framecpp/FrSerData.hh"
#include "framecpp/FrSimData.hh"
#include "framecpp/FrSimEvent.hh"
#include "framecpp/FrVect.hh"

#include "framecppc/FrChan.h"
#include "framecppc/FrVect.h"

#include "FrameCInternal.hh"
#include "StreamInternal.hh"
#include "FrChanInternal.hh"

using FrameC::Set;
typedef FrameC::FrChannel::vector_type       vector_type;
typedef FrameC::FrChannel::const_vector_type const_vector_type;

typedef int promoted_fr_chan_data_valid_t;
typedef int promoted_fr_chan_channel_group_t;
typedef int promoted_fr_chan_channel_number_t;

#if defined( FR_PROC_DATA__MIME_TYPE )
#define FR_PROC_TYPE_MIME FR_PROC_TYPE_UNKNOWN
#define FR_PROC_SUB_TYPE_MIME FR_PROC_SUB_TYPE_UNKNOWN
#define FR_PROC_SUB_TYPE_MIME FR_PROC_SUB_TYPE_UNKNOWN
#define FR_PROC_DATA_TYPE_MIME FR_VECT_1U
#endif /* */

namespace
{
#if 0
  template< typename SourceType,
	    typename ReturnType,
	    ReturnType (SourceType::*Func)( ) const >
  static inline
  bool get_value( boost::shared_ptr< SourceType > Source,
		  ReturnType* Value )
  {
    if ( Source )
    {
      *Value = (Source.get( )->*Func)( );
      return true;
    }
    return false;
  }
#endif /* 0 */

#if 0
  template< typename SourceType,
	    typename ParamType,
	    void (SourceType::*Func)( ParamType ) >
  static inline
  bool set_value( boost::shared_ptr< SourceType > Source,
		  ParamType Value )
  {
    if ( Source )
    {
      (Source.get( )->*Func)( Value );
      return true;
    }
    return false;
  }
#endif /* 0 */

#if 0
  template< typename SourceType,
	    void (SourceType::*Func)( const std::string& ) >
  static inline
  bool set_value( boost::shared_ptr< SourceType > Source,
		  const char* Value )
  {
    if ( Source )
    {
      (Source.get( )->*Func)( Value );
      return true;
    }
    return false;
  }
#endif /* 0 */
} // namespace

namespace FrameC
{
    template <>
    pointer_type
    PointerType( FrChannel* Value )
    {
        return POINTER_FR_CHANNEL;
    }

    template <>
    const char*
    Pointers::Name< POINTER_FR_CHANNEL >( )
    {
        return "FrChannel";
    }

} // namespace FrameC

/**
 * \todo
 *    Currently the DataType and NData fields are not being used.
 *    Either they need to be eliminated or used to create
 *    an appropriate FrVect for data storage.
 */
fr_chan_t*
FrameCFrChanAlloc( FrameCError**        Error,
                   const char*          Name,
                   fr_chan_type         Type,
                   fr_vect_data_types_t DataType,
                   fr_vect_ndata_t      NData )
{
    Set( Error );

    FrameC::FrChannel*                   retval = (FrameC::FrChannel*)NULL;
    std::unique_ptr< FrameC::FrChannel > tmp;

    try
    {
        tmp.reset( new FrameC::FrChannel( Name, Type, DataType, NData ) );
        try
        {
            tmp->Vector( DataType, NData );
        }
        catch ( const std::exception& Exception )
        {
            tmp.reset( );
            Set(
                Error, FRAMEC_ERRNO_FR_CHANNEL_ALLOC_ERROR, Exception.what( ) );
        }
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_ALLOC_ERROR, Exception.what( ) );
        tmp.reset( (FrameC::FrChannel*)NULL );
    }

    if ( tmp.get( ) )
    {
        FrameC::Handle::Deposit( tmp.get( ) );
        retval = tmp.release( );
    }
    return ( reinterpret_cast< fr_chan_t* >( retval ) );
}

/**
 * \todo
 *    Currently the DataType and NData fields are not being used.
 *    Either they need to be eliminated or used to create
 *    an appropriate FrVect for data storage.
 */
fr_chan_t*
FrameCFrProcChanAlloc( FrameCError**        Error,
                       const char*          Name,
                       fr_proc_type         Type,
                       fr_proc_sub_type     SubType,
                       fr_vect_data_types_t DataType,
                       fr_vect_ndata_t      NData )
{
    Set( Error );

    FrameC::FrChannel*                   retval = (FrameC::FrChannel*)NULL;
    std::unique_ptr< FrameC::FrChannel > tmp;

    try
    {
        tmp.reset(
            new FrameC::FrChannel( Name, Type, SubType, DataType, NData ) );
        try
        {
            tmp->Vector( DataType, NData );
        }
        catch ( const std::exception& Exception )
        {
            tmp.reset( );
            Set(
                Error, FRAMEC_ERRNO_FR_CHANNEL_ALLOC_ERROR, Exception.what( ) );
        }
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_ALLOC_ERROR, Exception.what( ) );
        tmp.reset( (FrameC::FrChannel*)NULL );
    }

    if ( tmp.get( ) )
    {
        FrameC::Handle::Deposit( tmp.get( ) );
        retval = tmp.release( );
    }
    return ( reinterpret_cast< fr_chan_t* >( retval ) );

    return (fr_chan_t*)NULL;
}

/**
 * \todo
 *    Currently the DataType and NData fields are not being used.
 *    Either they need to be eliminated or used to create
 *    an appropriate FrVect for data storage.
 */
fr_chan_t*
FrameCFrProcMimeChanAlloc( FrameCError**   Error,
                           const char*     Name,
                           const char*     MimeTypeName,
                           fr_vect_ndata_t NData,
                           fr_vect_data_t  MimeData )
{
    Set( Error );

    FrameC::FrChannel*                   retval = (FrameC::FrChannel*)NULL;
    std::unique_ptr< FrameC::FrChannel > tmp;

    try
    {
        tmp.reset( new FrameC::FrChannel( Name,
                                          FR_PROC_TYPE_MIME,
                                          FR_PROC_SUB_TYPE_MIME,
                                          FR_PROC_DATA_TYPE_MIME,
                                          NData ) );
        try
        {
            tmp->Vector( FR_PROC_DATA_TYPE_MIME, NData );
            vector_type v( tmp->Vector( ) );

            v->MimeData( MimeTypeName, MimeData, NData );
        }
        catch ( const std::exception& Exception )
        {
            tmp.reset( );
            Set(
                Error, FRAMEC_ERRNO_FR_CHANNEL_ALLOC_ERROR, Exception.what( ) );
        }
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_ALLOC_ERROR, Exception.what( ) );
        tmp.reset( (FrameC::FrChannel*)NULL );
    }

    if ( tmp.get( ) )
    {
        FrameC::Handle::Deposit( tmp.get( ) );
        retval = tmp.release( );
    }
    return ( reinterpret_cast< fr_chan_t* >( retval ) );

    return (fr_chan_t*)NULL;
}

void
FrameCFrChanFree( FrameCError** Error, fr_chan_t* Channel )
{
    FrameC::Handle::Free( Error,
                          Channel,
                          FrameC::POINTER_FR_CHANNEL,
                          FRAMEC_ERRNO_FR_CHANNEL_FREE_ERROR );
}

/**
 * Retrieve information about the channel structure.
 * The Option parameter dictates the number and type of
 * parameters to follow.
 * Multiple pieces of information can be retrieved by having
 * multiple Option/parameter sets.
 * The last value for Option must be FR_CHAN_FIELD_LAST to indicate
 * the end of the variable length argument list.
 */
void
FrameCFrChanQuery( FrameCError**    Error,
                   const fr_chan_t* Channel,
                   int              Option,
                   ... )
{
    using FrameCPP::FrAdcData;
    using FrameCPP::FrEvent;
    using FrameCPP::FrProcData;
    using FrameCPP::FrSimData;
    using FrameCPP::FrSimEvent;

    Set( Error );

    FrameC::FrChannel* oochannel( (FrameC::FrChannel*)Channel );

    try
    {
        va_list ap;
        va_start( ap, Option );

        if ( oochannel )
        {
            FrameC::FrChannel::channel_t::fr_adc_data  adc;
            FrameC::FrChannel::channel_t::fr_event     event;
            FrameC::FrChannel::channel_t::fr_proc_data proc;
            FrameC::FrChannel::channel_t::fr_sim_data  sim;
            FrameC::FrChannel::channel_t::fr_sim_event sim_event;

            switch ( oochannel->Type( ) )
            {
            case FR_ADC_CHAN_TYPE:
                adc = oochannel->m_channel.adc;
                break;
            case FR_EVENT_CHAN_TYPE:
                event = oochannel->m_channel.event;
                break;
            case FR_PROC_CHAN_TYPE:
                proc = oochannel->m_channel.proc;
                break;
            case FR_SIM_CHAN_TYPE:
                sim = oochannel->m_channel.sim;
                break;
            case FR_SIM_EVENT_CHAN_TYPE:
                sim_event = oochannel->m_channel.sim_event;
                break;
            default:
                break;
            }

            while ( Option != FR_CHAN_FIELD_LAST )
            {
                /** The following is a description of each value of Option and
                 * the parameters it takes.
                 *
                 * <ul>
                 *   <li> <b>FR_CHAN_FIELD_LAST</b>
                 *      <p> This is the last option in the list and specifies
                 *          the end of the query.
                 *      </p>
                 *   </li>
                 */
                switch ( Option )
                {
                case FR_CHAN_FIELD_COMMENT:
                    /**   <li> <b>FR_CHAN_FIELD_COMMENT</b>
                     *      <p>
                     *        Retrieve the comment associated with the channel.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address of storage
                     *          of type <b>fr_chan_comment_t</b>.
                     *          The comment associated with the channel
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_chan_comment_t data_t;
                        data_t*                   data;
                        static data_t             empty_comment = "";

                        data = va_arg( ap, data_t* );
                        *data = empty_comment;

                        if ( adc )
                        {
                            *data = adc->GetComment( ).c_str( );
                        }
                        else if ( event )
                        {
                            *data = event->GetComment( ).c_str( );
                        }
                        else if ( proc )
                        {
                            *data = proc->GetComment( ).c_str( );
                        }
                        else if ( sim )
                        {
                            *data = sim->GetComment( ).c_str( );
                        }
                        else if ( sim_event )
                        {
                            *data = sim_event->GetComment( ).c_str( );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support querying of this parameter.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_CHANNEL_GROUP:
                    /**   <li> <b>FR_CHAN_FIELD_CHANNEL_GROUP</b>
                     *      <p>
                     *        Retrieve the numeric group identifier associated
                     *with the channel. A single argument should follow this
                     *option. The argument needs to be the address of storage of
                     *type <b>fr_chan_channel_group_t</b>. The name of the
                     *channel will be stored here. Available for:
                     *          FR_ADC_CHAN_TYPE,
                     *   </li>
                     */
                    {
                        typedef fr_chan_channel_group_t data_t;
                        data_t*                         data;

                        data = va_arg( ap, data_t* );
                        if ( adc )
                        {
                            *data = adc->GetChannelGroup( );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support setting of sample rate.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_CHANNEL_NUMBER:
                    /**   <li> <b>FR_CHAN_FIELD_CHANNEL_NUMBER</b>
                     *      <p>
                     *        Retrieve the numeric identifier associated with
                     *the channel. A single argument should follow this option.
                     *          The argument needs to be the address of storage
                     *          of type <b>fr_chan_channel_number_t</b>.
                     *          The name of the channel
                     *          will be stored here.
                     *	    Available for:
                     *          FR_ADC_CHAN_TYPE,
                     *   </li>
                     */
                    {
                        typedef fr_chan_channel_group_t data_t;
                        data_t*                         data;

                        data = va_arg( ap, data_t* );
                        if ( adc )
                        {
                            *data = adc->GetChannelNumber( );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support a channel number
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_NAME:
                    /**   <li> <b>FR_CHAN_FIELD_NAME</b>
                     *      <p>
                     *        Retrieve the name associated with the channel.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address of storage
                     *          of type <b>fr_chan_name_t</b>.
                     *          The name of the channel
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_chan_name_t* data;

                        data = va_arg( ap, fr_chan_name_t* );
                        if ( adc )
                        {
                            *data = adc->GetName( ).c_str( );
                        }
                        else if ( event )
                        {
                            *data = event->GetName( ).c_str( );
                        }
                        else if ( proc )
                        {
                            *data = proc->GetName( ).c_str( );
                        }
                        else if ( sim )
                        {
                            *data = sim->GetName( ).c_str( );
                        }
                        else if ( sim_event )
                        {
                            *data = sim_event->GetName( ).c_str( );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support a channel name.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_SAMPLE_RATE:
                    /**   <li> <b>FR_CHAN_FIELD_SAMPLE_RATE</b>
                     *      <p>
                     *        Retrieve the sample rate associated with the
                     * channel. A single argument should follow this option. The
                     * argument needs to be the address of storage of type
                     * <b>fr_chan_sample_rate_t</b>. The name of the channel
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_chan_sample_rate_t data_t;
                        data_t*                       data;

                        data = va_arg( ap, data_t* );

                        if ( adc )
                        {
                            *data = adc->GetSampleRate( );
                        }
                        else if ( sim )
                        {
                            *data = sim->GetSampleRate( );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support retrieval of the sample rate.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_TIME_OFFSET:
                    /**   <li> <b>FR_CHAN_FIELD_TIME_OFFSET</b>
                     *      <p>
                     *        Retrieve the time offset associated with the
                     * channel. A single argument should follow this option. The
                     * argument needs to be the address of storage of type
                     * <b>fr_chan_time_offset_t</b>. The name of the channel
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_chan_time_offset_t data_t;
                        data_t*                       data;

                        data = va_arg( ap, data_t* );

                        if ( adc )
                        {
                            *data = adc->GetTimeOffset( );
                        }
                        else if ( proc )
                        {
                            *data = proc->GetTimeOffset( );
                        }
                        else if ( sim )
                        {
                            *data = sim->GetTimeOffset( );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support retrieving the time offset.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                default:
                    /**************************************************************
                      An unknown option has been specified. Terminate the
                    request.
                    ***************************************************************/
                    Option = FR_CHAN_FIELD_LAST;
                    continue;
                }
                /**
                 * </ul>
                 */
                int next_opt = va_arg( ap, int /* fr_vect_fields*/ );
                Option = (fr_chan_fields)next_opt;
            }
        }
        else
        {
            Set( Error,
                 FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR,
                 "NULL reference to channel" );
        }
        va_end( ap );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR, Exception.what( ) );
    }
    catch ( ... )
    {
        Set( Error,
             FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR,
             FrameC::UnknownError );
    }
}

fr_chan_t*
FrameCFrChanRead( FrameCError** Error,
                  fr_file_t* restrict Stream,
                  fr_chan_name_t restrict Name,
                  fr_chan_offset_t        Pos )
{
    Set( Error );

    std::unique_ptr< FrameC::FrChannel > channel;

    try
    {
        channel.reset( new FrameC::FrChannel );
        channel->Read( Stream, Name, Pos );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_READ_ERROR, Exception.what( ) );
        channel.reset( NULL );
    }
    catch ( ... )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_READ_ERROR, FrameC::UnknownError );
        channel.reset( NULL );
    }

    FrameC::Handle::Deposit( channel.get( ) );
    return reinterpret_cast< fr_chan_t* >( channel.release( ) );
}

/**
 * Establish values for the given channel
 * The Option parameter dictates the number and type of
 * parameters to follow.
 * Multiple pieces of information can be set by having
 * multiple Option/parameter sets.
 * The last value for Option must be FR_FIELD_LAST to indicate
 * the end of the variable length argument list.
 */
void
FrameCFrChanSet( FrameCError**    Error,
                 const fr_chan_t* Channel,
                 int              Option,
                 ... )
{
    using FrameCPP::FrAdcData;
    using FrameCPP::FrEvent;
    using FrameCPP::FrProcData;
    using FrameCPP::FrSimData;
    using FrameCPP::FrSimEvent;

    Set( Error );

    FrameC::FrChannel* oochannel( (FrameC::FrChannel*)Channel );

    try
    {
        va_list ap;
        va_start( ap, Option );

        if ( oochannel )
        {
            FrameC::FrChannel::channel_t::fr_adc_data  adc;
            FrameC::FrChannel::channel_t::fr_event     event;
            FrameC::FrChannel::channel_t::fr_proc_data proc;
            FrameC::FrChannel::channel_t::fr_sim_data  sim;
            FrameC::FrChannel::channel_t::fr_sim_event sim_event;

            switch ( oochannel->Type( ) )
            {
            case FR_ADC_CHAN_TYPE:
                adc = oochannel->m_channel.adc;
                break;
            case FR_EVENT_CHAN_TYPE:
                event = oochannel->m_channel.event;
                break;
            case FR_PROC_CHAN_TYPE:
                proc = oochannel->m_channel.proc;
                break;
            case FR_SIM_CHAN_TYPE:
                sim = oochannel->m_channel.sim;
                break;
            case FR_SIM_EVENT_CHAN_TYPE:
                sim_event = oochannel->m_channel.sim_event;
                break;
            default:
                break;
            }

            while ( Option != FR_CHAN_FIELD_LAST )
            {
                /** The following is a description of each value of Option and
                 * the parameters it takes.
                 *
                 * <ul>
                 *   <li> <b>FR_VECT_FIELD_LAST</b>
                 *      <p> This is the last option in the list and specifies
                 *          the end of the query.
                 *      </p>
                 *   </li>
                 */
                switch ( Option )
                {
                case FR_CHAN_FIELD_CHANNEL_GROUP:
                    /**   <li> <b>FR_CHAN_FIELD_CHANNEL_GROUP</b>
                     *      <p>
                     *        Set the numeric group identifier associated with
                     *the channel. A single argument should follow this option.
                     *          The argument needs to be of storage
                     *          type <b>fr_chan_channel_group_t</b>.
                     *          and contain the group identifier.
                     *	    Available for:
                     *          FR_ADC_CHAN_TYPE,
                     *   </li>
                     */
                    {
                        typedef fr_chan_channel_group_t data_t;
                        data_t                          data;

                        data = va_arg( ap, data_t );
                        if ( adc )
                        {
                            adc->SetChannelGroup( data );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support setting of the channel group value.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_CHANNEL_NUMBER:
                    /**   <li> <b>FR_CHAN_FIELD_CHANNEL_NUMBER</b>
                     *      <p>
                     *        Set the numeric identifier associated with the
                     *channel. A single argument should follow this option. The
                     *argument needs to be the address of storage of type
                     *<b>fr_chan_channel_number_t</b>. The name of the channel
                     *          will be stored here.
                     *	    Available for:
                     *          FR_ADC_CHAN_TYPE,
                     *   </li>
                     */
                    {
                        typedef fr_chan_channel_number_t data_t;
                        data_t                           data;

                        data = va_arg( ap, data_t );
                        if ( adc )
                        {
                            adc->SetChannelNumber( data );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support setting the channel number.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_COMMENT:
                    /**   <li> <b>FR_CHAN_FIELD_COMMENT</b>
                     *      <p>
                     *        Set the
                     *        comment
                     *        for the specified channel.
                     * 	    A single argument should follow this option.
                     *        The argument needs to be of type
                     *        fr_chan_comment_t
                     *        and initialized with the
                     *        comment
                     *        value.
                     *	    Available for:
                     *          FR_ADC_CHAN_TYPE,
                     *          FR_PROC_CHAN_TYPE,
                     *          FR_SIM_CHAN_TYPE,
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_chan_comment_t data_t;
                        data_t                    data;

                        data = va_arg( ap, data_t );

                        if ( adc )
                        {
                            adc->SetComment( data );
                        }
                        else if ( proc )
                        {
                            proc->SetComment( data );
                        }
                        else if ( sim )
                        {
                            sim->SetComment( data );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support setting of channel comments.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_DATA_VALID:
                    /**   <li> <b>FR_CHAN_FIELD_DATA_VALID</b>
                     *      <p>
                     *        Set the
                     *        validity of the data
                     *        for the specified channel.
                     * 	    A single argument should follow this option.
                     *        The argument needs to be of type
                     *        fr_chan_data_valid_t
                     *        and initialized with the
                     *        data valid flag
                     *        value.
                     *	    Available for:
                     *          FR_ADC_CHAN_TYPE,
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef promoted_fr_chan_data_valid_t data_t;

                        data_t data;

                        data = va_arg( ap, data_t );

                        if ( adc )
                        {
                            adc->SetDataValid( data );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support setting data validity.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_N_BITS:
                    /**   <li> <b>FR_CHAN_FIELD_N_BITS</b>
                     *      <p>
                     *        Set the
                     *        number of bits
                     *        for the specified channel.
                     * 	    A single argument should follow this option.
                     *        The argument needs to be of type
                     *        fr_chan_n_bits_t
                     *        and initialized with the
                     *        number of bits.
                     *	    Available for:
                     *          FR_ADC_CHAN_TYPE,
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_chan_n_bits_t data_t;

                        data_t data;

                        data = va_arg( ap, data_t );

                        if ( adc )
                        {
                            adc->SetNBits( data );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support setting of number of bits.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_SAMPLE_RATE:
                    /**   <li> <b>FR_CHAN_FIELD_SAMPLE_RATE</b>
                     *      <p> Set the sample rate for the specified channel.
                     * 	      A single argument should follow this option.
                     *          The argument needs to be of type
                     *          fr_chan_sample_rate_t
                     *          and initialized with the
                     *          sample rate
                     *          value.
                     *	      Available for:
                     *            FR_ADC_CHAN_TYPE,
                     *            FR_SIM_CHAN_TYPE
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_chan_sample_rate_t data_t;
                        data_t                        data;

                        data = va_arg( ap, fr_chan_sample_rate_t );

                        if ( adc )
                        {
                            adc->SetSampleRate( data );
                        }
                        else if ( sim )
                        {
                            sim->SetSampleRate( data );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support setting of sample rate.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_TIME_OFFSET:
                    /**   <li> <b>FR_CHAN_FIELD_TIME_OFFSET</b>
                     *      <p>
                     *	    Set the time offset of the 1st sample relative
                     *        to the frame start time (seconds).
                     *        The value must be positive and smaller than the
                     *        frame length.
                     *	    Available for:
                     *          FR_ADC_CHAN_TYPE,
                     *          FR_PROC_CHAN_TYPE,
                     *          FR_SIM_CHAN_TYPE
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_chan_time_offset_t data_t;

                        data_t data;

                        data = va_arg( ap, fr_chan_time_offset_t );

                        if ( adc )
                        {
                            adc->SetTimeOffset( data );
                        }
                        else if ( proc )
                        {
                            proc->SetTimeOffset( data );
                        }
                        else if ( sim )
                        {
                            sim->SetTimeOffset( data );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support setting of the time offset field.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                case FR_CHAN_FIELD_T_RANGE:
                    /**   <li> <b>FR_CHAN_FIELD_T_RANGE</b>
                     *      <p>
                     *        Set the
                     *        time range
                     *        for the specified channel.
                     * 	    A single argument should follow this option.
                     *        The argument needs to be of type
                     *        fr_chan_t_range_t
                     *        and initialized with the
                     *        time range.
                     *	    Available for:
                     *          FR_PROC_CHAN_TYPE,
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_chan_t_range_t data_t;

                        data_t data;

                        data = va_arg( ap, data_t );

                        if ( proc )
                        {
                            proc->SetTRange( data );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            /// \todo
                            /// Inform user that the specified channel does not
                            /// support setting of the TRange field.
                            //---------------------------------------------------------
                        }
                    }
                    break;
                default:
                    /**************************************************************
                      An unknown option has been specified. Terminate the
                    request.
                    ***************************************************************/
                    Option = FR_CHAN_FIELD_LAST;
                    continue;
                }
                /**
                 * </ul>
                 */
                int next_opt = va_arg( ap, int /* fr_chan_fields*/ );
                Option = (fr_chan_fields)next_opt;
            }
        }
        else
        {
            Set( Error,
                 FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR,
                 "NULL reference to channel" );
        }
        va_end( ap );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR, Exception.what( ) );
    }
    catch ( ... )
    {
        Set( Error,
             FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR,
             FrameC::UnknownError );
    }
}

void
FrameCFrChanVectorAlloc( FrameCError**        Error,
                         fr_chan_t*           Channel,
                         fr_vect_data_types_t DataType,
                         fr_vect_ndata_t      NData )
{
    Set( Error );

    FrameC::FrChannel* oochannel( (FrameC::FrChannel*)Channel );

    try
    {
        oochannel->Vector( DataType, NData );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_ALLOC_ERROR, Exception.what( ) );
    }
}

/**
 * Expand the data located in the data vector.
 */
void
FrameCFrChanVectorCompress( FrameCError**               Error,
                            fr_chan_t*                  Channel,
                            fr_vect_compression_schemes Scheme )
{
    Set( Error );

    FrameC::FrChannel* oochannel( (FrameC::FrChannel*)Channel );

    try
    {
        vector_type v( oochannel->Vector( ) );

        v->Compress( ::FrameC::FrChannel::CompressionScheme( Scheme ),
                     ::FrameCPP::FrVect::DEFAULT_GZIP_LEVEL );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error,
             FRAMEC_ERRNO_FR_CHANNEL_COMPRESSION_ERROR,
             Exception.what( ) );
    }
}

/**
 * Expand the data located in the data vector.
 */
void
FrameCFrChanVectorExpand( FrameCError** Error, fr_chan_t* Channel )
{
    Set( Error );

    FrameC::FrChannel* oochannel( (FrameC::FrChannel*)Channel );

    try
    {
        vector_type v( oochannel->Vector( ) );

        v->Uncompress( );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error,
             FRAMEC_ERRNO_FR_CHANNEL_COMPRESSION_ERROR,
             Exception.what( ) );
    }
}

/**
 * Retrieve information about the FrVector structure
 * associated with the channel.
 * The Option parameter dictates the number and type of
 * parameters to follow.
 * Multiple pieces of information can be retrieved by having
 * multiple Option/parameter sets.
 * The last value for Option must be FR_FIELD_LAST to indicate
 * the end of the variable length argument list.
 */
void
FrameCFrChanVectorQuery( FrameCError**    Error,
                         const fr_chan_t* Channel,
                         int              Option,
                         ... )
{
    Set( Error );

    FrameC::FrChannel* oochannel( (FrameC::FrChannel*)Channel );

    try
    {
        va_list ap;
        va_start( ap, Option );

        if ( oochannel )
        {
            const_vector_type v( oochannel->Vector( ) );

            if ( !v )
            {
                throw std::runtime_error(
                    "No data vector associated with the channel" );
            }
            while ( Option != FR_VECT_FIELD_LAST )
            {
                /** The following is a description of each value of Option and
                 * the parameters it takes.
                 *
                 * <ul>
                 *   <li> <b>FR_VECT_FIELD_LAST</b>
                 *      <p> This is the last option in the list and specifies
                 *          the end of the query.
                 *      </p>
                 *   </li>
                 */
                switch ( Option )
                {
                case FR_VECT_FIELD_COMPRESS:
                    /**   <li> <b>FR_VECT_FIELD_COMPRESS</b>
                     *      <p> Retrieve the compression method used on the
                     * data. A single argument should follow this option. The
                     * argument needs to be the address of storage of type
                     * <b>fr_vect_compress_t</b>. The compression method used on
                     * the data of the vector will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_compress_t* c;

                        c = va_arg( ap, fr_vect_compress_t* );
                        *c = FrameC::FrChannel::CompressionScheme(
                            v->GetCompress( ) );
                    }
                    break;
                case FR_VECT_FIELD_DATA:
                    /**   <li> <b>FR_VECT_FIELD_DATA</b>
                     *      <p> Retrieve the data associated with the vector.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address of storage
                     *          of type <b>fr_vect_data_t</b>.
                     *          This data is read-only.
                     *      </p>
                     *   </li>
                     */
                    {
                        vector_type vm( oochannel->Vector( ) );

                        fr_vect_data_t* data;

                        data = va_arg( ap, fr_vect_data_t* );
                        /* Need to do the cast to change CHAR_U to CHAR */
                        *data = reinterpret_cast< fr_vect_data_t >(
                            vm->GetDataRaw( ).get( ) );
                    }
                    break;
                case FR_VECT_FIELD_DX:
                    /**
                     *   <li> <b>FR_VECT_FIELD_DX</b>
                     *      <p> Retrieve the sample length along each
                     * coordinate. Two arguments should follow this option. The
                     * first argument needs to be a pointer of type
                     * <b>fr_vect_dx_t</b> and must be sufficiently large to
                     * hold fr_vect_ndim_t elements. The sample length of each
                     * dimension will be stored here. The second arguemnt needs
                     * to be of type <b>fr_vect_ndim_t</b> and contains the
                     * maximum number of elements that can be stored in previous
                     * argument.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_dx_t*  dx;
                        fr_vect_ndim_t dim;
                        fr_vect_ndim_t dim_limit = v->GetNDim( );
                        fr_vect_ndim_t dim_cur;

                        dx = va_arg( ap, fr_vect_dx_t* );
                        dim = va_arg( ap, fr_vect_ndim_t );

                        for ( dim_cur = 0;
                              ( dim_cur < dim_limit ) && ( dim_cur < dim );
                              ++dim_cur )
                        {
                            dx[ dim_cur ] = v->GetDim( dim_cur ).GetDx( );
                        }
                    }
                    break;
                case FR_VECT_FIELD_NAME:
                    /**   <li> <b>FR_VECT_FIELD_NAME</b>
                     *      <p> Retrieve the name associated with the vector.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address of storage
                     *          of type <b>fr_vect_name_t</b>.
                     *          This data is read-only.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_name_t* name;

                        name = va_arg( ap, fr_vect_name_t* );
                        *name = ::strdup( v->GetName( ).c_str( ) );
                    }
                    break;
                case FR_VECT_FIELD_NBYTES:
                    /**   <li> <b>FR_VECT_FIELD_NBYTES</b>
                     *      <p> Retrieve the number of bytes in the compressed
                     * vector. A single argument should follow this option. The
                     * argument needs to be the address of storage of type
                     * <b>fr_vect_nbytes_t</b>. The number of byrtes in the
                     * compressed vector will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_nbytes_t* nbytes;

                        nbytes = va_arg( ap, fr_vect_nbytes_t* );
                        *nbytes = v->GetNBytes( );
                    }
                    break;
                case FR_VECT_FIELD_NDATA:
                    /**   <li> <b>FR_VECT_FIELD_NDATA</b>
                     *      <p> Retrieve the number of sample elements in the
                     * data series. A single argument should follow this option.
                     *          The argument needs to be the address of storage
                     *          of type <b>fr_vect_ndata_t</b>.
                     *          The number of sample elements in the data series
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_ndata_t* ndata;

                        ndata = va_arg( ap, fr_vect_ndata_t* );
                        *ndata = v->GetNData( );
                    }
                    break;
                case FR_VECT_FIELD_NDIM:
                    /**   <li> <b>FR_VECT_FIELD_NDIM</b>
                     *      <p> Retrieve the number of dimensions for the
                     * vector. A single argument should follow this option. The
                     * argument needs to be the address of storage of type
                     * <b>fr_vect_ndim_t</b>. The number of dimensions for the
                     * vector will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_ndim_t* dim;

                        dim = va_arg( ap, fr_vect_ndim_t* );
                        *dim = v->GetNDim( );
                    }
                    break;
                case FR_VECT_FIELD_NX:
                    /**
                     *   <li> <b>FR_VECT_FIELD_NX</b>
                     *      <p> Retrieve the dimension lengths.
                     * 	    Two arguments should follow this option.
                     *          The first argument needs to be a pointer
                     *          of type <b>fr_vect_nx_t</b> and must be
                     *          sufficiently large to hold fr_vect_ndim_t
                     * elements. The length of each dimension will be stored
                     * here. The second arguemnt needs to be of type
                     * <b>fr_vect_ndim_t</b> and contains the maximum number of
                     * elements that can be stored in previous argument.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_nx_t*  nx;
                        fr_vect_ndim_t dim;
                        fr_vect_ndim_t dim_limit = v->GetNDim( );
                        fr_vect_ndim_t dim_cur;

                        nx = va_arg( ap, fr_vect_nx_t* );
                        dim = va_arg( ap, fr_vect_ndim_t );

                        for ( dim_cur = 0;
                              ( dim_cur < dim_limit ) && ( dim_cur < dim );
                              ++dim_cur )
                        {
                            nx[ dim_cur ] = v->GetDim( dim_cur ).GetNx( );
                        }
                    }
                    break;
                case FR_VECT_FIELD_START_X:
                    /**
                     *   <li> <b>FR_VECT_FIELD_START_X</b>
                     *      <p> Retrieve the origin for each data set.
                     * 	    Two arguments should follow this option.
                     *          The first argument needs to be a pointer
                     *          of type <b>fr_vect_start_x_t</b> and must be
                     *          sufficiently large to hold fr_vect_ndim_t
                     * elements. The start of each data set will be stored here.
                     *          The second arguemnt needs to be
                     *          of type <b>fr_vect_ndim_t</b>
                     *          and contains the maximum number of elements
                     *          that can be stored in previous argument.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_startx_t* startx;
                        fr_vect_ndim_t    dim;
                        fr_vect_ndim_t    dim_cur;
                        fr_vect_ndim_t    dim_limit = v->GetNDim( );

                        startx = va_arg( ap, fr_vect_startx_t* );
                        dim = va_arg( ap, fr_vect_ndim_t );

                        for ( dim_cur = 0;
                              ( dim_cur < dim_limit ) && ( dim_cur < dim );
                              ++dim_cur )
                        {
                            startx[ dim_cur ] =
                                v->GetDim( dim_cur ).GetStartX( );
                        }
                    }
                    break;
                case FR_VECT_FIELD_TYPE:
                    /**   <li> <b>FR_VECT_FIELD_TYPE</b>
                     *      <p> Retrieve the vector class.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address of storage
                     *          of type <b>fr_vect_type_t</b>.
                     *          The vector class
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_type_t* t;

                        t = va_arg( ap, fr_vect_type_t* );
                        *t = v->GetType( );
                    }
                    break;
                case FR_VECT_FIELD_UNIT_X:
                    /**
                     *   <li> <b>FR_VECT_FIELD_UNIT_X</b>
                     *      <p> Retrieve the descriptions of each element.
                     * 	    Two arguments should follow this option.
                     *          The first argument needs to be a pointer
                     *          of type <b>fr_vect_unit_x_t</b> and must be
                     *          sufficiently large to hold fr_vect_ndim_t
                     * elements. The description of each element. will be stored
                     * here. The second arguemnt needs to be of type
                     * <b>fr_vect_ndim_t</b> and contains the maximum number of
                     * elements that can be stored in previous argument.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_unit_x_t* unit_x;
                        fr_vect_ndim_t    dim;
                        fr_vect_ndim_t    dim_limit = v->GetNDim( );
                        fr_vect_ndim_t    dim_cur;

                        unit_x = va_arg( ap, fr_vect_unit_x_t* );
                        dim = va_arg( ap, fr_vect_ndim_t );

                        for ( dim_cur = 0;
                              ( dim_cur < dim_limit ) && ( dim_cur < dim );
                              ++dim_cur )
                        {
                            unit_x[ dim_cur ] =
                                v->GetDim( dim_cur ).GetUnitX( ).c_str( );
                        }
                    }
                    break;
                case FR_VECT_FIELD_UNIT_Y:
                    /**   <li> <b>FR_VECT_FIELD_UNIT_Y</b>
                     *      <p> Retrieve the unit_y associated with the vector.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address of storage
                     *          of type <b>fr_vect_unit_y_t</b>.
                     *          This data is read-only.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_unit_y_t* unit_y;

                        unit_y = va_arg( ap, fr_vect_unit_y_t* );
                        *unit_y = v->GetUnitY( ).c_str( );
                    }
                    break;
                default:
                    /**************************************************************
                      An unknown option has been specified. Terminate the
                    request.
                    ***************************************************************/
                    Option = FR_VECT_FIELD_LAST;
                    continue;
                }
                /**
                 * </ul>
                 */
                int next_opt = va_arg( ap, int /* fr_vect_fields*/ );
                Option = (fr_vect_fields)next_opt;
            }
        }
        else
        {
            Set( Error,
                 FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR,
                 "NULL reference to channel" );
        }
        va_end( ap );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR, Exception.what( ) );
    }
    catch ( ... )
    {
        Set( Error,
             FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR,
             FrameC::UnknownError );
    }
}

/**
 * Retrieve information about the FrVector structure
 * associated with the channel.
 * The Option parameter dictates the number and type of
 * parameters to follow.
 * Multiple pieces of information can be retrieved by having
 * multiple Option/parameter sets.
 * The last value for Option must be FR_FIELD_LAST to indicate
 * the end of the variable length argument list.
 */
void
FrameCFrChanVectorSet( FrameCError** Error,
                       fr_chan_t*    Channel,
                       int           Option,
                       ... )
{
    using FrameCPP::FrVect;

    Set( Error );

    FrameC::FrChannel* oochannel( (FrameC::FrChannel*)Channel );

    try
    {
        va_list ap;
        va_start( ap, Option );

        fr_vect_fields opt = fr_vect_fields( Option );

        if ( oochannel )
        {
            vector_type v( oochannel->Vector( ) );

            while ( opt != FR_VECT_FIELD_LAST )
            {
                /** The following is a description of each value of Option and
                 * the parameters it takes.
                 *
                 * <ul>
                 *   <li> <b>FR_VECT_FIELD_LAST</b>
                 *      <p> This is the last option in the list and specifies
                 *          the end of the query.
                 *      </p>
                 *   </li>
                 */
                switch ( opt )
                {
                case FR_VECT_FIELD_DATA:
                    /**
                     *   <li> <b>FR_VECT_FIELD_DATA</b>
                     *      <p> Set the data values of the vector.
                     * 	    Three arguments should follow this option.
                     *          The first argument needs to be
                     *          of type <b>fr_vect_data_t</b>.
                     *          The second argument needs to be
                     *          of type <b>int</b> casting of
                     * <b>fr_vect_data_types_t</b> because of argument promotion
                     * rules and contains the type appropriate for the data
                     * referenced by the first argument. The third argument
                     * needs to be of type <b>fr_ndata_t</b> and contain the
                     * number of data elements.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_data_t       data = (fr_vect_data_t)NULL;
                        fr_vect_data_types_t dtype = FR_VECT_C;
                        fr_vect_ndata_t      ndata = 0;

                        data = va_arg( ap, fr_vect_data_t );

                        /*-----------------------------------------------------------
                         * :NOTE: This is done because of promotion
                         *-----------------------------------------------------------*/
                        dtype = (fr_vect_data_types_t)va_arg( ap, int );
                        ndata = va_arg( ap, fr_vect_ndata_t );

                        if ( ( v->GetType( ) != dtype ) ||
                             ( v->GetNData( ) != ndata ) ||
                             ( v->GetCompress( ) != FrVect::RAW ) )
                        {
                            /** \todo
                             *   Produce error message because the data type or
                             * number of data elements is not consistant.
                             */
                            break;
                        }
                        /*
                         * Everything checks out so copy the data.
                         */

                        std::copy( data,
                                   &data[ v->GetNBytes( ) ],
                                   v->GetData( ).get( ) );
                    }
                    break;
                case FR_VECT_FIELD_DX:
                    /**
                     *   <li> <b>FR_VECT_FIELD_DX</b>
                     *      <p> Set the sample length along each coordinate.
                     * 	    Two arguments should follow this option.
                     *          The first argument needs to be a pointer
                     *          of type <b>fr_vect_dx_t</b> and must be
                     *          sufficiently large to hold fr_vect_ndim_t
                     * elements. The sample length of each dimension is stored
                     * here. The second arguemnt needs to be of type
                     * <b>fr_vect_ndim_t</b> and contains the maximum number of
                     * elements that can be stored in previous argument.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_dx_t*  dx;
                        fr_vect_ndim_t dim;
                        fr_vect_ndim_t dim_limit = v->GetNDim( );
                        fr_vect_ndim_t dim_cur;

                        dx = va_arg( ap, fr_vect_dx_t* );
                        dim = va_arg( ap, fr_vect_ndim_t );

                        for ( dim_cur = 0;
                              ( dim_cur < dim_limit ) && ( dim_cur < dim );
                              ++dim_cur )
                        {
                            v->GetDim( dim_cur ).SetDx( dx[ dim_cur ] );
                        }
                    }
                    break;
                case FR_VECT_FIELD_NAME:
                    /**   <li> <b>FR_VECT_FIELD_NAME</b>
                     *      <p> Set the name associated with the vector.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be
                     *          of type <b>fr_vect_name_t</b>.
                     *          This data is read-only.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_name_t name;

                        name = va_arg( ap, fr_vect_name_t );
                        v->SetName( name );
                    }
                    break;
                case FR_VECT_FIELD_NX:
                    /**
                     *   <li> <b>FR_VECT_FIELD_NX</b>
                     *      <p> Set the dimension lengths along each coordinate.
                     * 	    Two arguments should follow this option.
                     *          The first argument needs to be a pointer
                     *          of type <b>fr_vect_nx_t</b> and must be
                     *          sufficiently large to hold fr_vect_ndim_t
                     * elements. The sample length of each dimension is stored
                     * here. The second arguemnt needs to be of type
                     * <b>fr_vect_ndim_t</b> and contains the maximum number of
                     * elements that can be stored in previous argument.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_nx_t*  nx;
                        fr_vect_ndim_t dim;
                        fr_vect_ndim_t dim_limit = v->GetNDim( );
                        fr_vect_ndim_t dim_cur;

                        nx = va_arg( ap, fr_vect_nx_t* );
                        dim = va_arg( ap, fr_vect_ndim_t );

                        for ( dim_cur = 0;
                              ( dim_cur < dim_limit ) && ( dim_cur < dim );
                              ++dim_cur )
                        {
                            v->GetDim( dim_cur ).SetNx( nx[ dim_cur ] );
                        }
                    }
                    break;
                case FR_VECT_FIELD_START_X:
                    /**
                     *   <li> <b>FR_VECT_FIELD_START_X</b>
                     *      <p> Set the origin of each data set.
                     * 	    Two arguments should follow this option.
                     *          The first argument needs to be a pointer
                     *          of type <b>fr_vect_startx_t</b> and must be
                     *          sufficiently large to hold fr_vect_ndim_t
                     * elements. The origin of each data set is stored here. The
                     * second arguemnt needs to be of type <b>fr_vect_ndim_t</b>
                     *          and contains the maximum number of elements
                     *          that can be stored in previous argument.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_startx_t* startx;
                        fr_vect_ndim_t    dim;
                        fr_vect_ndim_t    dim_limit = v->GetNDim( );
                        fr_vect_ndim_t    dim_cur;

                        startx = va_arg( ap, fr_vect_startx_t* );
                        dim = va_arg( ap, fr_vect_ndim_t );

                        for ( dim_cur = 0;
                              ( dim_cur < dim_limit ) && ( dim_cur < dim );
                              ++dim_cur )
                        {
                            v->GetDim( dim_cur ).SetStartX( startx[ dim_cur ] );
                        }
                    }
                    break;
                case FR_VECT_FIELD_UNIT_X:
                    /**
                     *   <li> <b>FR_VECT_FIELD_UNIT_X</b>
                     *      <p> Set the ASCII scale factor of each data set.
                     * 	    Two arguments should follow this option.
                     *          The first argument needs to be a pointer
                     *          of type <b>fr_vect_unit_x_t</b> and must be
                     *          sufficiently large to hold fr_vect_ndim_t
                     * elements. The origin of each data set is stored here. The
                     * second arguemnt needs to be of type <b>fr_vect_ndim_t</b>
                     *          and contains the maximum number of elements
                     *          that can be stored in previous argument.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_unit_x_t* unitx;
                        fr_vect_ndim_t    dim;
                        fr_vect_ndim_t    dim_limit = v->GetNDim( );
                        fr_vect_ndim_t    dim_cur;

                        unitx = va_arg( ap, fr_vect_unit_x_t* );
                        dim = va_arg( ap, fr_vect_ndim_t );

                        for ( dim_cur = 0;
                              ( dim_cur < dim_limit ) && ( dim_cur < dim );
                              ++dim_cur )
                        {
                            v->GetDim( dim_cur ).SetUnitX( unitx[ dim_cur ] );
                        }
                    }
                    break;
                case FR_VECT_FIELD_UNIT_Y:
                    /**   <li> <b>FR_VECT_FIELD_UNIT_Y</b>
                     *      <p>
                     *          Set the description of how to interpret the
                     * value of each element.
                     *      </p>
                     *   </li>
                     */
                    {
                        fr_vect_name_t name;

                        name = va_arg( ap, fr_vect_name_t );
                        v->SetUnitY( name );
                    }
                    break;
                default:
                    /**************************************************************
                      An unknown option has been specified. Terminate the
                    request.
                    ***************************************************************/
                    opt = FR_VECT_FIELD_LAST;
                    continue;
                }
                /**
                 * </ul>
                 */
                int next_opt = va_arg( ap, int /* fr_vect_fields*/ );
                opt = (fr_vect_fields)next_opt;
            }
        }
        else
        {
            Set( Error,
                 FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR,
                 "NULL reference to channel" );
        }
        va_end( ap );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR, Exception.what( ) );
    }
    catch ( ... )
    {
        Set( Error,
             FRAMEC_ERRNO_FR_CHANNEL_ACCESSOR_ERROR,
             FrameC::UnknownError );
    }
}

namespace FrameC
{
    FrChannel::FrChannel( ) : Handle( POINTER_FR_CHANNEL )
    {
        m_channel.type = UNKNOWN_CHAN_TYPE;
    }

    FrChannel::FrChannel( const std::string&   Name,
                          fr_chan_type         ChannelType,
                          fr_vect_data_types_t DataType,
                          fr_vect_ndata_t      NData )
        : Handle( POINTER_FR_CHANNEL )
    {
        m_channel.type = ChannelType;
        switch ( ChannelType )
        {
        case FR_ADC_CHAN_TYPE:
            m_channel.adc.reset(
                new FrameCPP::FrAdcData( Name,
                                         0, /* Group */
                                         0, /* Channel */
                                         0, /* nBits */
                                         1.0 /* sampleRate */ ) );
            break;
        case FR_EVENT_CHAN_TYPE:
            m_channel.event.reset( new FrameCPP::FrEvent( ) );
            m_channel.type = ChannelType;
            break;
        case FR_SIM_CHAN_TYPE:
            m_channel.sim.reset( new FrameCPP::FrSimData( Name,
                                                          "", /* Comment */
                                                          1.0, /* sampleRate */
                                                          0.0, /* fShift */
                                                          0.0 /* phase */ ) );
            break;
        case FR_SIM_EVENT_CHAN_TYPE:
            m_channel.sim_event.reset( new FrameCPP::FrSimEvent( ) );
            break;
        case FR_PROC_CHAN_TYPE:
            //-----------------------------------------------------------------
            // This is an error since there isn't enough data to properly
            // create the FrProcData structure
            //-----------------------------------------------------------------
        default:
            //-----------------------------------------------------------------
            // Error condition since the request isn't handled
            //-----------------------------------------------------------------
            m_channel.type = UNKNOWN_CHAN_TYPE;
            break;
        }
    }

    FrChannel::FrChannel( const std::string&   Name,
                          fr_proc_type         Type,
                          fr_proc_sub_type     SubType,
                          fr_vect_data_types_t DataType,
                          fr_vect_ndata_t      NData )
        : Handle( POINTER_FR_CHANNEL )
    {
        m_channel.proc.reset(
            new FrameCPP::FrProcData( Name,
                                      "", /* Comment */
                                      Type,
                                      SubType,
                                      0.0, /* timeOffset */
                                      1.0, /* tRange */
                                      0.0, /* fShift unkown */
                                      FrameCPP::FrProcData::PHASE_UNKNOWN,
                                      FrameCPP::FrProcData::FRANGE_UNKNOWN,
                                      FrameCPP::FrProcData::BW_UNKNOWN ) );
        m_channel.type = FR_PROC_CHAN_TYPE;
    }

    FrChannel::~FrChannel( )
    {
    }

    inline const_vector_type
    FrChannel::Vector( ) const
    {
        vector_type retval;

        switch ( m_channel.type )
        {
        case FR_ADC_CHAN_TYPE:
            if ( m_channel.adc->RefData( ).size( ) <= 0 )
            {
                throw std::runtime_error(
                    "No data vector associated with the channel" );
            }
            retval = m_channel.adc->RefData( )[ 0 ];
            break;
        case FR_PROC_CHAN_TYPE:
            if ( m_channel.proc->RefData( ).size( ) <= 0 )
            {
                throw std::runtime_error(
                    "No data vector associated with the channel" );
            }
            retval = m_channel.proc->RefData( )[ 0 ];
            break;
        case FR_SIM_CHAN_TYPE:
            if ( m_channel.sim->RefData( ).size( ) <= 0 )
            {
                throw std::runtime_error(
                    "No data vector associated with the channel" );
            }
            retval = m_channel.sim->RefData( )[ 0 ];
            break;
        case FR_SIM_EVENT_CHAN_TYPE:
            if ( m_channel.sim_event->RefData( ).size( ) <= 0 )
            {
                throw std::runtime_error(
                    "No data vector associated with the channel" );
            }
            retval = m_channel.sim_event->RefData( )[ 0 ];
            break;
        default:
            break;
        }

        if ( !retval )
        {
            throw std::runtime_error( "No FrVect structure" );
        }
        return retval;
    }

    inline vector_type
    FrChannel::Vector( )
    {
        vector_type retval;

        switch ( m_channel.type )
        {
        case FR_ADC_CHAN_TYPE:
            if ( m_channel.adc->RefData( ).size( ) <= 0 )
            {
                throw std::runtime_error(
                    "No data vector associated with the channel" );
            }
            retval = m_channel.adc->RefData( )[ 0 ];
            break;
        case FR_PROC_CHAN_TYPE:
            if ( m_channel.proc->RefData( ).size( ) <= 0 )
            {
                throw std::runtime_error(
                    "No data vector associated with the channel" );
            }
            retval = m_channel.proc->RefData( )[ 0 ];
            break;
        case FR_SIM_CHAN_TYPE:
            if ( m_channel.sim->RefData( ).size( ) <= 0 )
            {
                throw std::runtime_error(
                    "No data vector associated with the channel" );
            }
            retval = m_channel.sim->RefData( )[ 0 ];
            break;
        case FR_SIM_EVENT_CHAN_TYPE:
            if ( m_channel.sim_event->RefData( ).size( ) <= 0 )
            {
                throw std::runtime_error(
                    "No data vector associated with the channel" );
            }
            retval = m_channel.sim_event->RefData( )[ 0 ];
            break;
        default:
            break;
        }

        if ( !retval )
        {
            throw std::runtime_error( "No FrVect structure" );
        }
        return retval;
    }

    inline vector_type
    FrChannel::Vector( fr_vect_data_types_t DataType, fr_vect_ndata_t NData )
    {
        ::FrameCPP::FrVect::dimension_type dims( NData );
        ::FrameCPP::FrVect::data_type      data(
            ::FrameCPP::FrVect::DataAlloc( DataType, &dims ) );

        vector_type retval( new ::FrameCPP::FrVect(
            "temp",
            ::FrameCPP::FrVect::RAW,
            DataType,
            1,
            &dims,
            NData,
            FrameCPP::FrVect::GetTypeSize( DataType ) * NData,
            data,
            "" ) );

        switch ( m_channel.type )
        {
        case FR_ADC_CHAN_TYPE:
            retval->SetName( m_channel.adc->GetName( ) );
            m_channel.adc->RefData( ).clear( );
            m_channel.adc->RefData( ).append( retval );
            break;
        case FR_PROC_CHAN_TYPE:
            retval->SetName( m_channel.proc->GetName( ) );
            m_channel.proc->RefData( ).clear( );
            m_channel.proc->RefData( ).append( retval );
            break;
        case FR_SIM_CHAN_TYPE:
            retval->SetName( m_channel.sim->GetName( ) );
            m_channel.sim->RefData( ).clear( );
            m_channel.sim->RefData( ).append( retval );
            break;
        case FR_SIM_EVENT_CHAN_TYPE:
            retval->SetName( m_channel.sim_event->GetName( ) );
            m_channel.sim_event->RefData( ).clear( );
            m_channel.sim_event->RefData( ).append( retval );
            break;
        default:
            retval.reset( );
            break;
        }

        if ( !retval )
        {
            throw std::runtime_error( "No FrVect structure" );
        }
        return retval;
    }

} // namespace FrameC
