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

#include <framecpp_config.h>

#include <iostream>
#include <boost/make_unique.hpp>
#include <boost/shared_ptr.hpp>

#include "ldastoolsal/regex.hh"
#include "ldastoolsal/regexmatch.hh"
#include "ldastoolsal/Logger.hh"

#include "framecpp/Common/IOStream.hh"
#include "framecpp/Common/Description.hh"
#include "framecpp/Common/FrameSpec.tcc"

#include "framecpp/Universal/STRING.hh"

#include "framecpp/Universal/impl/FrameSpec.hh"

#include "framecpp/Universal/FrSE.hh"
#include "framecpp/Universal/FrSH.hh"
#include "framecpp/Universal/FrTOC.hh"

#include "../Common/FrTOCPrivate.hh"

using FrameCPP::Common::Description;
using FrameCPP::Common::FrameSpec;

//=======================================================================
// DRY Helper Templates moved to ClassicIO class for protected method access
//=======================================================================

//=======================================================================
// Static
//=======================================================================
//-----------------------------------------------------------------------
// Local functions and variables
//-----------------------------------------------------------------------

FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( FrTOCImpl,
                                      FSI_FR_TOC,
                                      "FrTOC",
                                      "Tabble of Contents Data Structure" )

namespace
{
    //-------------------------------------------------------------------
    /// @brief Encode string checksum
    ///
    /// @param[in] String
    ///   The source string
    /// @param[in,out] Checksum
    ///   The checksum buffer
    //-------------------------------------------------------------------
    inline void
    string_checksum( const std::string&             String,
                     FrameCPP::Common::CheckSumCRC& Checksum )
    {
        INT_2U size = String.size( );

        Checksum.calc( &size, sizeof( size ) );
        Checksum.calc( String.c_str( ), size + 1 );
    }

    //-------------------------------------------------------------------
    /// @brief Get logger for FrTOC debugging
    //-------------------------------------------------------------------
    inline SimpleLogger*
    logger()
    {
        static SimpleLogger* instance = get_channel_logger( "framecpp.frtoc" );
        return instance;
    }
} // namespace

namespace FrameCPP
{
    namespace Universal
    {
        //===================================================================
        // FrTOC
        //===================================================================

        const INT_4U FrTOC::NO_DATA_AVAILABLE = 0xFFFFFFFF;

        //-----------------------------------------------------------------------
        /// Loop over members of the table of contents to gather information
        /// requested by the caller.
        //-----------------------------------------------------------------------
        void
        FrTOC::ForEach( query_info_type Info, FunctionBase& Action ) const
        {
            switch ( Info )
            {
            case TOC_CHANNEL_NAMES:
                //-------------------------------------------------------------------
                /// \todo Need to finish the list of channel names
                //-------------------------------------------------------------------
                FrTOCAdcDataClassicIO::forEach( Info, Action );
                FrTOCProcDataClassicIO::forEach( Info, Action );
                FrTOCSimDataClassicIO::forEach( Info, Action );
                break;
            case TOC_DETECTOR:
                FrTOCDataClassicIO::forEach( Info, Action );
                break;
            case TOC_FR_STRUCTS:
                FrTOCDataClassicIO::forEach( Info, Action );
                break;
            }
        }

        void
        FrTOC::FrStatDataQuery( const std::string&         NamePattern,
                                const GPSTime&             StartTime,
                                const GPSTime&             EndTime,
                                const INT_4U               Version,
                                Common::FrStatData::Query& Result ) const
        {
            // Get StatData info map (will be empty for v9+ frames)
            const auto& stat_map = GetStat();

            // If no StatData in TOC (normal for v9), just return without error
            if (stat_map.empty())
            {
                return;
            }

            // Convert GPSTime to seconds for comparison
            INT_4U query_start = StartTime.GetSeconds();
            INT_4U query_end = EndTime.GetSeconds();

            // Compile regex pattern for name matching
            Regex name_regex(NamePattern);
            RegexMatch matcher;

            // Iterate over all StatData entries in the TOC
            for (const auto& stat_entry : stat_map)
            {
                const std::string& name = stat_entry.first;
                const auto& stat = stat_entry.second;

                // Check if name matches pattern
                if (!matcher.match(name_regex, name.c_str()))
                {
                    continue;
                }

                // Iterate over all instances of this StatData
                for (const auto& instance : stat.stat_instances)
                {
                    // Add matching instance to result set
                    // Result.Add() will handle time range and version filtering
                    Result.Add(query_start,
                               query_end,
                               Version,
                               name,
                               instance.tStart,
                               instance.tEnd,
                               instance.version,
                               instance.positionStat,
                               stat.detector);
                }
            }
        }

