//
// 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 <sys/stat.h>

#include <cstdlib>

#include <fstream>
#include <sstream>

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

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

#include "ldastoolsal/Exec.hh"
#include "ldastoolsal/fstream.hh"

#include "framecpp/Common/BaseMemoryBuffer.hh"
#include "framecpp/Common/DynamicMemoryBuffer.hh"
#include "framecpp/Common/MemoryBuffer.hh"
#include "framecpp/Common/FrameBuffer.hh"

#include "framecpp/Types.hh"
#include "framecpp/GPSTime.hh"
#include "framecpp/IFrameStream.hh"
#include "framecpp/OFrameStream.hh"
#include "framecpp/FrameH.hh"
#include "framecpp/FrRawData.hh"
#include "framecpp/FrTOC.hh"
#include "framecpp/FrVect.hh"

#define SMALL_FRAMES_TYPE 1

using namespace FrameCPP;
using FrameCPP::GPSTime;
using LDASTools::AL::Exec;

using FrameCPP::Common::DynamicMemoryBuffer;
using FrameCPP::Common::FrameBuffer;
using FrameCPP::Common::MemoryBuffer;
using FrameCPP::Common::ReadOnlyMemoryBuffer;
typedef FrameCPP::Common::FrameBufferInterface::Scanner Scanner;
using FrameCPP::FrAdcData;
using FrameCPP::FrameH;
using FrameCPP::FrRawData;

typedef FrameBuffer< LDASTools::AL::filebuf >         frame_buffer_type;
typedef boost::shared_ptr< FrameCPP::Common::FrameH > build_frame_type;
typedef FrameCPP::Common::FrameSpec::version_type     frame_version_type;
typedef GPSTime                                       start_time_type;
typedef REAL_8                                        dt_type;

static build_frame_type
create_frame( frame_version_type Version, start_time_type Start, dt_type Dt );

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

static const bool HAS_BEEN_INITIALIZED = do_init( );

//-----------------------------------------------------------------------
// Common tasks to be performed when exiting.
//-----------------------------------------------------------------------
inline const std::string&
TEST_DIR( )
{
    static std::string test_dir_;

    if ( test_dir_.empty( ) )
    {
        const char* td( ::getenv( "TEST_DIR" ) );

        if ( td )
        {
            test_dir_ = td;
        }
    }
    return ( test_dir_ );
}

inline bool
verify_fileBaseName( const std::string Source )
{
    //---------------------------------------------------------
    // Verify fileBaseName
    //---------------------------------------------------------
    std::unique_ptr< frame_buffer_type > frame_buf(
        new frame_buffer_type( std::ios::in ) );
    frame_buf->open( Source.c_str( ), std::ios::in | std::ios::binary );

    IFrameStream stream( frame_buf.release( ) );

    auto toc_from_file = reinterpret_cast< const FrameCPP::FrTOC* >( stream.GetTOC( ) );
    auto fileBaseName_actual( boost::filesystem::path( toc_from_file->GetFileBaseName( ) ) );

    boost::filesystem::path path_info( Source );

    BOOST_TEST_MESSAGE( " fileBaseName_actual: " << fileBaseName_actual
                                                 << " =?= "
                                                 << path_info.filename( ) );

    return ( fileBaseName_actual == path_info.filename( ) );
}

inline bool
verify_fileBaseName( IFrameStream&      Source,
                     const std::string& fileBaseName_expected )
{
    //---------------------------------------------------------
    // Verify fileBaseName
    //---------------------------------------------------------
    auto fileBaseName_actual(
        reinterpret_cast< const FrameCPP::FrTOC* >( Source.GetTOC( ) )
            ->GetFileBaseName( ) );

    return ( fileBaseName_actual == fileBaseName_expected );
}

