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

/* -*- mode: C++; c-basic-offset: 2; -*- */
#if HAVE_CONFIG_H
#include <framecpp_config.h>
#endif /* HAVE_CONFIG_H */

#include <sys/stat.h>

#include <algorithm>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>

// Define BOOST_TEST_NO_MAIN since FrSample.hh includes unit_test.hpp
#define BOOST_TEST_NO_MAIN

#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/serialization/strong_typedef.hpp>
#include <boost/log/core.hpp>

#include "ldastoolsal/MemChecker.hh"
#include "ldastoolsal/CommandLineOptions.hh"
#include "ldastoolsal/fstream.hh"
#include "ldastoolsal/types.hh"

#include "framecpp/Common/FrameSpec.hh"
#include "framecpp/Common/CheckSum.hh"
#include "framecpp/Common/IOStream.hh"
#include "framecpp/Common/FrameBuffer.hh"
#include "framecpp/Common/FrameStream.hh"

#include "framecpp/Universal/FrMsg.hh"

#include "framecpp/FrameCPP.hh"
#include "framecpp/FrameCPPDebugManager.hh"
#include "ldastoolsal/Logger.hh"

// Include common frame sample utilities
#include "FrSampleCommon.hh"
#include "FrSampleTestConstants.hh"

#ifdef FRAMEL_FOUND
#include "FrSampleFrameL.hh"
#endif

#include "framecpp/FrameH.hh"
#include "framecpp/GPSTime.hh"
#include "framecpp/FrAdcData.hh"
#include "framecpp/FrDetector.hh"
#include "framecpp/FrEvent.hh"
#include "framecpp/FrHistory.hh"
#include "framecpp/FrMsg.hh"
#include "framecpp/FrProcData.hh"
#include "framecpp/FrRawData.hh"
#include "framecpp/FrSerData.hh"
#include "framecpp/FrSimData.hh"
#include "framecpp/FrSimEvent.hh"
#include "framecpp/FrStatData.hh"
#include "framecpp/FrSummary.hh"
#include "framecpp/FrTable.hh"
#include "framecpp/FrVect.hh"

using FrameCPP::GPSTime;

using FrameCPP::Universal::FrAdcData;
using FrameCPP::Universal::FrameH;
using FrameCPP::Universal::FrDetector;
using FrameCPP::Universal::FrEvent;
using FrameCPP::Universal::FrHistory;
using FrameCPP::Universal::FrProcData;
using FrameCPP::Universal::FrRawData;
using FrameCPP::Universal::FrSerData;
using FrameCPP::Universal::FrSimData;
using FrameCPP::Universal::FrSimEvent;
using FrameCPP::Universal::FrStatData;
using FrameCPP::Universal::FrSummary;
using FrameCPP::Universal::FrTable;

// Static variable to store command-line debug level for use during program termination
// This is safe to access during exit because it's a POD type with no destructor
static int g_command_line_debug_level = 0;
using FrameCPP::Universal::FrVect;

#include "FrSample.hh"
#include "FrSample3.tcc"
#include "FrSample4.tcc"
#include "FrSample6.tcc"
#include "FrSample7.tcc"
#include "FrSample8.tcc"
#include "FrSample9.tcc"

namespace po = boost::program_options;

using LDASTools::AL::filebuf;
using LDASTools::AL::MemChecker;

typedef LDASTools::AL::CommandLineOptions CommandLineOptions;
typedef CommandLineOptions::Option        Option;
typedef CommandLineOptions::OptionSet     OptionSet;
using FrameCPP::Common::CheckSum;
using FrameCPP::Common::FrameBuffer;
using FrameCPP::Common::OFrameStream;
using FrameCPP::Common::OStream;

using std::ostringstream;
using std::string;

using FrameCPP::FrAdcData;
using FrameCPP::FrameH;
using FrameCPP::FrProcData;
using FrameCPP::FrRawData;
using FrameCPP::FrSerData;
using FrameCPP::FrSimData;
using FrameCPP::FrVect;

namespace po = boost::program_options;
using FrameSampleCommon::Config;

//-----------------------------------------------------------------------
// Close obuf and verify that the close actually succeeded.
//
// FrameBuffer<filebuf>::close() returns nullptr on failure (the
// underlying basic_filebuf::close() tracks an __ok bool covering
// the final overflow(eof) flush, the _M_unshift, and the OS-level
// _M_close — see ldastoolsal/src/fstream.hh:958-993). A nullptr
// return therefore signals either a buffered-write that never made
// it to disk during the closing flush or a fclose()-level OS error.
//
// The pre-#286 code ignored close()'s return value entirely, so
// a buffered-write failure presented as "framecpp_sample exited 0,
// no output file" instead of as an exception. This is the root
// cause of issue #286 (and the linux_64 instance reported in #283).
//
// errno is captured immediately after close() because the underlying
// OS close() may set it. It may be stale (filebuf paths that fail
// in overflow may not have hit a syscall that updated errno), but
// when meaningful it pinpoints the actual kernel error.
//-----------------------------------------------------------------------
template < typename FB >
void
checked_close( FB* obuf, const std::string& filename, SimpleLogger* logger )
{
    errno = 0;
    FB* result = obuf->close( );
    if ( result == nullptr )
    {
        const int saved_errno = errno;
        std::ostringstream msg;
        msg << "file buffer close() returned nullptr for '" << filename
            << "' (final flush or OS close failed)";
        if ( saved_errno != 0 )
        {
            msg << "; errno=" << saved_errno
                << " (" << std::strerror( saved_errno ) << ")";
        }
        if ( logger )
        {
            logger_log_debug( logger, 10,
                "checked_close() CLOSE FAILED: ", msg.str( ) );
        }
        throw std::runtime_error( msg.str( ) );
    }
}

//=============================================================================
// FrameCPP Policy Class - Defines FrameCPP-specific behavior
//=============================================================================
struct FrameCPPPolicy
{
    //-------------------------------------------------------------------------
    // Type definitions
    //-------------------------------------------------------------------------
    using frame_type = boost::shared_ptr<FrameCPP::FrameH>;
    using logger_type = SimpleLogger*;

    //-------------------------------------------------------------------------
    /// \brief Get version description for command-line help
    //-------------------------------------------------------------------------
    static const char* getVersionDescription()
    {
        return "Frame specification version to use for the output frame (3-9)";
    }

    //-------------------------------------------------------------------------
    /// \brief Get program description for usage message
    //-------------------------------------------------------------------------
    static const char* getProgramDescription()
    {
        return "FrameCPP Sample - Test Frame Creator\n"
               "Create test frames using FrameCPP library (v3-v9)";
    }

    //-------------------------------------------------------------------------
    /// \brief Get example usage for help message
    //-------------------------------------------------------------------------
    static const char* getExampleUsage()
    {
        return "Examples:\n"
               "  # Full test frame (all structures):\n"
               "  framecpp_sample --test-frame-mode --version 7\n"
               "\n"
               "  # Minimal frames (single object, --test-frame-mode not required):\n"
               "  framecpp_sample --enable-detector --version 6\n"
               "  framecpp_sample --enable-history --with-history-msg \"My test\" --version 8\n"
               "  framecpp_sample --enable-fr-event --version 9\n";
    }

    //-------------------------------------------------------------------------
    /// \brief Add library-specific command-line options
    ///
    /// FrameCPP supports --disable-toc and --minimal-framecpp-frame options
    //-------------------------------------------------------------------------
    static void addSpecificOptions(
        boost::program_options::options_description& desc,
        Config& config)
    {
        // Notifier callback: set flags for minimal FrameCPP frame (FrHeader + FrEndOfFile only)
        auto set_minimal_framecpp = [&config](bool value) {
            if (value) {
                config.minimal_framecpp_frame = true;
                config.minimal_frame_mode = true;
                // Do NOT set enable_empty or any other enable_* flags
                // We want NO frame structures, just file header and EOF
            }
        };

        desc.add_options()
            ("disable-toc",
             po::bool_switch(&config.disable_toc),
             "Disable Table of Contents (TOC) generation in output frame file. "
             "Creates a frame file without FrTOC structure.")
            ("minimal-framecpp-frame",
             po::value<bool>(&config.minimal_framecpp_frame)
                ->default_value(false)->implicit_value(true)->notifier(set_minimal_framecpp),
             "Convenience option: Generate minimal FrameCPP frame for checksum validation testing "
             "(FrHeader + FrEndOfFile only, no FrameH). Equivalent to opening file and immediately closing.");
    }

    //-------------------------------------------------------------------------
    /// \brief Validate frame specification version for FrameCPP
    ///
    /// FrameCPP supports versions 3-9
    //-------------------------------------------------------------------------
    static bool validateVersion(int version, std::ostream& err)
    {
        if (version < 3 || version > 9)
        {
            err << "Error: FrameCPP library supports versions 3-9, requested version: "
                << version << "\n";
            return false;
        }
        return true;
    }

    //-------------------------------------------------------------------------
    /// \brief Validate configuration options for FrameCPP
    //-------------------------------------------------------------------------
    static bool validateOptions(const Config& /*config*/, std::ostream& /*err*/)
    {
        // FrameCPP supports all options
        return true;
    }

    //-------------------------------------------------------------------------
    /// \brief Get library-specific filename suffix
    //-------------------------------------------------------------------------
    static const char* getLibrarySuffix()
    {
        return "";  // No suffix for FrameCPP
    }

    //-------------------------------------------------------------------------
    /// \brief Minimal frame methods
    //-------------------------------------------------------------------------

    /// Create empty frame with only metadata
    static frame_type createEmptyFrame(int /*version*/, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrameH;

        logger_log_debug(logger, 20, "FrameCPPPolicy::createEmptyFrame() creating minimal frame");

        auto frame = boost::make_shared<FrameH>(
            "TestFrame",                              // name
            Config::RUN,                              // run (-1)
            0,                                        // frame number
            GPSTime(config.start_time, Config::GPS_NSEC),  // GPS time
            static_cast<double>(config.duration),     // dt (duration)
            0                                         // dataQuality
        );

        logger_log_debug(logger, 30, "FrameCPPPolicy::createEmptyFrame() frame created successfully");
        return frame;
    }