        void
        FrTOC::IndexObject( object_type Object, std::streampos Position )
        {
            using Common::TOCInfo;

            if ( !Object )
            {
                //---------------------------------------------------------------
                // Object holds nothing of interest
                //---------------------------------------------------------------
                return;
            }

            //-----------------------------------------------------------------
            // DEBUG: Log which object class is being indexed
            //-----------------------------------------------------------------
            {
                const char* class_name = "UNKNOWN";
                try
                {
                    const Common::Description* desc = Object->GetDescription( 9 );
                    if ( desc )
                    {
                        class_name = desc->GetName().c_str();
                    }
                }
                catch ( ... )
                {
                    // Ignore errors getting description
                }
                logger_log_debug( logger(), 8, "Indexing object class: ", class_name );
            }

            //-----------------------------------------------------------------
            // Identify object to look for specific information
            //-----------------------------------------------------------------
            try
            {
                //---------------------------------------------------------------
                // See if the object supports TOC querying.
                //---------------------------------------------------------------
                boost::shared_ptr< Common::TOCInfo > info(
                    boost::dynamic_pointer_cast< Common::TOCInfo >( Object ) );
                INT_4U frame_offset = ( m_dataQuality.size( ) <= 0 )
                    ? 0
                    : m_dataQuality.size( ) - 1;
                //---------------------------------------------------------------
                // Create object specific query
                //---------------------------------------------------------------
                switch ( Object->GetClass( ) )
                {
                case Common::FrameSpec::Info::FSI_FRAME_H:
                {
                    data_quality_type dq;
                    gtimes_type       gtimes;
                    gtimen_type       gtimen;
                    dt_type           dt;

                    info->TOCQuery( TOCInfo::IC_DATA_QUALITY,
                                    TOCInfo::DataType( dq ),
                                    &dq,

                                    TOCInfo::IC_GTIME_S,
                                    TOCInfo::DataType( gtimes ),
                                    &gtimes,

                                    TOCInfo::IC_GTIME_N,
                                    TOCInfo::DataType( gtimen ),
                                    &gtimen,

                                    TOCInfo::IC_DT,
                                    TOCInfo::DataType( dt ),
                                    &dt,

                                    TOCInfo::IC_EOQ );

                    m_dataQuality.push_back( dq );
                    m_GTimeS.push_back( gtimes );
                    m_GTimeN.push_back( gtimen );
                    m_dt.push_back( dt );
                    m_positionH.push_back( positionh_type( Position ) );
                }
                break;
                case Common::FrameSpec::Info::FSI_FR_DETECTOR:
                {
                    STRING name;

                    info->TOCQuery( TOCInfo::IC_NAME,
                                    TOCInfo::DataType( name ),
                                    &name,

                                    TOCInfo::IC_EOQ );

                    namedetector_container_type::const_iterator cur_detector =
                        m_nameDetector.begin( );
                    positiondetector_container_type::iterator cur_position =
                        m_positionDetector.begin( );
                    while ( cur_detector != m_nameDetector.end( ) )
                    {
                        if ( name == *cur_detector )
                        {
                            break;
                        }
                        ++cur_detector;
                        ++cur_position;
                    }
                    if ( cur_detector == m_nameDetector.end( ) )
                    {
                        m_nameDetector.push_back( name );
                        m_positionDetector.push_back(
                            positiondetector_type( Position ) );
                    }
                    else
                    {
                        if ( Position < std::streampos( *cur_position ) )
                        {
                            *cur_position = Position;
                        }
                    }
                }
                break;
                case Common::FrameSpec::Info::FSI_FR_ADC_DATA:
                    QueryAdc( *info, frame_offset, Position );
                    break;
                case Common::FrameSpec::Info::FSI_FR_EVENT:
                    QueryEvent( *info, frame_offset, Position );
                    break;
                case Common::FrameSpec::Info::FSI_FR_PROC_DATA:
                    QueryProc( *info, frame_offset, Position );
                    break;
                case Common::FrameSpec::Info::FSI_FR_SER_DATA:
                    QuerySer( *info, frame_offset, Position );
                    break;
                case Common::FrameSpec::Info::FSI_FR_SIM_DATA:
                    QuerySim( *info, frame_offset, Position );
                    break;
                case Common::FrameSpec::Info::FSI_FR_SIM_EVENT:
                    QuerySimEvent( *info, frame_offset, Position );
                    break;
                case Common::FrameSpec::Info::FSI_FR_STAT_DATA:
                    QueryStatData( *info, frame_offset, Position );
                    break;
                case Common::FrameSpec::Info::FSI_FR_SUMMARY:
                    QuerySum( *info, frame_offset, Position );
                    break;
                case Common::FrameSpec::Info::FSI_FR_SH:
                {
                    STRING    name;
                    shid_type id;

                    info->TOCQuery( TOCInfo::IC_NAME,
                                    TOCInfo::DataType( name ),
                                    &name,

                                    TOCInfo::IC_ID,
                                    TOCInfo::DataType( id ),
                                    &id,

                                    TOCInfo::IC_EOQ );

                    m_SHid.push_back( id );
                    m_SHname.push_back( name );
                }
                break;
                default:
                    //-------------------------------------------------------------
                    // Ignore all other objects
                    //-------------------------------------------------------------
                    break;
                }
            }
            catch ( ... )
            {
                //---------------------------------------------------------------
                // Quietly ignore any object that throws exception while
                //   trying to index
                //---------------------------------------------------------------
            }
        }