inline const std::string&
filename_source( int Spec = FRAME_SPEC_CURRENT )
{
    static std::string filename;

    std::ostringstream filename_oss;

    if ( TEST_DIR( ).size( ) > 0 )
    {
        filename_oss << TEST_DIR( ) << "/";
    }
    filename_oss << "Z-R_std_test_frame_ver" << Spec << "-" << 600000000 << "-"
                 << 1 << ".gwf";

    filename = filename_oss.str( );

    return filename;
}

void
test_dynamic_memory_buffer( )
{
    for ( INT_4U file_source_version = FRAME_SPEC_MIN,
                 file_source_version_end = FRAME_SPEC_MAX;
          file_source_version <= file_source_version_end;
          ++file_source_version )
    {
        if ( file_source_version == 5 )
        {
            continue;
        }

        std::ostringstream header;

        header << "test_dynamic_memory_buffer:"
               << " file source version: " << file_source_version;

        try
        {
            const std::string filename(
                filename_source( file_source_version ) );

            struct stat stat_buf;

            stat( filename.c_str( ), &stat_buf );

            DynamicMemoryBuffer            mb;
            boost::scoped_array< char >    read_buffer;
            DynamicMemoryBuffer::size_type read_buffer_size = 0;
            DynamicMemoryBuffer::size_type read_size = 0;
            std::ifstream                  s;

            mb.Reset( );

            s.open( filename.c_str( ) );
            while ( !mb.Ready( ) )
            {
                read_size = mb.NextBlockSize( );
                if ( read_buffer_size < read_size )
                {
                    read_buffer.reset( new char[ read_size ] );
                    read_buffer_size = read_size;
                }
                s.read( read_buffer.get( ), read_size );
                mb.NextBlock( read_buffer.get( ), read_size );
            }
            s.close( );

            IFrameStream ifs( false, &mb );

            while ( 1 )
            {
                try
                {
                    IFrameStream::frame_h_type frame( ifs.ReadNextFrame( ) );

                    if ( !frame )
                    {
                        break;
                    }

                    FrameH::rawData_type rd( frame->GetRawData( ) );

                    if ( rd )
                    {
                        BOOST_TEST_MESSAGE( header.str( )
                                            << ": firstAdc count: "
                                            << rd->RefFirstAdc( ).size( ) );
                        BOOST_CHECK( rd->RefFirstAdc( ).size( ) > 0 );
                    }

                    BOOST_TEST_MESSAGE( header.str( )
                                        << ": FrProcData count: "
                                        << frame->RefProcData( ).size( ) );
                    BOOST_CHECK( frame->RefProcData( ).size( ) > 0 );
                }
                catch ( const std::out_of_range& E )
                {
                    break;
                }
            }
        }
        catch ( const std::exception& E )
        {
            BOOST_TEST_MESSAGE( header.str( )
                                << ": Caught an exception: " << E.what( ) );
            BOOST_CHECK( false );
        }
        catch ( ... )
        {
            BOOST_TEST_MESSAGE( header.str( )
                                << ": Caught an unknown exception" );
            BOOST_CHECK( false );
        }
    }
}