    /// Add FrDetector to frame
    static void addDetector(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrDetector;
        using namespace FrameSampleCommon::TestConstants;

        std::string det_name = config.detector_name.empty()
            ? DETECTOR_NAME
            : config.detector_name;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addDetector() adding detector: ", det_name);

        auto detector = boost::make_shared<FrDetector>(
            det_name,                    // name
            DETECTOR_PREFIX,             // prefix
            DETECTOR_LONGITUDE_D,        // longitudeD
            DETECTOR_LONGITUDE_M,        // longitudeM
            DETECTOR_LONGITUDE_S,        // longitudeS
            DETECTOR_LATITUDE_D,         // latitudeD
            DETECTOR_LATITUDE_M,         // latitudeM
            DETECTOR_LATITUDE_S,         // latitudeS
            DETECTOR_ELEVATION,          // elevation
            DETECTOR_ARM_X_AZIMUTH,      // armXazimuth
            DETECTOR_ARM_Y_AZIMUTH       // armYazimuth
        );

        frame->RefDetectProc().append(detector);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addDetector() detector added");
    }

    /// Add FrHistory to frame
    static void addHistory(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrHistory;
        using namespace FrameSampleCommon::TestConstants;

        std::string msg = config.history_msg.empty()
            ? HISTORY_COMMENT_FRAMECPP
            : config.history_msg;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addHistory() adding history");

        auto history = boost::make_shared<FrHistory>(
            HISTORY_NAME,
            config.start_time,
            msg
        );

        frame->RefHistory().append(history);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addHistory() history added");
    }

    /// Add FrEvent to frame
    static void addEvent(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrEvent;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addEvent() adding event");

        FrEvent::ParamList_type params;
        params.push_back(FrEvent::Param_type(EVENT_PARAM_NAME, EVENT_PARAM_VALUE));

        auto event = boost::make_shared<FrEvent>(
            EVENT_NAME,
            EVENT_COMMENT,
            EVENT_INPUTS,
            GPSTime(config.start_time, 0),
            EVENT_TIME_BEFORE,
            EVENT_TIME_AFTER,
            EVENT_STATUS,
            EVENT_AMPLITUDE,
            EVENT_PROBABILITY,
            EVENT_STATISTICS,
            params
        );

        frame->RefEvent().append(event);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addEvent() event added");
    }

    /// Add FrMsg to frame
    static void addMsg(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrMsg;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addMsg() adding message");

        // FrMsg requires FrRawData container
        if (!frame->GetRawData()) {
            auto rawData = boost::make_shared<FrameH::rawData_type::element_type>("RawData");
            frame->SetRawData(rawData);
        }

        auto msg = boost::make_shared<FrMsg>(
            MSG_ALARM,
            MSG_MESSAGE,
            MSG_SEVERITY,
            GPSTime(config.start_time, 0)
        );

        frame->GetRawData()->RefLogMsg().append(msg);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addMsg() message added");
    }

    /// Add FrSimEvent to frame
    static void addSimEvent(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrSimEvent;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addSimEvent() adding sim event");

        FrSimEvent::ParamList_type params;
        params.push_back(FrSimEvent::Param_type(SIMEVENT_PARAM_NAME, SIMEVENT_PARAM_VALUE));

        auto simEvent = boost::make_shared<FrSimEvent>(
            SIMEVENT_NAME,
            SIMEVENT_COMMENT,
            SIMEVENT_INPUTS,
            GPSTime(config.start_time, 0),
            SIMEVENT_TIME_BEFORE,
            SIMEVENT_TIME_AFTER,
            SIMEVENT_AMPLITUDE,
            params
        );

        frame->RefSimEvent().append(simEvent);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addSimEvent() sim event added");
    }

    /// Add FrSummary to frame
    static void addSummary(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrSummary;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addSummary() adding summary");

        auto summary = boost::make_shared<FrSummary>(
            SUMMARY_NAME,
            SUMMARY_COMMENT,
            SUMMARY_TEST,
            GPSTime(config.start_time, 0)
        );

        frame->RefSummaryData().append(summary);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addSummary() summary added");
    }

    /// Add FrTable to frame
    static void addTable(frame_type& frame, const Config& /*config*/, logger_type logger)
    {
        using FrameCPP::Universal::FrTable;
        using FrameCPP::Universal::FrVect;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addTable() adding table");

        auto table = boost::make_shared<FrTable>(TABLE_NAME, TABLE_NROWS);
        table->AppendComment(TABLE_COMMENT);

        // Column 1: INT_4S ramp 0-9
        {
            INT_4S data[TABLE_NROWS];
            fillTableColumn1(data, TABLE_NROWS);
            FrVect::dimension_type dim(TABLE_NROWS, 1.0);
            auto vect = boost::make_shared<FrVect>(TABLE_COLUMN1_NAME, 1, &dim, data, TABLE_COLUMN1_UNITS);
            table->RefColumn().append(vect);
        }

        // Column 2: REAL_4 constant 42.0
        {
            REAL_4 data[TABLE_NROWS];
            fillTableColumn2(data, TABLE_NROWS);
            FrVect::dimension_type dim(TABLE_NROWS, 1.0);
            auto vect = boost::make_shared<FrVect>(TABLE_COLUMN2_NAME, 1, &dim, data, TABLE_COLUMN2_UNITS);
            table->RefColumn().append(vect);
        }

        frame->RefAuxTable().append(table);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addTable() table added");
    }

    /// Add FrProcData to frame
    static void addProcData(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrProcData;
        using FrameCPP::Universal::FrVect;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addProcData() adding processed data");

        auto procData = boost::make_shared<FrProcData>(
            PROC_CHANNEL_NAME,
            PROC_COMMENT,
            PROC_TYPE,
            PROC_SUBTYPE,
            PROC_TIME_OFFSET,
            static_cast<double>(config.duration),  // tRange
            PROC_FSHIFT,
            PROC_PHASE,
            PROC_FRANGE,
            PROC_BW
        );

        // Create sine wave data: sin(i * 0.1)
        REAL_8 data[PROC_NUM_SAMPLES];
        fillProcData(data, PROC_NUM_SAMPLES);

        FrVect::dimension_type dim(PROC_NUM_SAMPLES, 1.0 / PROC_NUM_SAMPLES);
        auto vect = boost::make_shared<FrVect>("ProcData", 1, &dim, data, PROC_UNITS);
        procData->RefData().append(vect);

        frame->RefProcData().append(procData);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addProcData() processed data added");
    }

    /// Add FrSimData to frame
    static void addSimData(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrSimData;
        using FrameCPP::Universal::FrVect;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addSimData() adding simulated data");

        auto simData = boost::make_shared<FrSimData>(
            SIM_CHANNEL_NAME,
            SIM_COMMENT,
            SIM_TIME_OFFSET,
            SIM_FSHIFT,
            SIM_PHASE
        );

        // Create cosine wave data: cos(i * 0.1)
        REAL_4 data[SIM_NUM_SAMPLES];
        fillSimData(data, SIM_NUM_SAMPLES);

        FrVect::dimension_type dim(SIM_NUM_SAMPLES, 1.0 / SIM_NUM_SAMPLES);
        auto vect = boost::make_shared<FrVect>("SimData", 1, &dim, data, SIM_UNITS);
        simData->RefData().append(vect);

        frame->RefSimData().append(simData);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addSimData() simulated data added");
    }

    /// Add FrAdcData to frame
    static void addAdcData(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrAdcData;
        using FrameCPP::Universal::FrVect;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addAdcData() adding ADC data");

        // FrAdcData requires FrRawData container
        if (!frame->GetRawData()) {
            auto rawData = boost::make_shared<FrameH::rawData_type::element_type>("RawData");
            frame->SetRawData(rawData);
        }

        // Create ramp data: 0-99
        INT_2S data[ADC_NUM_SAMPLES];
        fillAdcData(data, ADC_NUM_SAMPLES);

        FrVect::dimension_type dim(ADC_NUM_SAMPLES, 1.0 / ADC_NUM_SAMPLES);
        auto vect = boost::make_shared<FrVect>("AdcData", 1, &dim, data, ADC_UNITS);

        auto adcData = boost::make_shared<FrAdcData>(
            ADC_CHANNEL_NAME,
            ADC_CHANNEL_GROUP,
            ADC_CHANNEL_NUMBER,
            ADC_NBITS,
            static_cast<double>(ADC_NUM_SAMPLES) / config.duration,  // sampleRate
            ADC_BIAS,
            ADC_SLOPE,
            ADC_UNITS,
            ADC_FSHIFT,
            ADC_TIME_OFFSET
        );

        adcData->RefData().append(vect);
        frame->GetRawData()->RefFirstAdc().append(adcData);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addAdcData() ADC data added");
    }

    /// Add FrSerData to frame
    static void addSerData(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrSerData;
        using FrameCPP::Universal::FrVect;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addSerData() adding serial data");

        // FrSerData requires FrRawData container
        if (!frame->GetRawData()) {
            auto rawData = boost::make_shared<FrameH::rawData_type::element_type>("RawData");
            frame->SetRawData(rawData);
        }

        // Create integer sequence data: 0-19
        INT_4U data[SER_NUM_SAMPLES];
        fillSerData(data, SER_NUM_SAMPLES);

        FrVect::dimension_type dim(SER_NUM_SAMPLES, 1.0 / SER_NUM_SAMPLES);
        auto vect = boost::make_shared<FrVect>("SerData", 1, &dim, data, SER_UNITS);

        auto serData = boost::make_shared<FrSerData>(
            SER_CHANNEL_NAME,
            frame->GetGTime(),
            static_cast<double>(SER_NUM_SAMPLES) / config.duration  // sampleRate
        );

        serData->RefSerial().append(vect);
        frame->GetRawData()->RefFirstSer().append(serData);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addSerData() serial data added");
    }

