/* * 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 <stdio.h>

#include <stdlib.h>
#include <unistd.h>

#include "framecppc/FrameC.h"
#include "framecppc/FrChan.h"
#include "framecppc/FrDetector.h"
#include "framecppc/FrEvent.h"
#include "framecppc/FrHistory.h"
#include "framecppc/FrVect.h"
#include "framecppc/FrameH.h"
#include "framecppc/Stream.h"

/*----------------------------------------------------------------------
 * Global variables
 *----------------------------------------------------------------------*/
FrameCError* ErrorCode; /* Records the error code	*/
const char*  ProcessName; /* Name of the running process	*/
int          ExitStatus = 0; /* Exit code			*/
int          Verbose = 0; /* Verbose reporting of errors  */

static const INT_2U        FRAME_VERSION = 8;
static const INT_4U        START = 600000000;
static const INT_2U        DT = 1;
static const frame_h_run_t RUN = -1;

typedef union point
{
    INT_2U int_2u;
    INT_2S int_2s;
    INT_4U int_4u;
    INT_4S int_4s;
    INT_8U int_8u;
    INT_8S int_8s;
    REAL_4 real_4;
    REAL_8 real_8;
} point_t;

typedef struct
{
    fr_vect_data_types_t s_data_type;

    point_t s_start;
    point_t s_end;
    point_t s_inc;
} range_t;

typedef fr_chan_t* ( *channel_builder_t )( const char*    Caller,
                                           const range_t* Range );

/*----------------------------------------------------------------------
 * Local variables
 *----------------------------------------------------------------------*/

INT_2U channel_id = 1;

/*----------------------------------------------------------------------
 * Macro definition
 *----------------------------------------------------------------------*/

#define RANGE_SIZE( x )                                                        \
    retval =                                                                   \
        ( size_t )( ( Range->s_end.x - Range->s_start.x ) / Range->s_inc.x )

/*----------------------------------------------------------------------
 * Function definition
 *----------------------------------------------------------------------*/
int
Failed( const char* FunctionName )
{
    if ( ErrorCode )
    {
        if ( Verbose )
        {
            fprintf( stderr,
                     "FATAL: %s: %d : %s\n",
                     FunctionName,
                     ErrorCode->s_errno,
                     ErrorCode->s_message );
        }
        ExitStatus = 1;
        return 1;
    }
    return 0;
}

fr_vect_ndata_t
range_size( const range_t* Range )
{
    fr_vect_ndata_t retval = 0;

    if ( Range )
    {
        switch ( Range->s_data_type )
        {
        case FR_VECT_2U:
            RANGE_SIZE( int_2u );
            break;
        case FR_VECT_2S:
            RANGE_SIZE( int_2s );
            break;
        case FR_VECT_4U:
            RANGE_SIZE( int_4u );
            break;
        case FR_VECT_4S:
            RANGE_SIZE( int_4s );
            break;
        case FR_VECT_8U:
            RANGE_SIZE( int_8u );
            break;
        case FR_VECT_8S:
            RANGE_SIZE( int_8s );
            break;
        case FR_VECT_4R:
            RANGE_SIZE( real_4 );
            break;
        case FR_VECT_8R:
            RANGE_SIZE( real_8 );
            break;
        default:
            break;
        }
    }

#undef RANGE_SIZE

    return retval;
}