void
test_memory_buffer( )
{
    for ( INT_4U file_source_version = FRAME_SPEC_MIN,
                 file_source_version_end = FRAME_SPEC_MAX;
          file_source_version <= file_source_version_end;
          ++file_source_version )
    {
        if ( file_source_version == 5 )
        {
            continue;
        }

        std::ostringstream header;

        header << "test_memory_buffer:"
               << " file source version: " << file_source_version;

        try
        {
            const std::string filename(
                filename_source( file_source_version ) );

            struct stat stat_buf;

            stat( filename.c_str( ), &stat_buf );

            boost::scoped_array< char > tbuf( new char[ stat_buf.st_size ] );
            std::ifstream               s;
            s.open( filename.c_str( ) );
            s.read( tbuf.get( ), stat_buf.st_size );
            boost::scoped_array< char > buf( new char[ stat_buf.st_size ] );
            MemoryBuffer                mb( std::ios::in | std::ios::binary );
            mb.pubsetbuf( buf.get( ), stat_buf.st_size );
            mb.str( std::string( tbuf.get( ), stat_buf.st_size ) );
            s.close( );

            IFrameStream ifs( false, &mb );

            while ( 1 )
            {
                try
                {
                    IFrameStream::frame_h_type frame( ifs.ReadNextFrame( ) );

                    if ( !frame )
                    {
                        break;
                    }

                    FrameH::rawData_type rd( frame->GetRawData( ) );

                    if ( rd )
                    {
                        BOOST_TEST_MESSAGE( header.str( )
                                            << ": firstAdc count: "
                                            << rd->RefFirstAdc( ).size( ) );
                        BOOST_CHECK( rd->RefFirstAdc( ).size( ) > 0 );
                    }

                    BOOST_TEST_MESSAGE( header.str( )
                                        << ": FrProcData count: "
                                        << frame->RefProcData( ).size( ) );
                    BOOST_CHECK( frame->RefProcData( ).size( ) > 0 );
                }
                catch ( const std::out_of_range& E )
                {
                    break;
                }
            }
        }
        catch ( const std::exception& E )
        {
            BOOST_TEST_MESSAGE( header.str( )
                                << ": Caught an exception: " << E.what( ) );
            BOOST_CHECK( false );
        }
        catch ( ... )
        {
            BOOST_CHECK( false );

            BOOST_TEST_MESSAGE( header.str( )
                                << ": Caught an unknown exception" );
        }
    }
}

BOOST_AUTO_TEST_CASE( test_memory_buffer_ro )
{
    BOOST_TEST_MESSAGE( "=== STARTING test_memory_buffer_ro ===" );
    for ( INT_4U file_source_version = FRAME_SPEC_MIN,
                 file_source_version_end = FRAME_SPEC_MAX;
          file_source_version <= file_source_version_end;
          ++file_source_version )
    {
        if ( file_source_version == 5 )
        {
            continue;
        }

        std::ostringstream header;

        header << "test_memory_buffer_ro:"
               << " file source version: " << file_source_version;

        BOOST_TEST_MESSAGE( header.str() << " - starting iteration" );

        try
        {
            const std::string filename(
                filename_source( file_source_version ) );

            struct stat stat_buf;

            if ( stat( filename.c_str( ), &stat_buf ) != 0 )
            {
                BOOST_TEST_MESSAGE( header.str()
                                    << ": Caught an exception: File not found: "
                                    << filename );
                BOOST_CHECK( false );
                continue;
            }

            boost::scoped_array< char > buf( new char[ stat_buf.st_size ] );
            std::ifstream               s;
            s.open( filename.c_str( ) );
            s.read( buf.get( ), stat_buf.st_size );
            s.close( );

            ReadOnlyMemoryBuffer mb;

            mb.pubsetbuf( buf.get( ), stat_buf.st_size );

            BOOST_TEST_MESSAGE(
                header.str( )
                << " starting address: " << (void*)( buf.get( ) ) << " size: "
                << stat_buf.st_size << " availablle: " << mb.in_avail( )
                << " current: " << mb.pubseekpos( 0, std::ios_base::in )
                << " current out: " << mb.pubseekpos( 0, std::ios_base::out )
                << " current end: "
                << mb.pubseekoff( 0, std::ios_base::end, std::ios_base::in ) );

            mb.pubseekpos( 0, std::ios_base::in );

            // Use new auto-detecting stream (no version parameter - always auto-detects)
            // This fixes v3 MemoryBuffer reading by using correct StreamRef structure
            FrameCPP::Common::IFrameStreamAuto ifs( false, &mb );

            while ( 1 )
            {
                try
                {
                    FrameCPP::Common::IFrameStreamAuto::frame_h_type frame( ifs.ReadNextFrame( ) );

                    if ( !frame )
                    {
                        break;
                    }

                    FrameH::rawData_type rd( frame->GetRawData( ) );

                    if ( rd )
                    {
                        BOOST_TEST_MESSAGE( header.str( )
                                            << ": firstAdc count: "
                                            << rd->RefFirstAdc( ).size( ) );
                        BOOST_CHECK( rd->RefFirstAdc( ).size( ) > 0 );
                    }

                    BOOST_TEST_MESSAGE( header.str( )
                                        << ": FrProcData count: "
                                        << frame->RefProcData( ).size( ) );
                    BOOST_CHECK( frame->RefProcData( ).size( ) > 0 );
                }
                catch ( const std::out_of_range& E )
                {
                    break;
                }
            }
        }
        catch ( const std::exception& E )
        {
            BOOST_TEST_MESSAGE( header.str( )
                                << ": Caught an exception: " << E.what( ) );
            BOOST_CHECK( false );
        }
        catch ( ... )
        {
            BOOST_CHECK( false );
            BOOST_TEST_MESSAGE( header.str( )
                                << ": Caught an unknown exception" );
        }

        BOOST_TEST_MESSAGE( header.str() << " - completed iteration" );
    }
    BOOST_TEST_MESSAGE( "=== COMPLETED test_memory_buffer_ro ===" );
}