    /// Add FrRawData container test (ADC + SerData + Msg)
    static void addRawDataContainer(frame_type& frame, const Config& config, logger_type logger)
    {
        using FrameCPP::Universal::FrAdcData;
        using FrameCPP::Universal::FrSerData;
        using FrameCPP::Universal::FrMsg;
        using FrameCPP::Universal::FrVect;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addRawDataContainer() creating multi-object container");

        // FrRawData container required for all three object types
        if (!frame->GetRawData()) {
            auto rawData = boost::make_shared<FrameH::rawData_type::element_type>("RawData");
            frame->SetRawData(rawData);
        }

        // 1. FrAdcData: sine pattern
        {
            REAL_4 data[CONTAINER_ADC_NUM_SAMPLES];
            fillContainerAdcData(data, CONTAINER_ADC_NUM_SAMPLES);

            FrVect::dimension_type dim(CONTAINER_ADC_NUM_SAMPLES, 1.0 / CONTAINER_ADC_NUM_SAMPLES);
            auto vect = boost::make_shared<FrVect>("ContainerADC", 1, &dim, data, ADC_UNITS);

            auto adcData = boost::make_shared<FrAdcData>(
                CONTAINER_ADC_NAME,
                ADC_CHANNEL_GROUP,
                ADC_CHANNEL_NUMBER,
                CONTAINER_ADC_NBITS,
                static_cast<double>(CONTAINER_ADC_NUM_SAMPLES) / config.duration,
                ADC_BIAS,
                ADC_SLOPE,
                ADC_UNITS,
                ADC_FSHIFT,
                ADC_TIME_OFFSET
            );

            adcData->RefData().append(vect);
            frame->GetRawData()->RefFirstAdc().append(adcData);
            logger_log_debug(logger, 25, "  Added FrAdcData (", CONTAINER_ADC_NUM_SAMPLES, " samples, REAL_4, sine)");
        }

        // 2. FrSerData: integer sequence
        {
            INT_4U data[CONTAINER_SER_NUM_SAMPLES];
            fillContainerSerData(data, CONTAINER_SER_NUM_SAMPLES);

            FrVect::dimension_type dim(CONTAINER_SER_NUM_SAMPLES, 1.0 / CONTAINER_SER_NUM_SAMPLES);
            auto vect = boost::make_shared<FrVect>("ContainerSer", 1, &dim, data, SER_UNITS);

            auto serData = boost::make_shared<FrSerData>(
                CONTAINER_SER_NAME,
                frame->GetGTime(),
                static_cast<double>(CONTAINER_SER_NUM_SAMPLES) / config.duration
            );

            serData->RefSerial().append(vect);
            frame->GetRawData()->RefFirstSer().append(serData);
            logger_log_debug(logger, 25, "  Added FrSerData (", CONTAINER_SER_NUM_SAMPLES, " samples, INT_4U, sequence)");
        }

        // 3. FrMsg: Single message
        {
            auto msg = boost::make_shared<FrMsg>(
                CONTAINER_MSG_ALARM,
                CONTAINER_MSG_MESSAGE,
                CONTAINER_MSG_SEVERITY,
                GPSTime(config.start_time, 0)
            );

            frame->GetRawData()->RefLogMsg().append(msg);
            logger_log_debug(logger, 25, "  Added FrMsg ('", CONTAINER_MSG_MESSAGE, "')");
        }

        logger_log_debug(logger, 30, "FrameCPPPolicy::addRawDataContainer() container test complete");
    }

    /// Add FrStatData to frame (v3-v8 only, deprecated in v9)
    /// Note: FrStatData is written separately with WriteFrStatData() BEFORE WriteFrame()
    static void addStatData(frame_type& frame, const Config& config,
                           stat_data_container_type& stat_data, logger_type logger)
    {
        using FrameCPP::Universal::FrStatData;
        using FrameCPP::Universal::FrVect;
        using FrameCPP::Universal::FrDetector;
        using namespace FrameSampleCommon::TestConstants;

        logger_log_debug(logger, 20, "FrameCPPPolicy::addStatData() adding static data");

        // FrStatData requires a FrDetector
        if (frame->RefDetectProc().empty()) {
            std::string det_name = config.detector_name.empty()
                ? DETECTOR_NAME
                : config.detector_name;

            auto detector = boost::make_shared<FrDetector>(
                det_name,
                DETECTOR_PREFIX,
                DETECTOR_LONGITUDE_D,
                DETECTOR_LONGITUDE_M,
                DETECTOR_LONGITUDE_S,
                DETECTOR_LATITUDE_D,
                DETECTOR_LATITUDE_M,
                DETECTOR_LATITUDE_S,
                DETECTOR_ELEVATION,
                DETECTOR_ARM_X_AZIMUTH,
                DETECTOR_ARM_Y_AZIMUTH
            );
            frame->RefDetectProc().append(detector);
            logger_log_debug(logger, 25, "  Created FrDetector: ", det_name);
        }

        // Create FrStatData with standard test parameters
        auto statData = boost::make_shared<FrStatData>(
            STAT_NAME,
            STAT_COMMENT,
            STAT_REPRESENTATION,
            config.start_time,
            config.start_time + config.duration,
            STAT_VERSION
        );

        // Link to detector
        statData->SetDetector(frame->RefDetectProc()[0]);

        // Create simple FrVect data: constant 42.0 pattern
        REAL_4 data[STAT_NUM_SAMPLES];
        fillStatData(data, STAT_NUM_SAMPLES);

        FrVect::dimension_type dim(STAT_NUM_SAMPLES, 1.0);
        auto vect = boost::make_shared<FrVect>("StatData", 1, &dim, data, STAT_UNITS);
        statData->RefData().append(vect);

        // Add to stat_data container (will be written by WriteFrStatData in test_frame())
        stat_data.push_back(statData);
        logger_log_debug(logger, 30, "FrameCPPPolicy::addStatData() static data added to container");
    }
};

class CommandLine;

// The name of the program executable
// Note: Using vector instead of set for Boost.Program_options composing() support
typedef std::vector< std::string > channel_set_type;

typedef boost::shared_ptr< FrameH >     frame_h_type;
typedef boost::shared_ptr< FrAdcData >  fr_adc_data_type;
typedef boost::shared_ptr< FrRawData >  fr_raw_data_type;
typedef boost::shared_ptr< FrProcData > fr_proc_data_type;
typedef boost::shared_ptr< FrSerData >  fr_ser_data_type;
typedef boost::shared_ptr< FrSimData >  fr_sim_data_type;
typedef boost::shared_ptr< FrVect >     fr_vect_type;
typedef std::string                     detector_type;

BOOST_STRONG_TYPEDEF( INT_4U, version_type );

// Custom input operators for boost::program_options
// Note: These MUST be in namespace std for boost::lexical_cast to find them
namespace std
{
    ::std::istream&
    operator>>( ::std::istream& InputStream, GPSTime& StartTime )
    {
        INT_4U sec;

        InputStream >> sec;
        StartTime = GPSTime( sec, 0 );
        return InputStream;
    }

    ::std::istream&
    operator>>( ::std::istream& InputStream, channel_set_type& ChannelSet )
    {
        channel_set_type::value_type channel_name;

        InputStream >> channel_name;

        boost::to_lower( channel_name );
        ChannelSet.push_back( channel_name );

        return InputStream;
    }
} // namespace std

//=======================================================================
// Forward declaration of local functions
//=======================================================================

//-----------------------------------------------------------------------
static void all_type_frame( const Config&           config,
                            const channel_set_type& channel_adc_set,
                            const channel_set_type& channel_proc_set,
                            const channel_set_type& channel_ser_data_set,
                            const channel_set_type& channel_sim_data_set );

//-----------------------------------------------------------------------

template < class ChannelType >
static void all_type_iteration( const INT_4U            DeltaT,
                                const channel_set_type& ListOfChannels,
                                frame_h_type            Frame,
                                INT_4U*                 Channel,
                                INT_4U                  Iteration,
                                const Config&           config );

//-----------------------------------------------------------------------

template < class T >
static std::string
data_type( )
{
    return "";
}

//-----------------------------------------------------------------------

template < class T, class ChannelType >
static boost::shared_ptr< ChannelType >
ramped_channel( INT_4U             DeltaT,
                INT_4U*            Channel,
                INT_4U             Iteration,
                T                  Start,
                T                  Stop,
                T                  Inc,
                INT_4U             SampleRate,
                const Config&      config,
                const char*        IFO = "Z0" );

//-----------------------------------------------------------------------

template < class T >
static boost::shared_array< T >
ramp_data_create( INT_4U Samples, T Start, T Stop, T Inc );

//-----------------------------------------------------------------------

template < class T >
static string ramped_name( INT_4U Iteration, const char* IFO );

//-----------------------------------------------------------------------

template < class T >
static fr_vect_type ramped_vector( INT_4U      DeltaT,
                                   INT_4U      SampleRate,
                                   INT_4U      Iteration,
                                   T           Start,
                                   T           Stop,
                                   T           Inc,
                                   const char* IFO );

//-----------------------------------------------------------------------