int
create_ramp_data( fr_chan_t* Channel, const range_t* Range )
{
    static const char* FUNCTION_NAME = "create_ramp_data";
    int                retval = 1;

#define RAMP( x, data_type )                                                   \
    {                                                                          \
        data_type       value = (data_type)0;                                  \
        data_type*      data_ptr = (data_type*)NULL;                           \
        data_type*      data_ptr_cur = (data_type*)NULL;                       \
        data_type*      data_ptr_last = (data_type*)NULL;                      \
        fr_vect_ndata_t size = ( fr_vect_ndata_t )(                            \
            ( Range->s_end.x - Range->s_start.x ) / Range->s_inc.x );          \
                                                                               \
        FrameCFrChanVectorAlloc(                                               \
            &ErrorCode, Channel, Range->s_data_type, size );                   \
                                                                               \
        if ( Failed( FUNCTION_NAME ) )                                         \
            goto create_ramp_data_error;                                       \
        data_ptr = (data_type*)calloc( size, sizeof( data_type ) );            \
        if ( data_ptr == NULL )                                                \
            goto create_ramp_data_error;                                       \
        data_ptr_cur = data_ptr;                                               \
        data_ptr_last = &( data_ptr[ size ] );                                 \
        value = Range->s_start.x;                                              \
        while ( data_ptr_cur != data_ptr_last )                                \
        {                                                                      \
            *data_ptr_cur = value;                                             \
            value += Range->s_inc.x;                                           \
            ++data_ptr_cur;                                                    \
        }                                                                      \
        FrameCFrChanVectorSet( &ErrorCode,                                     \
                               Channel,                                        \
                               FR_VECT_FIELD_DATA,                             \
                               data_ptr,                                       \
                               (int)( Range->s_data_type ),                    \
                               size,                                           \
                               FR_VECT_FIELD_LAST );                           \
        if ( data_ptr )                                                        \
            free( data_ptr );                                                  \
                                                                               \
        if ( Failed( FUNCTION_NAME ) )                                         \
            goto create_ramp_data_error;                                       \
    }

    if ( Range )
    {
        switch ( Range->s_data_type )
        {
        case FR_VECT_2U:
            RAMP( int_2u, INT_2U );
            break;
        case FR_VECT_2S:
            RAMP( int_2s, INT_2S );
            break;
        case FR_VECT_4U:
            RAMP( int_4u, INT_4U );
            break;
        case FR_VECT_4S:
            RAMP( int_4s, INT_4S );
            break;
        case FR_VECT_8U:
            RAMP( int_8u, INT_8U );
            break;
        case FR_VECT_8S:
            RAMP( int_8s, INT_8S );
            break;
        case FR_VECT_4R:
            RAMP( real_4, REAL_4 );
            break;
        case FR_VECT_8R:
            RAMP( real_8, REAL_8 );
            break;
        default:
            break;
        }
    }

    return retval;

create_ramp_data_error:

    retval = 0;
    return retval;
}

void
init_range( const range_t* Range,
            const char**   DataType,
            double*        Start,
            double*        Stop,
            double*        Inc )
{
#define INIT( x, y )                                                           \
    *DataType = y;                                                             \
    *Start = (double)( Range->s_start.x );                                     \
    *Stop = (double)( Range->s_end.x );                                        \
    *Inc = (double)( Range->s_inc.x )

    switch ( Range->s_data_type )
    {
    case FR_VECT_2S:
        INIT( int_2s, "2S" );
        break;
    case FR_VECT_2U:
        INIT( int_2u, "2U" );
        break;
    case FR_VECT_4S:
        INIT( int_4s, "4S" );
        break;
    case FR_VECT_4U:
        INIT( int_4u, "4U" );
        break;
    case FR_VECT_8S:
        INIT( int_8s, "8S" );
        break;
    case FR_VECT_8U:
        INIT( int_8u, "8U" );
        break;
    case FR_VECT_4R:
        INIT( real_4, "4R" );
        break;
    case FR_VECT_8R:
        INIT( real_8, "8R" );
        break;
    default:
        break;
    }

#undef INIT
}