        FrTOC*
        FrTOC::Create( ) const
        {
            return new FrTOC;
        }

        const char*
        FrTOC::ObjectStructName( ) const
        {
            return StructName( );
        }

        bool
        FrTOC::operator==( const Common::FrameSpec::Object& RHS ) const
        {
            /// \todo
            ///   actually implement comparision operator
            throw std::runtime_error(
                "Comparison operator not supported for FrTOC" );
        }

        //-------------------------------------------------------------------
        // Interface routines
        //-------------------------------------------------------------------
        INT_4U
        FrTOC::nFrame( ) const
        {
            return GetNFrame( );
        }

        const FrTOC::cmn_dt_container_type&
        FrTOC::dt( ) const
        {
            return GetDt( );
        }

        const FrTOC::cmn_GTimeS_container_type&
        FrTOC::GTimeS( ) const
        {
            return GetGTimeS( );
        }

        const FrTOC::cmn_GTimeN_container_type&
        FrTOC::GTimeN( ) const
        {
            return GetGTimeN( );
        }

        FrTOC::cmn_runs_container_type const&
        FrTOC::runs( ) const
        {
            // Return m_runs data (empty for v9, populated for v<9)
            // This is version-agnostic - Load() handles version logic
            return m_runs;
        }

        void
        FrTOC::loadHeader( Common::IStream& Stream )
        {
            INT_4U nframe = 0; // Initialize for Load method
            FrTOCDataClassicIO::Load( Stream, nframe );
        }

        FrTOC::cmn_position_type
        FrTOC::positionDetector( const std::string& Name ) const
        {
            namedetector_container_type     nd( GetNameDetector( ) );
            positiondetector_container_type pd( GetPositionDetector( ) );

            namedetector_container_type::const_iterator     ind( nd.begin( ) );
            positiondetector_container_type::const_iterator ipd( pd.begin( ) );

            while ( ind != nd.end( ) )
            {
                if ( ind->compare( Name ) == 0 )
                {
                    return *ipd;
                    break;
                }
                ++ind;
                ++ipd;
            }
            std::ostringstream msg;

            msg << "Unable to locate any detector name " << Name
                << " within the table of contents for the stream";
            throw std::range_error( msg.str( ) );
        }

        FrTOC::cmn_position_type
        FrTOC::positionH( INT_4U FrameIndex ) const
        {
            const FrTOCDataData::positionh_container_type& positionh =
                GetPositionH( );

            if ( FrameIndex >= positionh.size( ) )
            {
                std::ostringstream msg;

                msg << "Request for frame " << FrameIndex
                    << " exceeds the range of 0 through "
                    << ( positionh.size( ) - 1 );
                throw std::out_of_range( msg.str( ) );
            }
            return positionh[ FrameIndex ];
        }

        const FrTOC::cmn_name_container_type&
        FrTOC::nameADC( ) const
        {
            return FrTOCImpl::FrTOCAdcDataData::m_keys;
        }

        FrTOC::cmn_position_type
        FrTOC::positionADC( INT_4U             FrameIndex,
                            const std::string& Channel ) const
        {
            return position_adc( FrameIndex, Channel );
        }

        FrTOC::cmn_position_type
        FrTOC::positionADC( INT_4U FrameIndex, INT_4U Channel ) const
        {
            return position_adc( FrameIndex, Channel );
        }

        FrTOC::cmn_position_type
        FrTOC::positionEvent( INT_4U             FrameIndex,
                              const std::string& Event ) const
        {
            return FrTOCEventData::positionEvent( FrameIndex, Event );
        }

        FrTOC::cmn_position_type
        FrTOC::positionEvent( const std::string& Event, INT_4U Index ) const
        {
            return FrTOCEventData::positionEvent( Event, Index );
        }

        const FrTOC::cmn_name_container_type&
        FrTOC::nameProc( ) const
        {
            return FrTOCImpl::FrTOCProcDataData::m_keys;
        }

        FrTOC::cmn_position_type
        FrTOC::positionProc( INT_4U             FrameIndex,
                             const std::string& Channel ) const
        {
            return position_proc( FrameIndex, Channel );
        }

        FrTOC::cmn_position_type
        FrTOC::positionProc( INT_4U FrameIndex, INT_4U Channel ) const
        {
            return position_proc( FrameIndex, Channel );
        }

        const FrTOC::cmn_name_container_type&
        FrTOC::nameSer( ) const
        {
            return FrTOCSerDataClassicIO::m_keys;
        }