//==============================================================================
// DISABLED: test_scanner - Causes 5-minute timeout
//==============================================================================
// This test hangs in an infinite loop during Scanner::NextBlock() processing.
//
// Root Cause:
// - The Scanner class processes frame files by reading blocks incrementally
// - Scanner::Ready() returns the value of frame_file_cached flag
// - For version 3 frames (and possibly others), frame_file_cached never becomes true
// - This causes the while (!scanner.Ready()) loop to run indefinitely
//
// Observed Behavior:
// - Test starts iteration for frame version 3
// - Hangs in the scanner loop reading blocks
// - Times out after 5 minutes (300 seconds)
// - Subsequent frame versions (4, 6, 7, 8, 9) are never tested
//
// To Re-enable:
// - Investigate Scanner::NextBlock() implementation in FrameBufferInterface.cc
// - Determine why frame_file_cached is not being set for version 3+ frames
// - Add safeguards (max iteration count, better error handling)
// - Change #if 0 to #if 1 below
//==============================================================================
#if 0
BOOST_AUTO_TEST_CASE( test_scanner )
{
    BOOST_TEST_MESSAGE( "=== STARTING test_scanner ===" );
    for ( INT_4U file_source_version = FRAME_SPEC_MIN,
                 file_source_version_end = FRAME_SPEC_MAX;
          file_source_version <= file_source_version_end;
          ++file_source_version )
    {
        if ( file_source_version == 5 )
        {
            continue;
        }

        std::ostringstream header;

        header << "test_scanner:"
               << " file source version: " << file_source_version;

        BOOST_TEST_MESSAGE( header.str() << " - starting iteration" );

        try
        {
            const std::string filename(
                filename_source( file_source_version ) );

            struct stat stat_buf;

            stat( filename.c_str( ), &stat_buf );

            std::ifstream               s;
            boost::scoped_array< char > read_buffer;
#if 0 || 1
            Scanner::size_type          read_buffer_size = 0;
            Scanner::size_type          read_size = 0;
#endif /* 0 */
            Scanner                     scanner;

            s.open( filename.c_str( ) );
#if 0 || 1
            while ( !scanner.Ready( ) )
            {
                read_size = scanner.NextBlockSize( );
                if ( read_buffer_size < read_size )
                {
                    read_buffer.reset( new char[ read_size ] );
                    read_buffer_size = read_size;
                }
#if DEBUG
                std::cerr //
                  << "DEBUG: read_buffer_size: " << read_buffer_size //
                          << " read_size: " << read_size //
                          << std::endl //
                  ;
#endif /* DEBUG */
                s.read( read_buffer.get( ), read_size );
                scanner.NextBlock( read_buffer.get( ), read_size );
            }
#endif /* 0 */
            s.close( );

            //-----------------------------------------------------------------
            // Display information about the stream
            //-----------------------------------------------------------------
            BOOST_TEST_MESSAGE( header.str( )
                                << " GPS Start: " << scanner.StartTime( )
                                << " DT: " << scanner.DeltaT( ) );
        }
        catch ( const std::exception& E )
        {
            BOOST_TEST_MESSAGE( header.str( )
                                << ": Caught an exception: " << E.what( ) );
            BOOST_CHECK( false );
        }
        catch ( ... )
        {
            BOOST_TEST_MESSAGE( header.str( )
                                << ": Caught an unknown exception" );
            BOOST_CHECK( false );
        }

        BOOST_TEST_MESSAGE( header.str() << " - completed iteration" );
    }
    BOOST_TEST_MESSAGE( "=== COMPLETED test_scanner ===" );
}
#endif