fr_chan_t*
create_channel_adc( const char* Caller, const range_t* Range )
{
    static const char* FUNCTION_NAME = "create_channel_adc";

    fr_chan_t*       channel = (fr_chan_t*)NULL;
    char             name[ 256 ];
    const char*      data_type = "unknown";
    double           start = 0.0;
    double           stop = 0.0;
    double           inc = 1.0;
    double           size = range_size( Range );
    fr_vect_dx_t     dx = ( 1.0 / size );
    fr_vect_nx_t     nx = 0;
    fr_vect_startx_t startX = 0.0;
    fr_vect_unit_x_t unitX = "UnitX";

    init_range( Range, &data_type, &start, &stop, &inc );

    snprintf(
        name, sizeof(name), "Z1:ADC-%s-%s-%g-%g-%g", Caller, data_type, start, stop, inc );

    channel = FrameCFrChanAlloc( &ErrorCode,
                                 name,
                                 FR_ADC_CHAN_TYPE,
                                 Range->s_data_type,
                                 range_size( Range ) );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_adc_error;

    if ( create_ramp_data( channel, Range ) == 0 )
    {
        goto create_channel_adc_error;
    }

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_adc_error;

    nx = range_size( Range );
    FrameCFrChanVectorSet( &ErrorCode,
                           channel,
                           FR_VECT_FIELD_NAME,
                           ( fr_vect_name_t )( name ),
                           FR_VECT_FIELD_NX,
                           &nx,
                           1,
                           FR_VECT_FIELD_DX,
                           &dx,
                           1,
                           FR_VECT_FIELD_START_X,
                           &startX,
                           1,
                           FR_VECT_FIELD_UNIT_X,
                           &unitX,
                           1,
                           FR_VECT_FIELD_UNIT_Y,
                           "UnitY",
                           FR_VECT_FIELD_LAST );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_adc_error;

    FrameCFrChanSet( &ErrorCode,
                     channel,
                     FR_CHAN_FIELD_CHANNEL_GROUP,
                     0,
                     FR_CHAN_FIELD_CHANNEL_NUMBER,
                     channel_id++,
                     FR_CHAN_FIELD_N_BITS,
                     16,
                     FR_CHAN_FIELD_SAMPLE_RATE,
                     ( fr_chan_sample_rate_t )( size ),
                     FR_CHAN_FIELD_LAST );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_adc_error;

    /*---------------------------------------------------------------------
     * Have a properly crafted channel.
     * Report back to the caller.
     *---------------------------------------------------------------------*/
    return channel;

create_channel_adc_error:
    if ( channel )
    {
        FrameCFrChanFree( &ErrorCode, channel );
        channel = (fr_chan_t*)NULL;
    }
    return NULL;
}

fr_chan_t*
create_channel_adc_invalid( const char* Caller, const range_t* Range )
{
    static const char* FUNCTION_NAME = "create_channel_adc_invalid";

    fr_chan_t*       channel = (fr_chan_t*)NULL;
    char             name[ 256 ];
    const char*      data_type = "unknown";
    double           start = 0.0;
    double           stop = 0.0;
    double           inc = 1.0;
    double           size = range_size( Range );
    fr_vect_dx_t     dx = ( 1.0 / size );
    fr_vect_nx_t     nx = 0;
    fr_vect_startx_t startX = 0.0;
    fr_vect_unit_x_t unitX = "UnitX";

    init_range( Range, &data_type, &start, &stop, &inc );

    snprintf( name,
              sizeof(name),
              "Z1:ADC-Invalid-%s-%s-%g-%g-%g",
              Caller,
              data_type,
              start,
              stop,
              inc );

    channel = FrameCFrChanAlloc( &ErrorCode,
                                 name,
                                 FR_ADC_CHAN_TYPE,
                                 Range->s_data_type,
                                 range_size( Range ) );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_adc_invalid_error;

    if ( create_ramp_data( channel, Range ) == 0 )
    {
        goto create_channel_adc_invalid_error;
    }

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_adc_invalid_error;

    nx = range_size( Range );
    FrameCFrChanVectorSet( &ErrorCode,
                           channel,
                           FR_VECT_FIELD_NAME,
                           ( fr_vect_name_t )( name ),
                           FR_VECT_FIELD_NX,
                           &nx,
                           1,
                           FR_VECT_FIELD_DX,
                           &dx,
                           1,
                           FR_VECT_FIELD_START_X,
                           &startX,
                           1,
                           FR_VECT_FIELD_UNIT_X,
                           &unitX,
                           1,
                           FR_VECT_FIELD_UNIT_Y,
                           "UnitY",
                           FR_VECT_FIELD_LAST );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_adc_invalid_error;

    FrameCFrChanSet( &ErrorCode,
                     channel,
                     FR_CHAN_FIELD_CHANNEL_GROUP,
                     0,
                     FR_CHAN_FIELD_CHANNEL_NUMBER,
                     channel_id++,
                     FR_CHAN_FIELD_N_BITS,
                     16,
                     FR_CHAN_FIELD_SAMPLE_RATE,
                     ( fr_chan_sample_rate_t )( size ),
                     FR_CHAN_FIELD_DATA_VALID,
                     2,
                     FR_CHAN_FIELD_LAST );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_adc_invalid_error;

    /*---------------------------------------------------------------------
     * Have a properly crafted channel.
     * Report back to the caller.
     *---------------------------------------------------------------------*/
    return channel;

create_channel_adc_invalid_error:
    if ( channel )
    {
        FrameCFrChanFree( &ErrorCode, channel );
        channel = (fr_chan_t*)NULL;
    }
    return NULL;
}