        FrTOC::cmn_position_type
        FrTOC::positionSer( INT_4U             FrameIndex,
                            const std::string& Channel ) const
        {
            //-----------------------------------------------------------------
            // Ensure the FrameIndex is within range
            //-----------------------------------------------------------------
            if ( FrameIndex >= GetNFrame( ) )
            {
                std::ostringstream msg;

                msg << "Request for frame " << FrameIndex
                    << " exceeds the range of 0 through "
                    << ( GetNFrame( ) - 1 );
                throw std::out_of_range( msg.str( ) );
            }
            //-----------------------------------------------------------------
            // Try to find the channel
            //-----------------------------------------------------------------
            MapSer_type::const_iterator cur = GetSer( Channel );
            if ( cur == GetSer( ).end( ) )
            {
                //---------------------------------------------------------------
                // Channel name does not exist.
                //---------------------------------------------------------------
                std::ostringstream msg;

                msg << "No FrSerData structures with the name " << Channel;
                throw std::out_of_range( msg.str( ) );
            }
            //-----------------------------------------------------------------
            // Return the offset
            //-----------------------------------------------------------------
            return cur->second[ FrameIndex ];
        }

        const FrTOC::cmn_name_container_type&
        FrTOC::nameSim( ) const
        {
            return GetSimKeysByName( );
        }

        FrTOC::cmn_position_type
        FrTOC::positionSim( INT_4U             FrameIndex,
                            const std::string& Channel ) const
        {
            //-----------------------------------------------------------------
            // Ensure the FrameIndex is within range
            //-----------------------------------------------------------------
            if ( FrameIndex >= GetNFrame( ) )
            {
                std::ostringstream msg;

                msg << "Request for frame " << FrameIndex
                    << " exceeds the range of 0 through "
                    << ( GetNFrame( ) - 1 );
                throw std::out_of_range( msg.str( ) );
            }
            //-----------------------------------------------------------------
            // Try to find the channel
            //-----------------------------------------------------------------
            MapSim_type::const_iterator cur = GetSim( Channel );
            if ( cur == GetSim( ).end( ) )
            {
                //---------------------------------------------------------------
                // Channel name does not exist.
                //---------------------------------------------------------------
                std::ostringstream msg;

                msg << "No FrSimData structures with the name " << Channel;
                throw std::out_of_range( msg.str( ) );
            }
            //-----------------------------------------------------------------
            // Return the offset
            //-----------------------------------------------------------------
            return cur->second[ FrameIndex ];
        }

        FrTOC::cmn_position_type
        FrTOC::positionSimEvent( INT_4U             FrameIndex,
                                 const std::string& SimEvent ) const
        {
            return FrTOCSimEventData::positionSimEvent( FrameIndex, SimEvent );
        }

        FrTOC::cmn_position_type
        FrTOC::positionSimEvent( const std::string& EventType,
                                 INT_4U             Index ) const
        {
            return FrTOCSimEventData::positionSimEvent( EventType, Index );
        }

        INT_4U
        FrTOC::nSH( ) const
        {
            return m_SHid.size( );
        }

        INT_2U
        FrTOC::SHid( INT_4U Offset ) const
        {
            if ( Offset < m_SHid.size( ) )
            {
                return m_SHid[ Offset ];
            }
            std::ostringstream msg;
            msg << "The value of " << Offset << " exceeds the size ( "
                << m_SHid.size( ) << " ) of the SHid.";
            throw std::range_error( msg.str( ) );
        }

        const std::string&
        FrTOC::SHname( INT_4U Offset ) const
        {
            if ( Offset < m_SHname.size( ) )
            {
                return m_SHname[ Offset ];
            }
            std::ostringstream msg;
            msg << "The value of " << Offset << " exceeds the size ( "
                << m_SHname.size( ) << " ) of the SHname.";
            throw std::range_error( msg.str( ) );
        }

        template < typename ChannelType >
        FrTOC::cmn_position_type
        FrTOC::position_adc( INT_4U FrameIndex, ChannelType Channel ) const
        {
            //-----------------------------------------------------------------
            // Ensure the FrameIndex is within range
            //-----------------------------------------------------------------
            if ( FrameIndex >= GetNFrame( ) )
            {
                std::ostringstream msg;

                msg << "Request for frame " << FrameIndex
                    << " exceeds the range of 0 through "
                    << ( GetNFrame( ) - 1 );
                throw std::out_of_range( msg.str( ) );
            }
            //-----------------------------------------------------------------
            // Try to find the channel
            //-----------------------------------------------------------------
            MapADC_type::const_iterator cur = GetADC( Channel );
            if ( cur == GetADC( ).end( ) )
            {
                //---------------------------------------------------------------
                // Channel name does not exist.
                //---------------------------------------------------------------
                std::ostringstream msg;

                msg << "No FrAdcData structures with the name " << Channel;
                throw std::out_of_range( msg.str( ) );
            }
            //-----------------------------------------------------------------
            // Return the offset
            //-----------------------------------------------------------------
            return cur->second.m_positionADC[ FrameIndex ];
        }