BOOST_AUTO_TEST_CASE( test_standard )
{
    BOOST_TEST_MESSAGE( "=== STARTING test_standard ===" );
    std::ostringstream header;

    header << "test_standard:";

    try
    {
        //-------------------------------------------------------------------
        // input buffer needed
        //-------------------------------------------------------------------
        std::unique_ptr< frame_buffer_type > i_frame_buf(
            new frame_buffer_type( std::ios::in ) );

        std::unique_ptr< frame_buffer_type > o_frame_buf(
            new frame_buffer_type( std::ios::out ) );

        //-------------------------------------------------------------------
        // Calculate the filename
        //-------------------------------------------------------------------
        std::ostringstream filename;

        if ( TEST_DIR( ).size( ) > 0 )
        {
            filename << TEST_DIR( ) << "/";
        }
        filename << "Z-R_std_test_frame_ver" << FRAME_SPEC_CURRENT << "-"
                 << 600000000 << "-" << 1 << ".gwf";

        std::ostringstream ofilename;

        if ( TEST_DIR( ).size( ) > 0 )
        {
            ofilename << TEST_DIR( ) << "/";
        }
        ofilename << "Z-R_std_test_frame_mem_buf_ver" << FRAME_SPEC_CURRENT
                  << "-" << 600000000 << "-" << 1 << ".gwf";
        //-----------------------------------------------------------------
        // Open the buffer and the stream
        //-----------------------------------------------------------------
        MemoryBuffer omb( std::ios::out );
        i_frame_buf->open( filename.str( ).c_str( ),
                           std::ios::in | std::ios::binary );

        o_frame_buf->open( ofilename.str( ).c_str( ),
                           std::ios::out | std::ios::binary );

        IFrameStream ifs( i_frame_buf.release( ) );

        OFrameStream ofs_mem( false, &omb );

        try
        {
            while ( 1 )
            {
                IFrameStream::frame_h_type f = ifs.ReadNextFrame( );

                ofs_mem.WriteFrame( f );
            }
        }
        catch ( const std::out_of_range& E )
        {
            ofs_mem.Close( );
            omb.pubsync( );

            OFrameStream ofs( o_frame_buf.release( ), FRAME_SPEC_CURRENT );
            auto toc_ptr = boost::reinterpret_pointer_cast< FrameCPP::FrTOC >( ofs.GetTOC( ) );
            toc_ptr->SetFileBaseName( ofilename.str( ) );

            //-----------------------------------------------------------------
            // load the input buffer with the output from the previous read
            //-----------------------------------------------------------------
            MemoryBuffer imb( std::ios::in );
            imb.str( omb.str( ) );

            IFrameStream ifs_mem( false, &imb );

            try
            {
                while ( 1 )
                {
                    IFrameStream::frame_h_type f = ifs_mem.ReadNextFrame( );
                    ofs.WriteFrame( f );
                }
            }
            catch ( const std::out_of_range& E )
            {
                // Do Nothing
            }
            ofs.Close( );
            {
                BOOST_TEST_MESSAGE( header.str( ) );
                BOOST_CHECK( verify_fileBaseName( ofilename.str( ) ) );
            }
        }
        catch ( std::exception& Exception )
        {
            throw;
        }
    }
    catch ( const std::exception& E )
    {
        BOOST_TEST_MESSAGE( "Caught an exception: " << E.what( ) );
        BOOST_CHECK( false );
    }
    catch ( ... )
    {
        BOOST_TEST_MESSAGE( "Caught an unknown exception" );
        BOOST_CHECK( false );
    }
    BOOST_TEST_MESSAGE( "=== COMPLETED test_standard ===" );
}