fr_chan_t*
create_channel_proc( const char* Caller, const range_t* Range )
{
    static const char* FUNCTION_NAME = "create_channel_proc";

    fr_chan_t*       channel = (fr_chan_t*)NULL;
    char             name[ 256 ];
    const char*      data_type = "unknown";
    double           start = 0.0;
    double           stop = 0.0;
    double           inc = 1.0;
    double           size = range_size( Range );
    fr_vect_dx_t     dx = ( 1.0 / size );
    fr_vect_nx_t     nx = 0;
    fr_vect_startx_t startX = 0.0;
    fr_vect_unit_x_t unitX = "UnitX";

    init_range( Range, &data_type, &start, &stop, &inc );

    snprintf(
        name, sizeof(name), "Z1:PROC-%s-%s-%g-%g-%g", Caller, data_type, start, stop, inc );

    channel = FrameCFrProcChanAlloc( &ErrorCode,
                                     name,
                                     FR_PROC_TYPE_TIME_SERIES,
                                     FR_PROC_SUB_TYPE_UNKNOWN,
                                     Range->s_data_type,
                                     range_size( Range ) );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_proc_error;

    if ( create_ramp_data( channel, Range ) == 0 )
    {
        goto create_channel_proc_error;
    }

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_proc_error;

    nx = range_size( Range );
    FrameCFrChanVectorSet( &ErrorCode,
                           channel,
                           FR_VECT_FIELD_NAME,
                           ( fr_vect_name_t )( name ),
                           FR_VECT_FIELD_NX,
                           &nx,
                           1,
                           FR_VECT_FIELD_DX,
                           &dx,
                           1,
                           FR_VECT_FIELD_START_X,
                           &startX,
                           1,
                           FR_VECT_FIELD_UNIT_X,
                           &unitX,
                           1,
                           FR_VECT_FIELD_UNIT_Y,
                           "UnitY",
                           FR_VECT_FIELD_LAST );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_proc_error;

    FrameCFrChanSet( &ErrorCode,
                     channel,
                     FR_CHAN_FIELD_COMMENT,
                     "Sample proc channel",
                     FR_CHAN_FIELD_T_RANGE,
                     1.0,
                     FR_CHAN_FIELD_SAMPLE_RATE,
                     ( fr_chan_sample_rate_t )( size ),
                     FR_CHAN_FIELD_LAST );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_proc_error;

    /*---------------------------------------------------------------------
     * Have a properly crafted channel.
     * Report back to the caller.
     *---------------------------------------------------------------------*/
    return channel;

create_channel_proc_error:
    if ( channel )
    {
        FrameCFrChanFree( &ErrorCode, channel );
        channel = (fr_chan_t*)NULL;
    }
    return NULL;
}