void
test_frame( const Config& config )
{
    using FrameCPP::Common::FrameSpec;
    using Policy = FrameCPPPolicy;

    /// @brief Get the logger for framecpp_sample test_frame debugging
    /// @return Static logger instance (thread-safe, lazy initialization)
    static auto logger = []() {
        static SimpleLogger* instance = get_channel_logger("framecpp.sample.test_frame");
        return instance;
    }();

    logger_log_debug(logger, 10, "test_frame() entry, version=", config.version, ", minimal_mode=", config.minimal_frame_mode);

    //-------------------------------------------------------------------
    // Create the frame
    //-------------------------------------------------------------------
    logger_log_debug(logger, 20, "test_frame() creating stat_data container and frame pointer");
    stat_data_container_type stat_data;
    make_frame_ret_type      frame;

    // Choose frame generation strategy based on mode
    if (config.minimal_frame_mode)
    {
        // Minimal frame: create empty frame + add only requested objects
        logger_log_debug(logger, 20, "test_frame() minimal mode - creating empty frame");
        frame = Policy::createEmptyFrame(config.version, config, logger);
        logger_log_debug(logger, 30, "test_frame() empty frame created successfully");

        // Add objects based on --enable-* flags
        if (config.enable_detector)
        {
            logger_log_debug(logger, 20, "test_frame() adding detector (minimal mode)");
            Policy::addDetector(frame, config, logger);
        }
        if (config.enable_history)
        {
            logger_log_debug(logger, 20, "test_frame() adding history (minimal mode)");
            Policy::addHistory(frame, config, logger);
        }
        if (config.enable_fr_adc)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrAdcData (minimal mode)");
            Policy::addAdcData(frame, config, logger);
        }
        if (config.enable_fr_event)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrEvent (minimal mode)");
            Policy::addEvent(frame, config, logger);
        }
        if (config.enable_fr_msg)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrMsg (minimal mode)");
            Policy::addMsg(frame, config, logger);
        }
        if (config.enable_fr_proc_data)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrProcData (minimal mode)");
            Policy::addProcData(frame, config, logger);
        }
        if (config.enable_fr_ser_data)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrSerData (minimal mode)");
            Policy::addSerData(frame, config, logger);
        }
        if (config.enable_fr_sim_data)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrSimData (minimal mode)");
            Policy::addSimData(frame, config, logger);
        }
        if (config.enable_fr_sim_event)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrSimEvent (minimal mode)");
            Policy::addSimEvent(frame, config, logger);
        }
        if (config.enable_fr_stat_data)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrStatData (minimal mode, v3-v8 only)");
            Policy::addStatData(frame, config, stat_data, logger);
        }
        if (config.enable_fr_summary)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrSummary (minimal mode)");
            Policy::addSummary(frame, config, logger);
        }
        if (config.enable_fr_table)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrTable (minimal mode)");
            Policy::addTable(frame, config, logger);
        }
        if (config.enable_fr_rawdata)
        {
            logger_log_debug(logger, 20, "test_frame() adding FrRawData container test (minimal mode)");
            Policy::addRawDataContainer(frame, config, logger);
        }
    }
    else
    {
        // Full test frame: create frame with all structures (241 ADC channels)
        logger_log_debug(logger, 20, "test_frame() full mode - entering version switch, version=", config.version);
        switch ( config.version )
        {
        case 3:
            logger_log_debug(logger, 30, "test_frame() calling makeFrame<3>()");
            frame = makeFrame< 3 >( stat_data );
            logger_log_debug(logger, 30, "test_frame() makeFrame<3>() completed successfully, frame=", (frame ? "valid" : "null"));
            break;
        case 4:
            logger_log_debug(logger, 30, "test_frame() calling makeFrame<4>()");
            frame = makeFrame< 4 >( stat_data );
            logger_log_debug(logger, 30, "test_frame() makeFrame<4>() completed successfully");
            break;
        case 5:
            logger_log_debug(logger, 30, "test_frame() calling makeFrame<5>()");
            frame = makeFrame< 5 >( stat_data );
            logger_log_debug(logger, 30, "test_frame() makeFrame<5>() completed successfully");
            break;
        case 6:
            logger_log_debug(logger, 30, "test_frame() calling makeFrame<6>()");
            frame = makeFrame< 6 >( stat_data );
            logger_log_debug(logger, 30, "test_frame() makeFrame<6>() completed successfully");
            break;
        case 7:
            logger_log_debug(logger, 30, "test_frame() calling makeFrame<7>()");
            frame = makeFrame< 7 >( stat_data );
            logger_log_debug(logger, 30, "test_frame() makeFrame<7>() completed successfully");
            break;
        case 8:
            logger_log_debug(logger, 30, "test_frame() calling makeFrame<8>()");
            frame = makeFrame< 8 >( stat_data );
            logger_log_debug(logger, 30, "test_frame() makeFrame<8>() completed successfully");
            break;
        case 9:
            logger_log_debug(logger, 30, "test_frame() calling makeFrame<9>()");
            frame = makeFrame< 9 >( stat_data );
            logger_log_debug(logger, 30, "test_frame() makeFrame<9>() completed successfully");
            break;
        default:
        {
            std::ostringstream msg;
            logger_log_debug(logger, 10, "test_frame() ERROR: unsupported version=", config.version);

            msg << "name makeFrame case for version: " << config.version;
            throw std::runtime_error( msg.str( ) );
        }
        break;
        }
    }

    //-------------------------------------------------------------------
    // Calculate the filename
    //-------------------------------------------------------------------
    logger_log_debug(logger, 20, "test_frame() calculating filename");
    std::string filename = config.GetFilename(Policy::getLibrarySuffix(), config.start_time);

    logger_log_debug(logger, 30, "test_frame() filename generated: ", filename);

    logger_log_debug(logger, 20, "test_frame() validating frame pointer");
    if ( frame.get( ) == (FrameSpec::Object*)NULL )
    {
        logger_log_debug(logger, 10, "test_frame() CRITICAL ERROR: frame pointer is null");
        throw std::runtime_error( "Failed to create frame" );
    }
    logger_log_debug(logger, 30, "test_frame() frame pointer validation successful");

    //-------------------------------------------------------------------
    // Creation of io buffer.
    //-------------------------------------------------------------------
    logger_log_debug(logger, 20, "test_frame() creating FrameBuffer for output");
    FrameBuffer< filebuf >* obuf( new FrameBuffer< filebuf >( std::ios::out ) );
    logger_log_debug(logger, 30, "test_frame() FrameBuffer created successfully, obuf=", static_cast<void*>(obuf));
    
    try
    {
        //-----------------------------------------------------------------
        // Open the buffer and the stream
        //-----------------------------------------------------------------
        logger_log_debug(logger, 30, "test_frame() opening file buffer: ", filename);
        obuf->open( filename.c_str( ),
                    std::ios::out | std::ios::binary );
        logger_log_debug(logger, 30, "test_frame() file buffer opened successfully");

        logger_log_debug(logger, 30, "test_frame() creating OFrameStream with version=", config.version);
        OFrameStream ofs( obuf, config.version );
        logger_log_debug(logger, 30, "test_frame() OFrameStream created successfully");

        logger_log_debug(logger, 30, "test_frame() setting checksum to CRC");
        ofs.SetCheckSumFile( CheckSum::CRC );
        logger_log_debug(logger, 30, "test_frame() checksum set successfully");

        // Disable TOC if requested
        if ( config.disable_toc )
        {
            logger_log_debug(logger, 20, "test_frame() disabling TOC");
            ofs.DisableTOC( );
            logger_log_debug(logger, 30, "test_frame() TOC disabled successfully");
        }

        //-----------------------------------------------------------------
        // Write the FrStatData data to the stream
        //-----------------------------------------------------------------
        logger_log_debug(logger, 20, "test_frame() writing FrStatData, count=", stat_data.size());
        for ( stat_data_container_type::iterator cur = stat_data.begin( ),
                                                 last = stat_data.end( );
              cur != last;
              ++cur )
        {
            logger_log_debug(logger, 40, "test_frame() writing FrStatData item");
            ofs.WriteFrStatData( *cur );
            logger_log_debug(logger, 40, "test_frame() FrStatData item written successfully");
        }
        logger_log_debug(logger, 30, "test_frame() all FrStatData written successfully");

        //-----------------------------------------------------------------
        // Write the frame to the file
        //-----------------------------------------------------------------
        logger_log_debug(logger, 20, "test_frame() writing frame to file - THIS IS CRITICAL POINT");
        ofs.WriteFrame( frame, CheckSum::CRC );
        logger_log_debug(logger, 20, "test_frame() frame written successfully");
        
        //-----------------------------------------------------------------
        // Close the stream and file
        //-----------------------------------------------------------------
        logger_log_debug(logger, 20, "test_frame() closing stream and file");
        ofs.Close( );
        logger_log_debug(logger, 30, "test_frame() OFrameStream closed successfully");
        checked_close( obuf, filename, logger );
        logger_log_debug(logger, 30, "test_frame() file buffer closed successfully");

        //-----------------------------------------------------------------
        // Post-close belt-and-braces check (issue #286). The primary
        // signal of a failed write is checked_close() above; this
        // stat() is defence-in-depth that catches kernel-side write
        // failures that may surface asynchronously (e.g. a delayed
        // ENOSPC that filebuf::close() didn't see, or a filesystem
        // that lies about close success). It is cheap and catches a
        // class of bugs that the close() return value alone cannot.
        //-----------------------------------------------------------------
        {
            struct stat st;
            if ( ::stat( filename.c_str( ), &st ) != 0 )
            {
                const int saved_errno = errno;
                std::ostringstream msg;
                msg << "post-close stat() failed for output file '"
                    << filename << "': errno=" << saved_errno
                    << " (" << std::strerror( saved_errno ) << ")";
                logger_log_debug( logger, 10,
                    "test_frame() POST-CLOSE STAT FAILED: ", msg.str( ) );
                throw std::runtime_error( msg.str( ) );
            }
            if ( !S_ISREG( st.st_mode ) || st.st_size == 0 )
            {
                std::ostringstream msg;
                msg << "post-close output file is not a regular file or "
                    << "is zero-sized: '" << filename
                    << "' size=" << st.st_size
                    << " mode=0" << std::oct << st.st_mode << std::dec;
                logger_log_debug( logger, 10,
                    "test_frame() POST-CLOSE FILE INVALID: ", msg.str( ) );
                throw std::runtime_error( msg.str( ) );
            }
            logger_log_debug( logger, 30,
                "test_frame() post-close stat OK: ", filename,
                " size=", st.st_size );
        }
    }
    catch ( ... )
    {
        logger_log_debug(logger, 10, "test_frame() EXCEPTION CAUGHT during frame writing operations");
        logger_log_debug(logger, 50, "test_frame() closing file buffer due to exception");
        obuf->close( );
        // unlink( filename.c_str( ) );
        logger_log_debug(logger, 10, "test_frame() rethrowing exception after cleanup");
        throw;
    }

    //-------------------------------------------------------------------
    // Release resources associated with the FrStatData
    //-------------------------------------------------------------------
    logger_log_debug(logger, 20, "test_frame() cleaning up stat_data resources, count=", stat_data.size());
    stat_data.clear( );
    logger_log_debug(logger, 20, "test_frame() stat_data cleared successfully");

    logger_log_debug(logger, 10, "test_frame() completed successfully for version=", config.version);
}

//=======================================================================
// Note: Minimal frame generation is now handled by FrameCPPPolicy methods
// in conjunction with test_frame(const Config& config).
// See FrameCPPPolicy::createEmptyFrame() and FrameCPPPolicy::add*() methods.
//=======================================================================