        template < typename ChannelType >
        FrTOC::cmn_position_type
        FrTOC::position_proc( INT_4U FrameIndex, ChannelType Channel ) const
        {
            //-----------------------------------------------------------------
            // Ensure the FrameIndex is within range
            //-----------------------------------------------------------------
            if ( FrameIndex >= GetNFrame( ) )
            {
                std::ostringstream msg;

                msg << "Request for frame " << FrameIndex
                    << " exceeds the range of 0 through "
                    << ( GetNFrame( ) - 1 );
                throw std::out_of_range( msg.str( ) );
            }
            //-----------------------------------------------------------------
            // Try to find the channel
            //-----------------------------------------------------------------
            MapProc_type::const_iterator cur = GetProc( Channel );
            if ( cur == GetProc( ).end( ) )
            {
                //---------------------------------------------------------------
                // Channel name does not exist.
                //---------------------------------------------------------------
                std::ostringstream msg;

                msg << "No FrProcData structures with the name " << Channel;
                throw std::out_of_range( msg.str( ) );
            }
            //-----------------------------------------------------------------
            // Return the offset
            //-----------------------------------------------------------------
            return cur->second[ FrameIndex ];
        }

        const FrTOC&
        FrTOC::operator=( const FrTOC& Source )
        {
            FrTOCDataData::operator=( Source );
            // FrTOCStatData::    operator=( Source );
            FrTOCAdcDataData:: operator=( Source );
            FrTOCProcDataData::operator=( Source );
            FrTOCSimDataData:: operator=( Source );
            FrTOCSerDataData:: operator=( Source );
            FrTOCSummaryData:: operator=( Source );
            FrTOCEventData::   operator=( Source );
            FrTOCSimEventData::operator=( Source );

            m_StatType = Source.m_StatType;

            return *this;
        }

        void
        FrTOC::cacheAdcDataPositions( istream_type& Stream )
        {
            FrTOCAdcDataClassicIO::cachePositions( Stream );
        }

        void
        FrTOC::seekAdcDataPositions( istream_type&                  Stream,
                                     Common::FrTOC::channel_id_type Channel )
        {
            FrTOCAdcDataClassicIO::seekPositions( Stream, Channel );
        }

        void
        FrTOC::seekAdcDataPositions( istream_type&      Stream,
                                     const std::string& Channel )
        {
            FrTOCAdcDataClassicIO::seekPositions( Stream, Channel );
        }

        void
        FrTOC::procDataCachePositions( istream_type& Stream )
        {
            FrTOCProcDataClassicIO::cachePositions( Stream );
        }

        namespace FrTOCImpl
        {

            void
            FrTOCAdcDataClassicIO::Load( istream_type& Stream, INT_4U& nframe )
            {
                auto frame_stream_version = StreamVersion( Stream );

                logger_log_debug( logger( ),
                                  10,
                                  "FrTOCAdcData::Load() entry, stream version ",
                                  frame_stream_version,
                                  ", nframe=",
                                  nframe,
                                  " this=",
                                  static_cast<const void*>(this) );

                nadc_type nadc;
                Stream >> nadc;

                logger_log_debug(
                    logger( ), 20, "FrTOCAdcData::Load() read nadc=", nadc );

                if ( nadc && ( nadc != NO_DATA_AVAILABLE ) )
                {
                    typedef FrameCPP::Common::FrTOCAdcDataInputFunctor9<
                        FrTOCAdcData::MapADC_type,
                        FrTOCAdcData::name_type,
                        FrTOCAdcData::position_type >
                        functor;

                    //---------------------------------------------------------------
                    // Read in the information
                    //---------------------------------------------------------------
                    std::vector< name_type >::size_type s( nadc );

                    logger_log_debug(
                        logger( ),
                        30,
                        "FrTOCAdcData::Load() resizing keys array to ",
                        s,
                        " channels" );
                    m_keys.resize( s );
                    if ( positions_cache.size( ) != ( s * nframe ) )
                    {
                        logger_log_debug(
                            logger( ),
                            30,
                            "FrTOCAdcData::Load() resizing positions cache to ",
                            s * nframe,
                            " entries" );
                        positions_cache.resize( s * nframe );
                    }

                    logger_log_debug( logger( ),
                                      30,
                                      "FrTOCAdcData::Load() reading ",
                                      s,
                                      " channel names" );
                    for ( auto& key : m_keys )
                    {
                        Common::FrTOC::string_stream_type::Read( Stream, key );
                    }

                    // Read channelID and groupID arrays for v4-v8
                    std::vector< channel_id_type > channelIDs_cache;
                    std::vector< group_id_type > groupIDs_cache;
                    if ( frame_stream_version >= 4 && frame_stream_version < 9 )
                    {
                        channelIDs_cache.resize( s );
                        groupIDs_cache.resize( s );
                        logger_log_debug( logger( ),
                                          30,
                                          "FrTOCAdcData::Load() reading ",
                                          s,
                                          " channelID values" );
                        Stream >> channelIDs_cache;
                        logger_log_debug( logger( ),
                                          30,
                                          "FrTOCAdcData::Load() reading ",
                                          s,
                                          " groupID values" );
                        Stream >> groupIDs_cache;
                    }

                    //---------------------------------------------------------------
                    // Capture the starting position relative to the end of
                    // the file.
                    //---------------------------------------------------------------
                    positions_cache_offset = Stream.Size( ) - Stream.tellg( );
                    logger_log_debug( logger( ),
                                      30,
                                      "FrTOCAdcData::Load() reading ",
                                      positions_cache.size( ),
                                      " position values, cache offset=",
                                      positions_cache_offset );
                    Stream >> positions_cache;

                    //---------------------------------------------------------------
                    // Move into structure.
                    //---------------------------------------------------------------
                    functor f( m_info, positions_cache.begin( ), nframe );

                    std::for_each( m_keys.begin( ), m_keys.end( ), f );

                    // Populate channelID and groupID vectors for v4-v8
                    if ( frame_stream_version >= 4 && frame_stream_version < 9 )
                    {
                        logger_log_debug( logger( ),
                                          30,
                                          "FrTOCAdcData::Load() populating channelID/groupID vectors" );
                        for ( std::size_t i = 0; i < m_keys.size( ); ++i )
                        {
                            auto& adc_info = m_info[ m_keys[ i ] ];
                            // Replicate channelID and groupID across all frames (same value per frame)
                            adc_info.m_channelID.resize( nframe, channelIDs_cache[ i ] );
                            adc_info.m_groupID.resize( nframe, groupIDs_cache[ i ] );
                        }
                    }

                    logger_log_debug( logger( ),
                                      15,
                                      "FrTOCAdcData::Load() after functor, m_info.size()=",
                                      m_info.size(),
                                      " this=",
                                      static_cast<const void*>(this) );

                    logger_log_debug( logger( ),
                                      20,
                                      "FrTOCAdcData::Load() processed ",
                                      nadc,
                                      " ADC channels with ",
                                      nframe,
                                      " frames each" );
                }
                else
                {
                    logger_log_debug(
                        logger( ),
                        20,
                        "FrTOCAdcData::Load() no ADC data available (nadc=",
                        nadc,
                        ")" );
                }

                logger_log_debug(
                    logger( ), 10, "FrTOCAdcData::Load() complete" );
            }

        } // namespace FrTOCImpl
    } // namespace Universal
} // namespace FrameCPP