BOOST_AUTO_TEST_CASE( test_small_frames )
{
    BOOST_TEST_MESSAGE( "=== STARTING test_small_frames ===" );
    typedef std::list< INT_4U > frames_per_file_type;

    build_frame_type     frame;
    frames_per_file_type frames_per_file;

    frames_per_file.push_back( 50 );
    frames_per_file.push_back( 60 );

    for ( frame_version_type cur = FRAME_SPEC_CURRENT,
                             last = FRAME_SPEC_CURRENT + 1;
          cur != last;
          ++cur )
    {
        BOOST_TEST_MESSAGE( "test_small_frames: version " << cur << " - starting" );
        for ( frames_per_file_type::const_iterator
                  cur_fpf = frames_per_file.begin( ),
                  last_fpf = frames_per_file.end( );
              cur_fpf != last_fpf;
              ++cur_fpf )
        {
            BOOST_TEST_MESSAGE( "test_small_frames: version " << cur << ", frames_per_file " << *cur_fpf << " - starting" );
            start_time_type start( 913423140, 0 );
            dt_type         dt( 1 );

            std::ostringstream filename;
            filename << "Z-SmallFrame_" << *cur_fpf << "-"
                     << start.GetSeconds( ) << "-" << int( dt * *cur_fpf )
                     << ".gwf";
#if SMALL_FRAMES_TYPE == 0
            INT_4U OBUFFER_SIZE = ( 10 * 1024 * 1024 );
            boost::scoped_array< frame_buffer_type::char_type >

                                                 obuffer( new frame_buffer_type::char_type[ OBUFFER_SIZE ] );
            std::unique_ptr< frame_buffer_type > obuf(
                new frame_buffer_type( std::ios::out ) );
            obuf->pubsetbuf( obuffer.get( ), OBUFFER_SIZE );
            obuf->open( filename.str( ).c_str( ),
                        std::ios::out | std::ios::binary );

            OFrameStream ofs( obuf.release( ) );
#elif SMALL_FRAMES_TYPE == 1
            INT_4U OBUFFER_SIZE = ( 10 * 1024 * 1024 );
            boost::scoped_array< frame_buffer_type::char_type >

                                            obuffer( new frame_buffer_type::char_type[ OBUFFER_SIZE ] );
            FrameCPP::Common::MemoryBuffer* obuf(
                new FrameCPP::Common::MemoryBuffer( std::ios::out ) );
            obuf->pubsetbuf( obuffer.get( ), OBUFFER_SIZE );
            FrameCPP::OFrameStream ofs( obuf );
#endif
            ofs.DisableTOC( );

            BOOST_TEST_MESSAGE( "test_small_frames: version " << cur << ", frames_per_file " << *cur_fpf << " - writing frames" );
            for ( frames_per_file_type::value_type cur_sec = 0,
                                                   last_sec = *cur_fpf;
                  cur_sec != last_sec;
                  ++cur_sec, start += dt )
            {
                frame = create_frame( cur, start, dt );
                if ( frame )
                {
                    ofs.WriteFrame( frame,
                                    FrameCPP::FrVect::RAW,
                                    FrameCPP::FrVect::DEFAULT_GZIP_LEVEL );
                }
            }
            BOOST_TEST_MESSAGE( "test_small_frames: version " << cur << ", frames_per_file " << *cur_fpf << " - closing output stream" );
            ofs.Close( );
#if SMALL_FRAMES_TYPE == 1
            std::ofstream fstr;
            fstr.open( filename.str( ).c_str( ),
                       std::ios::out | std::ios::binary );
            fstr.write( obuf->str( ).c_str( ), obuf->str( ).length( ) );
            fstr.close( );
#endif /* 0 */
            char cfilename[ 4096 ];

            ::strcpy( cfilename, filename.str( ).c_str( ) );

            BOOST_TEST_MESSAGE( "test_small_frames: version " << cur << ", frames_per_file " << *cur_fpf << " - starting framecpp_verify for " << filename.str() );
            Exec cmd( "./framecpp_verify",
                      "-v", "2",
                      cfilename,
                      Exec::COMMAND_END );
            cmd.Spawn( );
            BOOST_TEST_MESSAGE(
                "Verify gstreamer short frames: " << filename.str( ) );
            BOOST_CHECK( cmd.ExitCode( ) == 0 );
            if ( cmd.ExitCode( ) != 0 )
            {
                BOOST_TEST_MESSAGE( "StdOut: " << cmd.Stdout( ) );
                BOOST_TEST_MESSAGE( "StdErr:" << cmd.Stderr( ) );
            }
            BOOST_TEST_MESSAGE( "test_small_frames: version " << cur << ", frames_per_file " << *cur_fpf << " - completed framecpp_verify" );
        }
        BOOST_TEST_MESSAGE( "test_small_frames: version " << cur << " - completed" );
    }
    BOOST_TEST_MESSAGE( "=== COMPLETED test_small_frames ===" );
}