fr_chan_t*
create_channel_sim( const char* Caller, const range_t* Range )
{
    static const char* FUNCTION_NAME = "create_channel_sim";

    fr_chan_t*       channel = (fr_chan_t*)NULL;
    char             name[ 256 ];
    const char*      data_type = "unknown";
    double           start = 0.0;
    double           stop = 0.0;
    double           inc = 1.0;
    double           size = range_size( Range );
    fr_vect_dx_t     dx = ( 1.0 / size );
    fr_vect_nx_t     nx = 0;
    fr_vect_startx_t startX = 0.0;
    fr_vect_unit_x_t unitX = "UnitX";

    init_range( Range, &data_type, &start, &stop, &inc );

    snprintf(
        name, sizeof(name), "Z1:SIM-%s-%s-%g-%g-%g", Caller, data_type, start, stop, inc );

    channel = FrameCFrChanAlloc( &ErrorCode,
                                 name,
                                 FR_SIM_CHAN_TYPE,
                                 Range->s_data_type,
                                 range_size( Range ) );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_sim_error;

    if ( create_ramp_data( channel, Range ) == 0 )
    {
        goto create_channel_sim_error;
    }

    nx = range_size( Range );
    FrameCFrChanVectorSet( &ErrorCode,
                           channel,
                           FR_VECT_FIELD_NAME,
                           ( fr_vect_name_t )( name ),
                           FR_VECT_FIELD_NX,
                           &nx,
                           1,
                           FR_VECT_FIELD_DX,
                           &dx,
                           1,
                           FR_VECT_FIELD_START_X,
                           &startX,
                           1,
                           FR_VECT_FIELD_UNIT_X,
                           &unitX,
                           1,
                           FR_VECT_FIELD_UNIT_Y,
                           "UnitY",
                           FR_VECT_FIELD_LAST );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_sim_error;

    FrameCFrChanSet( &ErrorCode,
                     channel,
                     FR_CHAN_FIELD_COMMENT,
                     "Sample sim channel",
                     FR_CHAN_FIELD_SAMPLE_RATE,
                     ( fr_chan_sample_rate_t )( size ),
                     FR_CHAN_FIELD_LAST );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_sim_error;

    /*---------------------------------------------------------------------
     * Have a properly crafted channel.
     * Report back to the caller.
     *---------------------------------------------------------------------*/
    return channel;

create_channel_sim_error:
    return NULL;
}

void
create_channel_set( channel_builder_t func, frame_h_t* FrameH )

{
    static const char* FUNCTION_NAME = "create_channel_set";
    range_t            range;
    fr_chan_t*         channel = (fr_chan_t*)NULL;

    /*---------------------------------------------------------------------
     * Create a set of channels using the specified generator
     *---------------------------------------------------------------------*/
    range.s_data_type = FR_VECT_4R;
    range.s_start.real_4 = 0.0;
    range.s_end.real_4 = 256.0;
    range.s_inc.real_4 = .25;

    channel = ( *func )( "Ramp", &range );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_set_error;

    FrameCFrameHFrChanAdd( &ErrorCode, FrameH, channel );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_set_error;

    FrameCFrChanFree( &ErrorCode, channel );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_set_error;

    /*---------------------------------------------------------------------*/

    range.s_data_type = FR_VECT_8R;
    range.s_start.real_8 = 0.0;
    range.s_end.real_8 = 256.0;
    range.s_inc.real_8 = 1.0;

    channel = ( *func )( "Ramp", &range );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_set_error;

    FrameCFrameHFrChanAdd( &ErrorCode, FrameH, channel );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_set_error;

    FrameCFrChanFree( &ErrorCode, channel );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_set_error;

    /*---------------------------------------------------------------------*/

    range.s_data_type = FR_VECT_8S;
    range.s_start.int_8s = (INT_8S)-1024;
    range.s_end.int_8s = (INT_8S)1024;
    range.s_inc.int_8s = (INT_8S)2;

    channel = ( *func )( "Ramp", &range );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_set_error;

    FrameCFrameHFrChanAdd( &ErrorCode, FrameH, channel );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_set_error;

    FrameCFrChanFree( &ErrorCode, channel );

    if ( Failed( FUNCTION_NAME ) )
        goto create_channel_set_error;

    /*---------------------------------------------------------------------
     * Normal completion
     *---------------------------------------------------------------------*/
    return;

create_channel_set_error:
    if ( channel )
    {
        FrameCFrChanFree( &ErrorCode, channel );
        channel = (fr_chan_t*)NULL;
    }
    return;
}