//=======================================================================
//               C L A S S I C   I O
//=======================================================================
namespace FrameCPP
{
    namespace Universal
    {
        namespace FrTOCImpl
        {
            template <>
            INT_4U
            ClassicIO< Universal::FrTOC >::GetTOCChecksum( )
            {
                Common::CheckSumCRC checksum;

                // nFrame, dt
                {
                    auto nFrame = GetNFrame( );

                    checksum.calc( &nFrame, sizeof( nFrame ) );

                    if ( nFrame > 0 )
                    {
                        auto dt = GetDt( );
                        checksum.calc( &dt[ 0 ],
                                       sizeof( dt_type ) * dt.size( ) );
                    }
                }
                if ( GetNFrame( ) > 0 )
                {
                    // nADC, name,
                    {
                        auto      names = GetADCKeysByName( );
                        nadc_type nADC = names.size( );

                        checksum.calc( &nADC, sizeof( nADC ) );
                        for ( auto name : names )
                        {
                            string_checksum( name, checksum );
                        }
                    }
                    // nProc, nameProc,
                    {
                        auto nameProcs = FrTOCProcDataClassicIO::GetKeys( );
                        nproc_type nProc = nameProcs.size( );

                        checksum.calc( &nProc, sizeof( nProc ) );
                        for ( auto nameProc : nameProcs )
                        {
                            string_checksum( nameProc, checksum );
                        }
                    }
                    // nSim, nameSim,
                    {
                        auto      sims = FrTOCSimDataClassicIO::GetSim( );
                        nsim_type nSim = sims.size( );

                        checksum.calc( &nSim, sizeof( nSim ) );
                        for ( auto sim : sims )
                        {
                            string_checksum( sim.first, checksum );
                        }
                    }
                    // nSer, nameSer,
                    {
                        auto sers = FrTOCSerDataClassicIO::GetSer( );
                        FrTOCSerDataClassicIO::n_type nSer = sers.size( );

                        checksum.calc( &nSer, sizeof( nSer ) );
                        for ( auto ser : sers )
                        {
                            string_checksum( ser.first, checksum );
                        }
                    }
                    // nSummary, nameSum,
                    {
                        auto summarys = FrTOCSummaryClassicIO::GetSummary( );
                        FrTOCSummaryClassicIO::n_type nSummary =
                            summarys.size( );

                        checksum.calc( &nSummary, sizeof( nSummary ) );
                        for ( auto summary : summarys )
                        {
                            string_checksum( summary.first, checksum );
                        }
                    }
                    // nEventType, nameEvent, nEvent, nTotalEvent,
                    {
                        FrTOCEventClassicIO::nEvent_type nEventType =
                            GetEvent( ).size( );
                        FrTOCEventClassicIO::nTotalEvent_type nTotalEvent = 0;

                        // nEventType
                        checksum.calc( &nEventType, sizeof( nEventType ) );
                        if ( nEventType > 0 )
                        {
                            // nameEvent
                            for ( auto event : GetEvent( ) )
                            {
                                string_checksum( event.first, checksum );
                            }
                            // nEvent
                            for ( auto event : GetEvent( ) )
                            {
                                const FrTOCEventClassicIO::nEvent_type nEvent =
                                    event.second.size( );

                                nTotalEvent += nEvent;
                                checksum.calc( &nEvent, sizeof( nEvent ) );
                            }
                        }
                        // nTotalEvent
                        checksum.calc( &nTotalEvent, sizeof( nTotalEvent ) );
                    }
                    // nSimEventType, nameSimEvent, nSimEvent, nTotalSEven
                    {
                        FrTOCSimEventClassicIO::nevent_type nSimEventType =
                            GetSimEvent( ).size( );
                        FrTOCSimEventClassicIO::nTotalSEvent_type nTotalSEvent =
                            0;

                        // nSimEventType
                        checksum.calc( &nSimEventType,
                                       sizeof( nSimEventType ) );
                        if ( nSimEventType > 0 )
                        {
                            // nameSimEvent
                            for ( auto event : GetSimEvent( ) )
                            {
                                string_checksum( event.first, checksum );
                            }
                            // nSimEvent
                            for ( auto event : GetSimEvent( ) )
                            {
                                const FrTOCSimEventClassicIO::nevent_type
                                    nSimEvent = event.second.size( );

                                nTotalSEvent += nSimEvent;
                                checksum.calc( &nSimEvent,
                                               sizeof( nSimEvent ) );
                            }
                        }
                        // nTotalSEvent
                        checksum.calc( &nTotalSEvent, sizeof( nTotalSEvent ) );
                    }
                }
                return ( checksum.value( ) );
            }