//-----------------------------------------------------------------------
// Common tasks to be performed when exiting.
//-----------------------------------------------------------------------
inline void
depart( int ExitCode )
{
    /// @brief Get the logger for depart() function debugging
    /// @return Static logger instance (thread-safe, lazy initialization)
    static auto depart_logger = []() {
        static SimpleLogger* instance = get_channel_logger("framecpp.sample.depart");
        return instance;
    }();

    logger_log_debug(depart_logger, 10, "depart() called with ExitCode=", ExitCode);
    logger_log_debug(depart_logger, 20, "depart() about to call exit() - program termination");
    
    // CRITICAL FIX: Complete logging and static object cleanup before exit
    try {
        // Safe debug level check during program termination
        bool show_depart_debug = false;
        
        // First check the static variable set from command line
        if (g_command_line_debug_level >= 10) {
            show_depart_debug = true;
        }
        
        // Also check environment variable as fallback (in case not set via command line)
        if (!show_depart_debug) {
            const char* debug_env = std::getenv("FRAMECPP_DEBUG_LEVEL");
            if (debug_env && std::atoi(debug_env) >= 10) {
                show_depart_debug = true;
            }
        }
        
        if (show_depart_debug) {
            std::cerr << "[DEPART] Beginning comprehensive cleanup sequence" << std::endl;
            std::cerr.flush();
        }
        
        // Step 1: Flush all active logging
        boost::log::core::get()->flush();
        
        // Step 2: Remove all sinks to prevent static destruction issues
        boost::log::core::get()->remove_all_sinks();
        
        // Step 3: Reset the core to force cleanup of all internal structures
        boost::log::core::get()->reset_filter();
        
        if (show_depart_debug) {
            std::cerr << "[DEPART] Logging system shutdown complete, calling _Exit(" << ExitCode << ")" << std::endl;
            std::cerr.flush();
        }
        
        // Use _Exit() instead of exit() to bypass static destructors entirely
        // This prevents static destruction order issues that cause segfaults
        _Exit(ExitCode);
        
    } catch (...) {
        // Emergency exit - only show if debugging was explicitly requested
        const char* debug_env = std::getenv("FRAMECPP_DEBUG_LEVEL");
        if (debug_env && std::atoi(debug_env) >= 10) {
            std::cerr << "[DEPART] Exception during cleanup, using emergency exit" << std::endl;
            std::cerr.flush();
        }
        _Exit(ExitCode);
    }
}

//-----------------------------------------------------------------------
/// \brief Class to handle command line options for this application
//-----------------------------------------------------------------------
class CommandLine
{
public:
    typedef std::string history_msg_type;
    typedef bool        with_fr_event_type;
    typedef bool        with_fr_msg_type;
    typedef bool        with_fr_sim_event_type;
    typedef bool        with_fr_summary_type;
    typedef bool        with_fr_table_type;

    CommandLine( int ArgC, char** ArgV );

    inline const channel_set_type&
    ChannelsOfTypeProc( ) const
    {
        return channel_proc_set;
    }

    inline void
    ChannelsOfTypeProc( const std::string& Value )
    {
        channel_proc_set.push_back( Value );
    }

    inline const channel_set_type&
    ChannelsOfTypeSerData( ) const
    {
        return channel_ser_data_set;
    }

    inline const channel_set_type&
    ChannelsOfTypeSimData( ) const
    {
        return channel_sim_data_set;
    }

    inline const channel_set_type&
    ChannelType( ) const
    {
        return m_channel_set;
    }

    inline void
    ChannelType( const std::string& Value )
    {
        std::string channel_name( Value );

        boost::to_lower( channel_name );
        m_channel_set.push_back( channel_name );
    }

    inline const std::string&
    Description( ) const
    {
        return m_description;
    }

    inline void
    Description( const std::string& Value )
    {
        m_description = Value;
    }

    inline const detector_type&
    Detector( ) const
    {
        return detector;
    }

    inline INT_4U
    Duration( ) const
    {
        return m_duration;
    }

    inline void
    Duration( INT_4U Value )
    {
        m_duration = Value;
    }

    inline const history_msg_type&
    HistoryMsg( ) const
    {
        return ( history_msg );
    }

    inline INT_4U
    Iterations( ) const
    {
        return m_iterations;
    }

    inline void
    Iterations( INT_4U Value )
    {
        m_iterations = Value;
    }

    inline const std::string&
    ProgramName( ) const
    {
        return m_program_name;
    }

    inline const GPSTime&
    StartTime( ) const
    {
        return m_start_time;
    }

    inline void
    StartTime( const GPSTime& Value )
    {
        m_start_time = Value;
    }

    inline bool
    TestFrameMode( ) const
    {
        return m_test_frame_mode;
    }

    inline void
    TestFrameMode( bool Value )
    {
        m_test_frame_mode = Value;
    }

    inline version_type
    Version( ) const
    {
        return m_version;
    }

    inline void
    Version( version_type Value )
    {
        m_version = Value;
    }

    inline bool
    WithFrEvent( ) const
    {
        return ( with_fr_event );
    }

    inline bool
    WithFrMsg( ) const
    {
        return ( with_fr_msg );
    }

    inline bool
    WithFrSimEvent( ) const
    {
        return ( with_fr_sim_event );
    }

    inline bool
    WithFrSummary( ) const
    {
        return ( with_fr_summary );
    }

    inline bool
    WithFrTable( ) const
    {
        return ( with_fr_table );
    }

    inline bool
    UseFrameL( ) const
    {
        return use_framel;
    }

    inline bool
    DisableTOC( ) const
    {
        return disable_toc;
    }

    inline void
    Usage( int ExitValue ) const
    {
        std::cerr << "Usage: " << ProgramName( ) << std::endl
                  << m_options << std::endl;
        depart( ExitValue );
    }

private:
    po::options_description m_options;
    channel_set_type        m_channel_set;
    channel_set_type        channel_proc_set;
    channel_set_type        channel_ser_data_set;
    channel_set_type        channel_sim_data_set;
    std::string             m_description;
    detector_type           detector;
    INT_4U                  m_duration;
    history_msg_type        history_msg;
    INT_4U                  m_iterations;
    std::string             m_program_name;
    GPSTime                 m_start_time;
    bool                    m_test_frame_mode;
    version_type            m_version;
    with_fr_event_type      with_fr_event = false;
    with_fr_msg_type        with_fr_msg = false;
    with_fr_sim_event_type  with_fr_sim_event = false;
    with_fr_summary_type    with_fr_summary = false;
    with_fr_table_type      with_fr_table = false;
    bool                    use_framel = false;
    bool                    disable_toc = false;
};

CommandLine::CommandLine( int ArgC, char** ArgV )
    : m_description( "ilwd_test_frame" ), m_duration( 1 ), m_iterations( 1 ),
      m_start_time( GPSTime( 600000000, 0 ) ), m_test_frame_mode( false ),
      m_version( FRAME_SPEC_DEFAULT )
{
    po::options_description desc( "Options" );
    po::variables_map vm; // needed for debug options

    m_program_name.assign( ArgV[ 0 ] );
    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    m_options.add_options( ) //
        ( "help,h", "Print help messages" ) //
        ( "description",
          po::value< std::string >( &m_description )
              ->default_value( "ilwd_test_frame" ),
          "Specify the contents of the discription portion"
          " of the frame filename." ) //
        ( "channel-type",
          po::value< channel_set_type >( &m_channel_set ),
          "Add a channel type to the output frame."
          " This option may appear multiple times on the command line to"
          " specify multiple channel types." ) //
        ( "channel-proc-type",
          po::value< channel_set_type >( &channel_proc_set ),
          "Add a processed channel type to the output frame."
          " This option may appear multiple times on the command line to"
          " specify multiple processed channel types." ) //
        ( "channel-ser-data-type",
          po::value< channel_set_type >( &channel_ser_data_set ),
          "Add a serial data channel type to the output frame."
          " This option may appear multiple times on the command line to"
          " specify multiple processed channel types." ) //
        ( "channel-sim-data-type",
          po::value< channel_set_type >( &channel_sim_data_set ),
          "Add a simulated data channel type to the output frame."
          " This option may appear multiple times on the command line to"
          " specify multiple processed channel types." ) //
        ( "detector",
          po::value< std::string >( &detector )->default_value( "" ),
          "Add a detector structure to the frame." ) //
        ( "duration",
          po::value< INT_4U >( &m_duration )->default_value( 1 ),
          "The length of the frame in seconds." ) //
        ( "iterations",
          po::value< INT_4U >( &m_iterations )->default_value( 1 ),
          "Number of sets of channels to produce."
          " This can be used to inflate the size of a frame." ) //
        ( "history-msg",
          po::value< decltype( history_msg ) >( &history_msg )
              ->default_value( history_msg ),
          "Text of history record." ) //
        ( "start-time",
          po::value< GPSTime >( &m_start_time )
              ->default_value( GPSTime( 600000000, 0 ) ),
          "Number of sets of channels to produce."
          " This can be used to inflate the size of a frame." ) //
        ( "test-frame-mode",
          po::value< bool >( &m_test_frame_mode )
              ->default_value( false )
              ->implicit_value( true ),
          "Generate standard test frame."
          "Enable test frame mode" ) //
        ( "version",
          po::value< version_type >( &m_version )
              ->default_value( version_type( FRAME_SPEC_DEFAULT ) ),
          "Verion of the frame specification to use for the output frame." ) //
        ( "with-fr-event",
          po::bool_switch( &with_fr_event ),
          "Add a moch FrEvent structure to output frame." ) //
        ( "with-fr-msg",
          po::bool_switch( &with_fr_msg ),
          "Add a moch FrMsg structure to output frame." ) //
        ( "with-fr-sim-event",
          po::bool_switch( &with_fr_sim_event ),
          "Add a moch FrSimEvent structure to output frame." ) //
        ( "with-fr-summary",
          po::bool_switch( &with_fr_summary ),
          "Add a moch FrSummary structure to output frame." ) //
        ( "with-fr-table",
          po::bool_switch( &with_fr_table ),
          "Add a moch FrTable structure to the FrameH::uxTable of output "
          "frame." ) //
        ( "use-framel",
          po::bool_switch( &use_framel ),
          "Use FrameL library instead of FrameCPP for frame generation."
          " Only supports versions 3-8." ) //
        ( "disable-toc",
          po::bool_switch( &disable_toc ),
          "Disable Table of Contents (TOC) generation in output frame file."
          " Creates a frame file without FrTOC structure." ) //
        ;

    // Add debug options using FrameCPPDebugManager
    FrameCPP::Utilities::FrameCPPDebugManager::add_debug_options(m_options, vm);

    //---------------------------------------------------------------------
    // Parse the options specified on the command line
    //---------------------------------------------------------------------

    po::store(
        po::command_line_parser( ArgC, ArgV ).options( m_options ).run( ), vm );
    po::notify( vm );

    if ( vm.count( "help" ) )
    {
        Usage( 0 );
    }
}