frame_h_t*
create_frame( void )
{
    static const char* FUNCTION_NAME = "create_frame";

    frame_h_t*      frame;
    frame_h_gtime_t start;
    frame_h_dt_t    dt = (frame_h_dt_t)DT;

    start.sec = START;
    start.nan = 0;

    frame = FrameCFrameHAlloc( &ErrorCode, "LIGO", start, dt, 0 );

    if ( Failed( FUNCTION_NAME ) )
        goto create_frame_error;

    /*--------------------------------------------------------------------
     * Establish some additional values
     *--------------------------------------------------------------------*/
    FrameCFrameHSet(
        &ErrorCode, frame, FRAME_H_FIELD_RUN, RUN, FRAME_H_FIELD_LAST );

    if ( Failed( FUNCTION_NAME ) )
        goto create_frame_error;

    /*--------------------------------------------------------------------
     * Add some history
     *--------------------------------------------------------------------*/
    {
        fr_history_t* history;

        history =
            FrameCFrHistoryAlloc( &ErrorCode,
                                  "CSampleFrame",
                                  FrameCGPSTimeNow( ),
                                  "Example of how to create frames in C" );

        if ( Failed( FUNCTION_NAME ) )
            goto create_frame_error;

        FrameCFrameHFrHistoryAdd( &ErrorCode, frame, history );

        if ( Failed( FUNCTION_NAME ) )
        {
            /* Cleanup allocated resources */
            FrameCFrHistoryFree( &ErrorCode, history );
            goto create_frame_error;
        }

        FrameCFrHistoryFree( &ErrorCode, history );

        if ( Failed( FUNCTION_NAME ) )
            goto create_frame_error;
    }

    /*--------------------------------------------------------------------
     * Just for kicks, put in detector information for:
     *   Hanford, Livingston, and Virgo.
     * Normally, you would only put in the detector information for the
     *   sites referenced by the channels which compose the frame.
     *--------------------------------------------------------------------*/
    {
        size_t x;

        int types[] = { DETECTOR_DATA_QUALITY_OFFSET_H1,
                        DETECTOR_DATA_QUALITY_OFFSET_L1,
                        DETECTOR_DATA_QUALITY_OFFSET_V1 };

        for ( x = 0; x < sizeof( types ) / sizeof( *types ); ++x )
        {
            fr_detector_t* detector = FrameCGetDetector(
                &ErrorCode, ( std_detectors )( types[ x ] ) );

            if ( Failed( FUNCTION_NAME ) )
                goto create_frame_error;

            FrameCFrameHFrDetectorAdd( &ErrorCode, frame, detector );

            if ( Failed( FUNCTION_NAME ) )
            {
                FrameCFrDetectorFree( &ErrorCode, detector );
                goto create_frame_error;
            }

            FrameCFrDetectorFree( &ErrorCode, detector );

            if ( Failed( FUNCTION_NAME ) )
                goto create_frame_error;
        }
    }

    /*--------------------------------------------------------------------
     * More fun by adding an FrEvent
     *--------------------------------------------------------------------*/
    {
        fr_event_parameters_t params[ 2 ] = { { 1.0, "event_param_1" },
                                              { 2.0, "event_param_2" } };

        fr_event_t*      event;
        fr_event_gtime_t now;

        now.sec = FrameCGPSTimeNow( );
        now.nan = (gpstime_nanoseconds_t)0;

        event = FrameCFrEventAlloc( &ErrorCode,
                                    "event_1",
                                    "This is the first event",
                                    "xy",
                                    now,
                                    4.0,
                                    6.0,
                                    0,
                                    0,
                                    0.5,
                                    "unlikely",
                                    sizeof( params ) / sizeof( *params ),
                                    params );
        if ( Failed( FUNCTION_NAME ) )
            goto create_frame_error;

        FrameCFrameHFrEventAdd( &ErrorCode, frame, event );

        if ( Failed( FUNCTION_NAME ) )
            goto create_frame_error;

        FrameCFrEventFree( &ErrorCode, event );

        if ( Failed( FUNCTION_NAME ) )
            goto create_frame_error;
    }

    /*--------------------------------------------------------------------
     * Create some standard channels
     *--------------------------------------------------------------------*/
    create_channel_set( create_channel_adc, frame );
    create_channel_set( create_channel_adc_invalid, frame );
    create_channel_set( create_channel_proc, frame );
    create_channel_set( create_channel_sim, frame );

    /*--------------------------------------------------------------------
     * This was the code path when nothing fails
     *--------------------------------------------------------------------*/
    return frame;

create_frame_error:
    /*--------------------------------------------------------------------
     * Something has failed. Unwind resources
     *--------------------------------------------------------------------*/
    if ( frame )
    {
        FrameCFrameHFree( &ErrorCode, frame );
        frame = (frame_h_t*)NULL;
    }
    return frame;
}