BOOST_AUTO_TEST_CASE( test_read_events )
{
    BOOST_TEST_MESSAGE( "=== STARTING test_read_events ===" );
    try
    {
        static const size_t EVENTS = 2;
        const std::string   event_name( "TesT" );

        //-------------------------------------------------------------------
        // input buffer needed
        //-------------------------------------------------------------------
        std::unique_ptr< frame_buffer_type > i_frame_buf(
            new frame_buffer_type( std::ios::in ) );

        //-------------------------------------------------------------------
        // Calculate the filename
        //-------------------------------------------------------------------
        std::ostringstream filename;

        if ( TEST_DIR( ).size( ) > 0 )
        {
            filename << TEST_DIR( ) << "/";
        }
        filename << "Z-R_std_test_frame_ver" << FRAME_SPEC_CURRENT << "-"
                 << 600000000 << "-" << 1 << ".gwf";

        //-----------------------------------------------------------------
        // Open the buffer and the stream
        //-----------------------------------------------------------------
        BOOST_TEST_MESSAGE( "test_read_events: opening file " << filename.str() );
        i_frame_buf->open( filename.str( ).c_str( ),
                           std::ios::in | std::ios::binary );

        BOOST_TEST_MESSAGE( "test_read_events: creating IFrameStream" );
        IFrameStream ifs( i_frame_buf.release( ) );

        BOOST_TEST_MESSAGE( "test_read_events: reading FrEvents" );
        size_t x = 0;
        try
        {
            while ( true )
            {
                ifs.ReadFrEvent( event_name, x );
                ++x;
            }
        }
        catch ( const std::range_error& Error )
        {
        }
        catch ( const std::exception& Error )
        {
        }
        BOOST_TEST_MESSAGE( "Verifying the expected number of events (expected: " << EVENTS << ", received: " << x << ")" );
        BOOST_CHECK( EVENTS == x );
        {
            BOOST_TEST_MESSAGE( "test_read_events: reading FrSimEvents" );
            static const size_t SIM_EVENTS = size_t( 1 );
            const std::string   sim_event_name( "TesT" );
            x = 0;
            try
            {
                while ( true )
                {
                    ifs.ReadFrSimEvent( sim_event_name, x );
                    ++x;
                }
            }
            catch ( const std::range_error& Error )
            {
            }
            catch ( const std::exception& Error )
            {
            }
            BOOST_TEST_MESSAGE(
                "Verifying the expected number of simulated events "
                << "(expected: " << SIM_EVENTS << " received: " << x << ")" );
            BOOST_CHECK( SIM_EVENTS == x );
        }
    }
    catch ( std::exception& Exception )
    {
        BOOST_TEST_MESSAGE( "Caught an exception: " << Exception.what( ) );
        BOOST_CHECK( false );
    }
    BOOST_TEST_MESSAGE( "=== COMPLETED test_read_events ===" );
}