//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// Static destructor debugging - this object will be destroyed FIRST
// and can help track when the destruction phase begins
class StaticDestructorTracker {
public:
    StaticDestructorTracker() {
        // Constructor - happens early, before most other statics
    }
    
    ~StaticDestructorTracker() {
        // This destructor should be called LAST (since constructed first)
        // Use direct write to stderr to avoid logger dependencies
        std::cerr << "[DESTRUCTOR] StaticDestructorTracker::~StaticDestructorTracker() - static destruction phase beginning" << std::endl;
        std::cerr.flush();
    }
};

// This static object will track when global destruction begins
static StaticDestructorTracker static_destruction_tracker;

int
main( int ArgC, char* ArgV[] )
try
{
    using Policy = FrameCPPPolicy;

    // Add early static destruction detector - will be destroyed late
    static class MainLateDestructor {
    public:
        ~MainLateDestructor() {
            std::cerr << "[DESTRUCTOR] main() late static destructor called" << std::endl;
            std::cerr.flush();
        }
    } late_destructor;

    MemChecker::Trigger gc_trigger( true );

    // Configuration structure
    Config config;

    // Command-line options
    po::options_description desc("Options");
    FrameSampleCommon::addCommonOptions<Policy>(desc, config);

    // Legacy channel customization options (from old all_type_frame mode)
    // These options trigger the all_type_frame() code path for customized channel generation
    channel_set_type channel_adc_set, channel_proc_set, channel_ser_data_set, channel_sim_data_set;

    // Notifier callback: automatically set use_all_type_frame when any channel option is used
    auto set_all_type_frame_mode = [&config](const channel_set_type& value) {
        if (!value.empty()) {
            config.use_all_type_frame = true;
        }
    };

    desc.add_options()
        ("channel-adc-type", po::value<channel_set_type>(&channel_adc_set)
            ->composing()
            ->notifier(set_all_type_frame_mode),
         "Specify FrAdcData channel types for customized frame generation (Z0: prefix). Can be repeated.")
        ("channel-proc-type", po::value<channel_set_type>(&channel_proc_set)
            ->composing()
            ->notifier(set_all_type_frame_mode),
         "Specify FrProcData channel types for customized frame generation. Can be repeated.")
        ("channel-ser-data-type", po::value<channel_set_type>(&channel_ser_data_set)
            ->composing()
            ->notifier(set_all_type_frame_mode),
         "Specify FrSerData channel types for customized frame generation. Can be repeated.")
        ("channel-sim-data-type", po::value<channel_set_type>(&channel_sim_data_set)
            ->composing()
            ->notifier(set_all_type_frame_mode),
         "Specify FrSimData channel types for customized frame generation. Can be repeated.");

    // Parse command-line arguments
    po::variables_map vm;
    po::store(po::parse_command_line(ArgC, ArgV, desc), vm);
    po::notify(vm);  // Notifiers automatically set config.minimal_frame_mode

    // Handle --help
    if (vm.count("help"))
    {
        FrameSampleCommon::printUsage<Policy>("framecpp_sample", desc);
        return 0;
    }

    // Initialize debug level manager with frameCPP-specific configuration
    if (!FrameCPP::Utilities::FrameCPPDebugManager::initialize(ArgC, ArgV, "framecpp_sample")) {
        return 0; // Help was requested and shown
    }

    // Store the global debug level for use during program termination
    // This allows depart() to check debug level even when static objects are being destroyed
    g_command_line_debug_level = FrameCPP::Utilities::FrameCPPDebugManager::get_global_level();

    FrameCPP::Initialize( );

    // Validate configuration
    if (!FrameSampleCommon::validateConfig<Policy>(config, std::cerr))
    {
        return 1;
    }

    /// @brief Get the logger for main() function debugging
    /// @return Static logger instance (thread-safe, lazy initialization)
    static auto main_logger = []() {
        static SimpleLogger* instance = get_channel_logger("framecpp.sample.main");
        return instance;
    }();

    // Default to test_frame_mode if no explicit mode was specified (backward compatibility)
    // This ensures CMakeLists.txt invocations work without modification
    if (!config.test_frame_mode && !config.minimal_frame_mode && !config.use_all_type_frame)
    {
        logger_log_debug(main_logger, 20, "main() no explicit mode specified, defaulting to test_frame_mode=true");
        config.test_frame_mode = true;
    }

    // Special handling for minimal-framecpp-frame: create file with ONLY FrHeader + FrEndOfFile
    // This bypasses normal frame generation (no FrameH structure)
    if (config.minimal_framecpp_frame)
    {
        std::cout << "Generating minimal FrameCPP file (FrHeader + FrEndOfFile only), version " << config.version << "\n";
        logger_log_debug(main_logger, 10, "main() minimal_framecpp_frame mode - creating bare minimum file");

        std::string filename = config.GetFilename("", config.start_time);
        logger_log_debug(main_logger, 20, "main() filename: ", filename);

        try
        {
            // Open file buffer
            FrameBuffer<filebuf>* obuf = new FrameBuffer<filebuf>(std::ios::out);
            obuf->open(filename.c_str(), std::ios::out | std::ios::binary);
            logger_log_debug(main_logger, 30, "main() file buffer opened");

            // Create OFrameStream (writes FrHeader automatically)
            OFrameStream ofs(obuf, config.version);
            logger_log_debug(main_logger, 30, "main() OFrameStream created, FrHeader written");

            // Set checksum type
            ofs.SetCheckSumFile(CheckSum::CRC);
            logger_log_debug(main_logger, 30, "main() checksum type set to CRC");

            // Close stream (writes FrEndOfFile automatically)
            logger_log_debug(main_logger, 20, "main() closing stream to write FrEndOfFile");
            ofs.Close();
            logger_log_debug(main_logger, 30, "main() OFrameStream closed, FrEndOfFile written");

            checked_close( obuf, filename, main_logger );
            logger_log_debug(main_logger, 30, "main() file buffer closed");

            std::cout << "  ✓ Successfully generated minimal file: " << filename << "\n";
            logger_log_debug(main_logger, 10, "main() minimal_framecpp_frame completed successfully");
        }
        catch (const std::exception& e)
        {
            logger_log_debug(main_logger, 10, "main() EXCEPTION during minimal_framecpp_frame: ", e.what());
            std::cerr << "Error generating minimal frame: " << e.what() << "\n";
            return 1;
        }

        logger_log_debug(main_logger, 10, "main() about to call depart(0) - normal exit");
        depart(0);
    }

    // Generate frame based on the selected mode
    if ( config.use_all_type_frame )
    {
        // all_type_frame mode with optional test_frame_mode (cumulative)
        if ( config.test_frame_mode )
        {
            std::cout << "Generating full test frame with additional custom channels, version " << config.version << "\n";
        }
        else
        {
            std::cout << "Generating customized frame with channel types, version " << config.version << "\n";
        }

        logger_log_debug(main_logger, 10, "main() calling all_type_frame() with version=", config.version, ", test_frame_mode=", config.test_frame_mode);
        all_type_frame(config, channel_adc_set, channel_proc_set, channel_ser_data_set, channel_sim_data_set);
        logger_log_debug(main_logger, 10, "main() all_type_frame() returned successfully");

        std::cout << "  ✓ Successfully generated frame\n";
    }
    else if ( config.test_frame_mode || config.minimal_frame_mode )
    {
        // Standard test frame or minimal frame with --enable-* flags
        std::cout << "Generating ";
        if (config.minimal_frame_mode) {
            std::cout << "minimal ";
        }
        std::cout << "test frame, version " << config.version << "\n";

        logger_log_debug(main_logger, 10, "main() calling test_frame() with version=", config.version);
        test_frame(config);
        logger_log_debug(main_logger, 10, "main() test_frame() returned successfully");

        std::cout << "  ✓ Successfully generated frame\n";
    }
    else
    {
        logger_log_debug(main_logger, 10, "main() no mode selected - this should not happen");
        std::cerr << "Error: No frame generation mode selected\n";
        std::cerr << "       Use --test-frame-mode, --enable-* flags, or channel-*-type options\n";
        return 1;
    }

    logger_log_debug(main_logger, 10, "main() about to call depart(0) - normal exit");
    depart( 0 );
}
catch ( const std::exception& e )
{
    /// @brief Get the logger for exception handling debugging  
    /// @return Static logger instance (thread-safe, lazy initialization)
    static auto exception_logger = []() {
        static SimpleLogger* instance = get_channel_logger("framecpp.sample.exception");
        return instance;
    }();

    logger_log_debug(exception_logger, 10, "main() caught std::exception: ", e.what());
    std::cerr << "FATAL: Caught exception: " << e.what( ) << std::endl;
    logger_log_debug(exception_logger, 10, "main() calling depart(1) due to exception");
    depart( 1 );
}
catch ( ... )
{
    /// @brief Get the logger for exception handling debugging  
    /// @return Static logger instance (thread-safe, lazy initialization)
    static auto exception_logger = []() {
        static SimpleLogger* instance = get_channel_logger("framecpp.sample.exception");
        return instance;
    }();

    logger_log_debug(exception_logger, 10, "main() caught unknown exception");
    std::cerr << "FATAL: Caught unknown exception: " << std::endl;
    logger_log_debug(exception_logger, 10, "main() calling depart(1) due to unknown exception");
    depart( 1 );
}

//=======================================================================
// Local functions
//=======================================================================