int
main( int ArgC, char** ArgV )
{
    static const char* FUNCTION_NAME = "main";

    char       filename[ 1024 ];
    frame_h_t* frame = (frame_h_t*)NULL;

    fr_file_t* ofs = (fr_file_t*)NULL;

    ProcessName = ArgV[ 0 ];

    sleep( 30 );
    snprintf( filename, sizeof(filename), "Z-Sample_C_ver%d-%d-%d.gwf", FRAME_VERSION, START, DT );
    /*--------------------------------------------------------------------
     * Ensure the library is properly initialized
     *--------------------------------------------------------------------*/
    FrameCInitialize( );
    /*--------------------------------------------------------------------
     * Open the stream for writing
     *--------------------------------------------------------------------*/
    ofs = FrameCFileOpen( &ErrorCode, filename, FRAMEC_FILE_MODE_OUTPUT );

    if ( Failed( FUNCTION_NAME ) )
        goto error;

    /*--------------------------------------------------------------------
     * Create the frame with all of its data components
     *--------------------------------------------------------------------*/
    frame = create_frame( );

    if ( ExitStatus != 0 )
    {
        /*------------------------------------------------------------------
         * Failed to  create the frame. The error has already been reported,
         * just need to reclaim resources.
         *------------------------------------------------------------------*/
        goto error;
    }

    /*--------------------------------------------------------------------
     * Write the frame to the stream
     *--------------------------------------------------------------------*/
    FrameCFrameHWrite( &ErrorCode, ofs, frame );

    if ( Failed( FUNCTION_NAME ) )
        goto error;

error:
    /*--------------------------------------------------------------------
     * Close the stream
     *--------------------------------------------------------------------*/
    if ( ofs )
    {
        FrameCFileClose( &ErrorCode, ofs );
        FrameCFileFree( &ErrorCode, ofs );
    }
    if ( frame )
    {
        FrameCFrameHFree( &ErrorCode, frame );
    }
    /*--------------------------------------------------------------------
     * Ensure the library is properly cleaned up.
     *--------------------------------------------------------------------*/
    FrameCCleanup( );
    exit( ExitStatus );
}