            template <>
            void
            ClassicIO< Universal::FrTOC >::fill_description(
                unsigned int         frame_stream_version,
                Common::Description& Desc ) const
            {
                // DRY approach using member helper function
                call_all_get_description_dry( frame_stream_version, Desc );
            }

            template <>
            const Description*
            ClassicIO< Universal::FrTOC >::GetDescription(
                unsigned int frame_stream_version ) const
            {
                using Common::Description;

                Description* description{ nullptr };

#if DEBUG
                std::cerr
                    << "DEBUG: FrTOC::GetDescription: frame_stream_version: "
                    << frame_stream_version << std::endl;
#endif /* DEBUG */

                if ( frame_stream_version >= 9 )
                {
                    static Description v9_description;

                    if ( v9_description.size( ) == 0 )
                    {
                        //-----------------------------------------------
                        v9_description( FrSH( Data::StructName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        // DRY approach using member helper function
                        call_all_get_description_dry( frame_stream_version,
                                                      v9_description );

                        v9_description( FrSE( "chkSum",
                                              CheckSumDataClass( ),
                                              CheckSumDataComment( ) ) );
                    }
                    description = &v9_description;
                }
                else if ( frame_stream_version >= 8 )
                {
                    static Description v8_description;

                    if ( v8_description.size( ) == 0 )
                    {
                        //-----------------------------------------------
                        v8_description( FrSH( Data::StructName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        // DRY approach using member helper function
                        call_all_get_description_dry( frame_stream_version,
                                                      v8_description );

                        v8_description( FrSE( "chkSum",
                                              CheckSumDataClass( ),
                                              CheckSumDataComment( ) ) );
                    }
                    description = &v8_description;
                }
                else if ( frame_stream_version >= 5 )
                {
                    static Description v5_description;

                    if ( v5_description.size( ) == 0 )
                    {
                        //-----------------------------------------------
                        v5_description( FrSH( Data::StructName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        // DRY approach using member helper function
                        call_all_get_description_dry( frame_stream_version,
                                                      v5_description );
                    }
                    description = &v5_description;
                }
                else if ( frame_stream_version >= 4 )
                {
                    static Description v4_description;

                    if ( v4_description.size( ) == 0 )
                    {
                        //-----------------------------------------------
                        v4_description( FrSH( Data::StructName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        // DRY approach using member helper function
                        call_all_get_description_dry( frame_stream_version,
                                                      v4_description );
                    }
                    description = &v4_description;
                }

                return description;
            }

            template <>
            Common::FrameSpec::size_type
            ClassicIO< Universal::FrTOC >::pBytes(
                const Common::StreamBase& Stream ) const
            {
                // DRY approach using member helper function
                return call_all_bytes_dry( Stream );
            }

            template <>
            ClassicIO< Universal::FrTOC >::self_type*
            ClassicIO< Universal::FrTOC >::pCreate( istream_type& Stream ) const
            {
                if ( StreamVersion( Stream ) < 4 )
                {
                    // Table of contents was introduced in version 4
                    return ( nullptr );
                }
                // FIXED: Avoid static logger to prevent segfaults from static
                // destructor ordering issues
                if ( false ) // Temporarily disabled to prevent static
                             // destructor issues
                {
                    std::cerr << "FrTOC::pCreate() ENTRY - About to create "
                                 "Universal::FrTOC object"
                              << std::endl;
                }

                auto obj = boost::make_unique< Universal::FrTOC >( );
                if ( false ) // Temporarily disabled to prevent static
                             // destructor issues
                {
                    std::cerr << "pCreate: FrTOC object created, address="
                              << static_cast< void* >( obj.get( ) )
                              << std::endl;

                    // Check initial state before loading data
                    std::cerr << "pCreate: Initial m_dataQuality.size()="
                              << obj->GetDataQuality( ).size( ) << std::endl;
                }
                // FIXED: Disabled static logger debug statements to prevent
                // segfaults

                // Cast both to FrTOCDataData to compare inheritance
                const FrTOCDataData* obj_data =
                    dynamic_cast< const FrTOCDataData* >( obj.get( ) );
                const FrTOCDataData* this_data =
                    static_cast< const FrTOCDataData* >( this );

                if ( obj_data )
                {
                    // FIXED: Disabled static logger debug statements to prevent
                    // segfaults
                    if ( false )
                    {
                        std::cerr << "pCreate: obj casts to FrTOCDataData at="
                                  << static_cast< const void* >( obj_data )
                                  << std::endl;
                        std::cerr << "pCreate: obj FrTOCDataData instance ID="
                                  << obj_data << std::endl;
                        std::cerr << "pCreate: obj m_dataQuality address="
                                  << static_cast< const void* >(
                                         &obj_data->GetDataQuality( ) )
                                  << std::endl;
                    }
                }
                else
                {
                    // FIXED: Disabled static logger debug statements to prevent
                    // segfaults
                    if ( false )
                    {
                        std::cerr << "pCreate: CRITICAL: obj does NOT cast to "
                                     "FrTOCDataData!"
                                  << std::endl;
                    }
                }

                // FIXED: Disabled static logger debug statements to prevent
                // segfaults
                if ( false )
                {
                    std::cerr << "pCreate: this casts to FrTOCDataData at="
                              << static_cast< const void* >( this_data )
                              << std::endl;
                    std::cerr << "pCreate: this FrTOCDataData instance ID="
                              << this_data << std::endl;
                    std::cerr << "pCreate: this m_dataQuality address="
                              << static_cast< const void* >(
                                     &this_data->GetDataQuality( ) )
                              << std::endl;
                }

                if ( obj_data && this_data && obj_data != this_data )
                {
                    // FIXED: Disabled static logger debug statements to prevent
                    // segfaults
                    if ( false )
                    {
                        std::cerr << "pCreate: ROOT CAUSE IDENTIFIED: obj "
                                     "FrTOCDataData != this FrTOCDataData!"
                                  << std::endl;
                        std::cerr << "pCreate: call_all_load_dry will Load() "
                                     "into 'this' but return 'obj'!"
                                  << std::endl;
                        std::cerr << "pCreate: This explains the complete data "
                                     "loss bug!"
                                  << std::endl;
                    }
                }

                // Load TOC data directly into obj (not into this!)
                // This is efficient - no copy needed since call_all_load_dry
                // now loads directly into the target object
                call_all_load_dry( obj.get( ), Stream );

                // Data is now loaded directly into obj - no copy needed!
                // FIXED: Disabled static logger debug statements to prevent
                // segfaults
                if ( false )
                {
                    std::cerr << "pCreate: POST-FIX VERIFICATION:" << std::endl;
                    std::cerr << "pCreate: obj->GetDataQuality().size()="
                              << obj->GetDataQuality( ).size( ) << std::endl;
                    std::cerr << "pCreate: this->GetDataQuality().size()="
                              << this_data->GetDataQuality( ).size( )
                              << std::endl;

                    if ( obj->GetDataQuality( ).size( ) > 0 )
                    {
                        std::cerr << "pCreate: SUCCESS: Fix worked! obj now "
                                     "contains loaded data."
                                  << std::endl;
                        std::cerr << "pCreate: obj->GetDataQuality()[0]="
                                  << obj->GetDataQuality( )[ 0 ] << std::endl;
                    }
                    else
                    {
                        std::cerr
                            << "pCreate: ERROR: Fix failed! obj is still empty."
                            << std::endl;
                    }

                    if ( obj->GetDataQuality( ).size( ) > 0 )
                    {
                        std::cerr << "pCreate: Data loaded successfully, first "
                                     "element="
                                  << obj->GetDataQuality( )[ 0 ] << std::endl;
                    }

                    std::cerr << "pCreate: About to release object ownership "
                                 "and return"
                              << std::endl;
                    std::cerr << "pCreate: Returning object address="
                              << static_cast< void* >( obj.get( ) )
                              << std::endl;
                }

                return ( obj.release( ) );
            }

            template <>
            void
            ClassicIO< Universal::FrTOC >::pWrite(
                Common::OStream& Stream ) const
            {
                if ( StreamVersion( Stream ) < 4 )
                {
                    // Table of contents was introduced in version 4
                    return;
                }
                // DRY approach using member helper function
                call_all_write_dry( Stream );
            }

        } // namespace FrTOCImpl
    } // namespace Universal
} // namespace FrameCPP