//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------
static void
all_type_frame( const Config&           config,
                const channel_set_type& channel_adc_set,
                const channel_set_type& channel_proc_set,
                const channel_set_type& channel_ser_data_set,
                const channel_set_type& channel_sim_data_set )
{
    //-------------------------------------------------------------------
    // Create filename
    //-------------------------------------------------------------------
    std::ostringstream filename;

    // Construct GPSTime from config
    GPSTime Start(config.start_time, Config::GPS_NSEC);

    filename << "Z-" << config.description << "-" << Start.GetSeconds( ) << "-" << config.duration
             << ".gwf";
    //-------------------------------------------------------------------
    // Open the buffer and the stream
    //-------------------------------------------------------------------
    FrameBuffer< filebuf >* obuf( new FrameBuffer< filebuf >( std::ios::out ) );
    obuf->open( filename.str( ).c_str( ), std::ios::out | std::ios::binary );

    // OFrameStream ofs( obuf, config.version, FRAME_SPEC_CURRENT );
    OFrameStream ofs( obuf, config.version );

    ofs.SetCheckSumFile( CheckSum::CRC );

    //-------------------------------------------------------------------
    // Generation of the frame
    //-------------------------------------------------------------------
    frame_h_type frame;
    stat_data_container_type stat_data;  // For test_frame_mode

    // If test_frame_mode is enabled, start with a full test frame
    if ( config.test_frame_mode )
    {
        // Generate full test frame with 241 ADC channels
        switch ( config.version )
        {
        case 3:
            frame = makeFrame< 3 >( stat_data );
            break;
        case 4:
            frame = makeFrame< 4 >( stat_data );
            break;
        case 5:
            frame = makeFrame< 5 >( stat_data );
            break;
        case 6:
            frame = makeFrame< 6 >( stat_data );
            break;
        case 7:
            frame = makeFrame< 7 >( stat_data );
            break;
        case 8:
            frame = makeFrame< 8 >( stat_data );
            break;
        case 9:
            frame = makeFrame< 9 >( stat_data );
            break;
        default:
        {
            std::ostringstream msg;
            msg << "No makeFrame case for version: " << config.version;
            throw std::runtime_error( msg.str( ) );
        }
        }
    }
    else
    {
        // Create minimal frame for custom channels only
        frame = frame_h_type( new FrameH(
            config.description, -1, 0, Start, Start.GetLeapSeconds( ), REAL_8( config.duration ) ) );

        {
            FrameH::rawData_type rawData(
                new FrameH::rawData_type::element_type( "RawData" ) );
            frame->SetRawData( rawData );
        }
    }

    //-------------------------------------------------------------------
    // Handle FrDetector
    //-------------------------------------------------------------------
    if ( config.detector_name.size( ) > 0 )
    {
        using FrameCPP::FrDetector;

        try
        {
            auto data_quality_offset =
                FrDetector::GetDataQualityOffset( config.detector_name );

            if ( data_quality_offset != FrDetector::DQO_UNSET )
            {
                auto detector = FrDetector::GetDetector( data_quality_offset );

                frame->RefDetectProc( ).append( detector );
            }

            switch ( data_quality_offset )
            {
            case FrDetector::DQO_UNSET:
            default:
                break;
            }
        }
        catch ( ... )
        {
            // Ignore erros
        }
    }

    //-------------------------------------------------------------------
    // Loop over the channel types for creation of the data sets
    //-------------------------------------------------------------------

    // FrAdcData channels
    if ( channel_adc_set.size( ) > 0 )
    {
        INT_4U channel = 0;
        all_type_iteration< FrAdcData >(
            config.duration,
            channel_adc_set,
            frame,
            &channel,
            1,  // iteration
            config );
    }

    // FrProcData channels
    if ( channel_proc_set.size( ) > 0 )
    {
        INT_4U channel = 0;
        all_type_iteration< FrProcData >(
            config.duration,
            channel_proc_set,
            frame,
            &channel,
            1,  // iteration
            config );
    }

    // FrSerData channels
    if ( channel_ser_data_set.size( ) > 0 )
    {
        INT_4U channel = 0;
        all_type_iteration< FrSerData >(
            config.duration,
            channel_ser_data_set,
            frame,
            &channel,
            1,  // iteration
            config );
    }

    // FrSimData channels
    if ( channel_sim_data_set.size( ) > 0 )
    {
        INT_4U channel = 0;
        all_type_iteration< FrSimData >(
            config.duration,
            channel_sim_data_set,
            frame,
            &channel,
            1,  // iteration
            config );
    }

    // Add history if requested
    if ( config.enable_history && config.history_msg.size( ) > 0 )
    {
        using FrameCPP::FrHistory;

        frame->RefHistory( ).append(
            FrHistory( "ALL_TYPE_FRAME_HISTORY_RECORD",
                       Start.GetSeconds( ),
                       config.history_msg ) );
    }

    // Add FrEvent if requested
    if ( config.enable_fr_event )
    {
        using FrameCPP::FrEvent;

        FrEvent::ParamList_type param_list;
        param_list.push_back( FrEvent::Param_type( "MOCH_PARAM_1", 42.0 ) );

        frame->RefEvent( ).append(
            FrEvent( "MOCK_FR_EVENT", // name
                     "This is a moch event for testing the frame "
                     "specification", // comment
                     "Z1:NON_CHANNEL", // inputs
                     Start, // GTime
                     5.0, // timeeBefore,
                     10.0, // timeAfter
                     12, // eventStatus
                     1.0, // amplitude
                     -1.0, // probability
                     "", // statistics
                     param_list // param
                     ) );
    }

    // Add FrMsg if requested
    if ( config.enable_fr_msg )
    {
        using FrameCPP::FrMsg;

        frame->GetRawData( )->RefLogMsg( ).append(
            FrMsg( "MOCK_FR_MSG", // alarm
                   "This is a moch event for testing the frame "
                   "specification", // message
                   5.0, // severity
                   Start // GTime
                   ) );
    }

    // Add FrSimEvent if requested
    if ( config.enable_fr_sim_event )
    {
        using FrameCPP::FrSimEvent;

        FrSimEvent::ParamList_type param_list;
        param_list.push_back(
            FrSimEvent::Param_type( "MOCH_SIM_PARAM_1", 32.0 ) );

        frame->RefSimEvent( ).append(
            FrSimEvent( "MOCK_FR_SIM_EVENT", // name
                        "This is a moch simulated event for testing the frame "
                        "specification being simulated", // comment
                        "Z1:NON_SIM_CHANNEL", // inputs
                        Start, // GTime
                        10.0, // timeeBefore,
                        5.0, // timeAfter
                        10.0, // amplitude
                        param_list // param
                        ) );
    }

    // Add FrSummary if requested
    if ( config.enable_fr_summary )
    {
        using FrameCPP::FrSummary;

        frame->RefSummaryData( ).append(
            FrSummary( "MOCK_FR_SUMMARY_DATA", // name
                       "This is a moch simulated summary for testing the frame "
                       "specification being simulated", // comment
                       "MOCH_STATISTICAL_TEST", // test
                       Start // GTime
                       ) );
    }

    // Add FrTable if requested
    if ( config.enable_fr_table )
    {
        using FrameCPP::FrTable;

        frame->RefAuxTable( ).append( FrTable( "MOCK_FR_TABLE_DATA", // name
                                               3 // Nrows
                                               ) );
        auto& table = *( frame->RefAuxTable( ).end( ) - 1 );
        table->AppendComment( "This is a moch table for testing the frame "
                              "specification being simulated" );

        {
            REAL_4 data[ 8 ] = { 1.0, 2.1, 3.2, 4.3, 5.4, 6.5, 7.6, 8.7 };
            FrameCPP::FrVect::dimension_type dims[ 1 ] = {
                FrameCPP::FrVect::dimension_type( 8, 1.0 )
            };
            FrameCPP::FrVect table_data( "OpticWeight", 1, dims, data, "kg" );
            table->RefColumn( ).append( table_data );
        }
        {
            INT_4U data[ 8 ] = {
                1988, 1989, 1993, 1997, 1998, 2004, 2007, 2019
            };
            FrameCPP::FrVect::dimension_type dims[ 1 ] = {
                FrameCPP::FrVect::dimension_type( 8, 1.0 )
            };
            FrameCPP::FrVect table_data(
                "YearOfManufactored", 1, dims, data, "year" );
            table->RefColumn( ).append( table_data );
        }
    }

    //-------------------------------------------------------------------
    // Writing of the frame
    //-------------------------------------------------------------------

    // Write FrStatData if we generated a full test frame
    if ( config.test_frame_mode && !stat_data.empty() )
    {
        for ( stat_data_container_type::iterator cur = stat_data.begin( ),
                                                 last = stat_data.end( );
              cur != last;
              ++cur )
        {
            ofs.WriteFrStatData( *cur );
        }
    }

    ofs.WriteFrame( frame );

    //-------------------------------------------------------------------
    // Close the frame and file stream
    //-------------------------------------------------------------------
    ofs.Close( );
    checked_close( obuf, filename.str( ), nullptr );

    // Clean up stat_data (shared_ptr will automatically release)
    stat_data.clear();
}

template < class ChannelType >
auto
get_channel_ifo( )
{
    if
        constexpr( std::is_same_v< ChannelType, FrAdcData > )
        {
            return "Z0";
        }
    else if
        constexpr( std::is_same_v< ChannelType, FrProcData > )
        {
            return "Z1";
        }
    else if
        constexpr( std::is_same_v< ChannelType, FrSimData > )
        {
            return "Z2";
        }
    else if
        constexpr( std::is_same_v< ChannelType, FrSerData > )
        {
            return "Z3";
        }
    throw( std::runtime_error( "get_channel_ifo: Unsupported ChannelType" ) );
}

template < class ChannelType >
auto&
get_channel_reference( frame_h_type Frame )
{
    if
        constexpr( std::is_same_v< ChannelType, FrAdcData > )
        {
            fr_raw_data_type rd( Frame->GetRawData( ) );

            if ( !rd )
            {
                throw std::range_error( "No FrRawData" );
            }
            return rd->RefFirstAdc( );
        }
    else if
        constexpr( std::is_same_v< ChannelType, FrProcData > )
        {
            return Frame->RefProcData( );
        }
    if
        constexpr( std::is_same_v< ChannelType, FrSerData > )
        {
            fr_raw_data_type rd( Frame->GetRawData( ) );

            if ( !rd )
            {
                throw std::range_error( "No FrRawData" );
            }
            return rd->RefFirstSer( );
        }
    else if
        constexpr( std::is_same_v< ChannelType, FrSimData > )
        {
            return Frame->RefSimData( );
        }
    else
    {
        static_assert( true, "get_channel_reference: Unsupported ChannelType" );
    }
}

//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------