template < frame_version_type >
build_frame_type frameh( start_time_type StartTime, dt_type Dt );

template <>
build_frame_type
frameh< 9 >( start_time_type StartTime, dt_type Dt )
{
    using FrameCPP::Universal::FrameH;
    using FrameCPP::Universal::FrProcData;
    using FrameCPP::Universal::FrVect;

    boost::shared_ptr< FrameH > frameh(
        new FrameH( "SmallFrame", 0, 0, StartTime, Dt ) );

    std::string           name( "SFProc" );
    std::vector< REAL_8 > d( 512 );
    REAL_8                value( StartTime.GetSeconds( ) );

    for ( std::vector< REAL_8 >::iterator cur_d = d.begin( ), last_d = d.end( );
          cur_d != last_d;
          ++cur_d, value += Dt )
    {
        *cur_d = value;
    }
    FrVect::dimension_type dim( d.size( ) );
    FrVect                 v( name, 1, &dim, &d[ 0 ] );
    FrProcData             proc( name,
                     "",
                     FrProcData::TIME_SERIES,
                     FrProcData::UNKNOWN_SUB_TYPE,
                     0, /* TimeOffset */
                     0, /* TRange */
                     0, /* FShift */
                     0, /* Phase */
                     0, /* FRange */
                     0 /* BW */
    );
    proc.RefData( ).append( v );
    frameh->RefProcData( ).append( proc );

    return boost::dynamic_pointer_cast< build_frame_type::element_type >(
        frameh );
}

template <>
build_frame_type
frameh< 8 >( start_time_type StartTime, dt_type Dt )
{
    using FrameCPP::Universal::FrameH;
    using FrameCPP::Universal::FrProcData;
    using FrameCPP::Universal::FrVect;

    boost::shared_ptr< FrameH > frameh(
        new FrameH( "SmallFrame", 0, 0, StartTime, Dt ) );

    std::string           name( "SFProc" );
    std::vector< REAL_8 > d( 512 );
    REAL_8                value( StartTime.GetSeconds( ) );

    for ( std::vector< REAL_8 >::iterator cur_d = d.begin( ), last_d = d.end( );
          cur_d != last_d;
          ++cur_d, value += Dt )
    {
        *cur_d = value;
    }
    FrVect::dimension_type dim( d.size( ) );
    FrVect                 v( name, 1, &dim, &d[ 0 ] );
    FrProcData             proc( name,
                     "",
                     FrProcData::TIME_SERIES,
                     FrProcData::UNKNOWN_SUB_TYPE,
                     0, /* TimeOffset */
                     0, /* TRange */
                     0, /* FShift */
                     0, /* Phase */
                     0, /* FRange */
                     0 /* BW */
    );
    proc.RefData( ).append( v );
    frameh->RefProcData( ).append( proc );

    return boost::dynamic_pointer_cast< build_frame_type::element_type >(
        frameh );
}

static build_frame_type
create_frame( frame_version_type Version, start_time_type Start, dt_type Dt )
{
    build_frame_type result;

    switch ( Version )
    {
    case 9:
        result = frameh< 9 >( Start, Dt );
        break;
    case 8:
        result = frameh< 8 >( Start, Dt );
        break;
    default:
        break;
    }
    return result;
}