template < class ChannelType >
static void
all_type_iteration( const INT_4U            DeltaT,
                    const channel_set_type& ListOfChannels,
                    frame_h_type            Frame,
                    INT_4U*                 Channel,
                    INT_4U                  Iteration,
                    const Config&           config )
try
{
    //-------------------------------------------------------------------
    // Obtain the channel list to be appended
    //-------------------------------------------------------------------
    auto& frame_channels = get_channel_reference< ChannelType >( Frame );
    auto  ifo = get_channel_ifo< ChannelType >( );
    //-------------------------------------------------------------------
    // Add the ramped channels
    //-------------------------------------------------------------------

    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "int_2u" ) != ListOfChannels.end() )
    {
        frame_channels.append(
            ramped_channel< INT_2U, ChannelType >( DeltaT,
                                                   Channel,
                                                   Iteration,
                                                   0,
                                                   512,
                                                   1,
                                                   512,
                                                   config,
                                                   ifo ) );
    }
    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "int_2s" ) != ListOfChannels.end() )
    {
        frame_channels.append(
            ramped_channel< INT_2S, ChannelType >( DeltaT,
                                                   Channel,
                                                   Iteration,
                                                   -512,
                                                   512,
                                                   1,
                                                   512,
                                                   config,
                                                   ifo ) );
    }
    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "int_4u" ) != ListOfChannels.end() )
    {
        frame_channels.append(
            ramped_channel< INT_4U, ChannelType >( DeltaT,
                                                   Channel,
                                                   Iteration,
                                                   0,
                                                   1024,
                                                   1,
                                                   1024,
                                                   config,
                                                   ifo ) );
    }
    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "int_4s" ) != ListOfChannels.end() )
    {

        frame_channels.append(
            ramped_channel< INT_4S, ChannelType >( DeltaT,
                                                   Channel,
                                                   Iteration,
                                                   -1024,
                                                   1024,
                                                   1,
                                                   1024,
                                                   config,
                                                   ifo ) );
    }
    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "int_8u" ) != ListOfChannels.end() )
    {
        frame_channels.append(
            ramped_channel< INT_8U, ChannelType >( DeltaT,
                                                   Channel,
                                                   Iteration,
                                                   0,
                                                   2048,
                                                   1,
                                                   1024,
                                                   config,
                                                   ifo ) );
    }
    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "int_8s" ) != ListOfChannels.end() )
    {
        frame_channels.append(
            ramped_channel< INT_8S, ChannelType >( DeltaT,
                                                   Channel,
                                                   Iteration,
                                                   -2048,
                                                   2048,
                                                   1,
                                                   1024,
                                                   config,
                                                   ifo ) );
    }

    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "real_4" ) != ListOfChannels.end() )
    {
        frame_channels.append(
            ramped_channel< REAL_4, ChannelType >( DeltaT,
                                                   Channel,
                                                   Iteration,
                                                   -1024,
                                                   1024,
                                                   0.5,
                                                   1024,
                                                   config,
                                                   ifo ) );
    }
    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "real_8" ) != ListOfChannels.end() )
    {
        frame_channels.append(
            ramped_channel< REAL_8, ChannelType >( DeltaT,
                                                   Channel,
                                                   Iteration,
                                                   -1024,
                                                   1024,
                                                   0.5,
                                                   1024,
                                                   config,
                                                   ifo ) );
    }

    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "complex_8" ) != ListOfChannels.end() )
    {
        frame_channels.append(
            ramped_channel< COMPLEX_8, ChannelType >( DeltaT,
                                                      Channel,
                                                      Iteration,
                                                      COMPLEX_8( -1024, 0 ),
                                                      COMPLEX_8( 1024, 0 ),
                                                      COMPLEX_8( 1, 0 ),
                                                      1024,
                                                      config,
                                                      ifo ) );
    }
    if ( std::find( ListOfChannels.begin(), ListOfChannels.end(), "complex_16" ) != ListOfChannels.end() )
    {
        frame_channels.append(
            ramped_channel< COMPLEX_16, ChannelType >( DeltaT,
                                                       Channel,
                                                       Iteration,
                                                       COMPLEX_16( -1024, 0 ),
                                                       COMPLEX_16( 1024, 0 ),
                                                       COMPLEX_16( 1, 0 ),
                                                       1024,
                                                       config,
                                                       ifo ) );
    }
}
catch ( ... )
{
    return;
}

#define DATA_TYPE( A )                                                         \
    template <>                                                                \
    std::string data_type< A >( )                                              \
    {                                                                          \
        return #A;                                                             \
    }

#if 0
//--------------------------
// Currently not being used
//--------------------------
DATA_TYPE(CHAR_U)
DATA_TYPE(CHAR)
#endif /* 0 */
DATA_TYPE( INT_2U )
DATA_TYPE( INT_2S )
DATA_TYPE( INT_4U )
DATA_TYPE( INT_4S )
DATA_TYPE( INT_8U )
DATA_TYPE( INT_8S )
DATA_TYPE( REAL_4 )
DATA_TYPE( REAL_8 )
DATA_TYPE( COMPLEX_8 )
DATA_TYPE( COMPLEX_16 )

#undef DATA_TYPE

//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------

template < class ChannelType >
boost::shared_ptr< ChannelType >
create_channel( const std::string& ChannelName,
                INT_4U*            Channel,
                INT_4U             SampleRate,
                const Config&      config )
{
    if
        constexpr( std::is_same_v< ChannelType, FrAdcData > )
        {
            //-------------------------------------------------------------------
            // Create the FrAdcData structure to hold the Adc metadata
            //-------------------------------------------------------------------
            boost::shared_ptr< FrAdcData > channel(
                new FrAdcData( ChannelName,
                               0, /* Group */
                               *Channel, /* Channel Number */
                               32, /* NBits */
                               SampleRate, /* sampleRate */
                               0.0, /* bias */
                               1.0, /* slope */
                               "counts", /* units */
                               0.0, /* fShift */
                               0.0, /* timeOffset */
                               0, /* dataValid */
                               0.0 /* phase */ ) );
            return channel;
        }
    else if
        constexpr( std::is_same_v< ChannelType, FrProcData > )
        {
            //-------------------------------------------------------------------
            // Create the FrProcData structure to hold the ProcData metadata
            //-------------------------------------------------------------------
            boost::shared_ptr< FrProcData > channel(
                new FrProcData( ChannelName,
                                "Moch up of an FrProcData structure",
                                FrProcData::TIME_SERIES, // type
                                FrProcData::UNKNOWN_SUB_TYPE, // subType
                                0.0, // timeOffset
                                1.0, // TRange
                                0.0, // FShift
                                0.0, // Phase
                                0.0, // FRange
                                0.0 // BW
                                ) );
            return channel;
        }
    else if
        constexpr( std::is_same_v< ChannelType, FrSerData > )
        {
            //-------------------------------------------------------------------
            // Create the FrSerData structure to hold the Ser data metadata
            //-------------------------------------------------------------------
            GPSTime start_time(config.start_time, Config::GPS_NSEC);
            boost::shared_ptr< FrSerData > channel(
                new FrSerData( ChannelName,
                               start_time, /* time */
                               SampleRate /* sampleRate */
                               ) );
            return channel;
        }
    else if
        constexpr( std::is_same_v< ChannelType, FrSimData > )
        {
            //-------------------------------------------------------------------
            // Create the FrAdcData structure to hold the Adc metadata
            //-------------------------------------------------------------------
            boost::shared_ptr< ChannelType > channel(
                new ChannelType( ChannelName,
                                 "Moch up of an FrSimData structure",
                                 SampleRate, /* sampleRate */
                                 0.0, /* fShift */
                                 0.0 /* phase */ ) );
            return channel;
        }
    throw( std::runtime_error( "create_channel: Unhandled type" ) );
}

//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------

template < class T, class ChannelType >
static boost::shared_ptr< ChannelType >
ramped_channel( INT_4U             DeltaT,
                INT_4U*            Channel,
                INT_4U             Iteration,
                T                  Start,
                T                  Stop,
                T                  Inc,
                INT_4U             SampleRate,
                const Config&      config,
                const char*        IFO )
{
    //-------------------------------------------------------------------
    // Create the channel structure to hold the metadata
    //-------------------------------------------------------------------
    auto channel_name = ramped_name< T >( Iteration, IFO );
    auto channel = create_channel< ChannelType >(
        channel_name, Channel, SampleRate, config );
    //-------------------------------------------------------------------
    // Link the data to the metadata
    //-------------------------------------------------------------------
    {
        fr_vect_type vec( ramped_vector< T >(
            DeltaT, SampleRate, Iteration, Start, Stop, Inc, IFO ) );

        if
            constexpr( std::is_same_v< ChannelType, FrSerData > )
            {
                channel->RefSerial( ).append( vec );
            }
        else
        {
            channel->RefData( ).append( vec );
        }
    }
    //-------------------------------------------------------------------
    // Incriment the channel number
    //-------------------------------------------------------------------
    ( *Channel )++;
    return channel;
}

//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------

template < class T >
static boost::shared_array< T >
ramp_data_create( INT_4U Samples, T Start, T Stop, T Inc )
{
    boost::shared_array< T > data( new T[ Samples ] );
    data[ 0 ] = Start;
    for ( INT_4U x = 1; x < Samples; x++ )
    {
        if ( data[ x - 1 ] == Stop )
        {
            data[ x ] = Start;
        }
        else
        {
            data[ x ] = data[ x - 1 ] + Inc;
        }
    }
    return data;
}

//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------

template < class T >
static string
ramped_name( INT_4U Iteration, const char* Prefix )
{
    ostringstream name;

    name << Prefix << ":RAMPED_" << data_type< T >( ) << "_" << Iteration;
    return name.str( );
}

//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------

template < class T >
static fr_vect_type
ramped_vector( INT_4U      DeltaT,
               INT_4U      SampleRate,
               INT_4U      Iteration,
               T           Start,
               T           Stop,
               T           Inc,
               const char* IFO )
{
    const INT_4U samples( DeltaT * SampleRate );

    FrVect::dimension_type   dim( samples );
    boost::shared_array< T > data(
        ramp_data_create< T >( samples, Start, Stop, Inc ) );

    fr_vect_type vect( new FrVect(
        ramped_name< T >( Iteration, IFO ), 1, &dim, data.get( ), "" /* unitY */
        ) );
    return vect;
}
