//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2025 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/Common/Description.hh"
#include "framecpp/Common/TOCInfo.hh"
#include "framecpp/Common/FrameCPPServices.hh"

#include "framecpp/Universal/STRING.hh"

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

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

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

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

namespace FrameCPP
{
    namespace Universal
    {
        // Definition of static instance counter for debugging
        namespace FrTOCImpl {
            std::atomic<int> FrTOCDataData::s_instance_counter{0};

            // Implementation of FrTOCDataData static logger method
            SimpleLogger* FrTOCDataData::logger() {
                static bool first_call = true;
                if (first_call) {
                    // Logger initialization (output disabled for production)
                    print_call_stack();
                    first_call = false;
                }
                static SimpleLogger* instance = get_channel_logger("framecpp.frtoc.data");
                return instance;
            }

            // Implementation of FrTOCDataData dependency injection logger method
            SimpleLogger* FrTOCDataData::logger(const FrameCPP::Common::FrameCPPServices& services) {
                return services.logger_mgr.getLogger("framecpp.frtoc.data");
            }
        }
        namespace FrTOCImpl
        {
        } // namespace FrTOCImpl
    } // namespace Universal
} // namespace FrameCPP

//=======================================================================
//                                D A T A
//=======================================================================

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

//=======================================================================
//               C L A S S I C   I O
//=======================================================================
namespace FrameCPP
{
    namespace Universal
    {
        namespace FrTOCImpl
        {
            Common::FrameSpec::size_type
            FrTOCDataClassicIO::Bytes( const Common::StreamBase& Stream ) const
            {
                auto frame_stream_version = StreamVersion( Stream );

                logger_log_debug( logger( ),
                                  10,
                                  "FrTOCData::Bytes() entry, stream version ",
                                  frame_stream_version );

                Common::FrameSpec::size_type retval = 0;

                if ( frame_stream_version < 4 )
                {
                    logger_log_debug(
                        logger( ),
                        20,
                        "FrTOCData::Bytes() early return, version < 4" );
                    return retval;
                }
                //-----------------------------------------------------------------
                // main header block
                //-----------------------------------------------------------------
                retval += //
                    ( ( frame_stream_version < 9 ) //
                          ? sizeof( v4::uleaps_type )
                          : 0 ) //
                    + ( ( frame_stream_version >= 9 )
                            ? STRING::Bytes( fileBaseName )
                            : 0 ) //
                  + ( (frame_stream_version == 4 ) //
                      ? sizeof( v4::localTime_type ) //
                      : 0 )
                    + sizeof( nFrame_type ) //
                    + ( GetNFrame( ) *
                        ( ( (frame_stream_version >= 5) //
                            ? sizeof( data_quality_type ) : 0 )//
                          + sizeof( gtimes_type ) //
                          + sizeof( gtimen_type ) //
                          + sizeof( dt_type ) //
                          + ( ( frame_stream_version < 9 ) //
                                  ? ( sizeof( v4::runs_type ) //
                                      + sizeof( v4::frame_type ) ) //
                                  : 0 ) //
                          + sizeof( positionh_type ) //
                          + ( ( frame_stream_version < 9 ) //
                                  ? ( sizeof( v4::nfirstadc_type ) //
                                      + sizeof( v4::nfirstser_type ) //
                                      + sizeof( v4::nfirsttable_type ) //
                                      + sizeof( v4::nfirstmsg_type ) //
                                      ) //
                                  : 0 //
                              ) //
                          ) //
                        ) //
                    ;

                logger_log_debug( logger( ),
                                  30,
                                  "FrTOCData::Bytes() main header block: ",
                                  retval,
                                  " bytes, nFrames=",
                                  GetNFrame( ) );

                //-----------------------------------------------------------------
                // SH elements
                //-----------------------------------------------------------------
                retval += sizeof( nsh_type ) +
                    ( sizeof( shid_type ) * m_SHid.size( ) );
                for ( shname_container_type::const_iterator
                          cur = m_SHname.begin( ),
                          last = m_SHname.end( );
                      cur != last;
                      ++cur )
                {
                    retval += STRING::Bytes( *cur );
                }

                logger_log_debug( logger( ),
                                  30,
                                  "FrTOCData::Bytes() SH elements: nSH=",
                                  m_SHid.size( ),
                                  ", total bytes so far: ",
                                  retval );
                //-----------------------------------------------------------------
                // Detector information
                //-----------------------------------------------------------------
                if ( frame_stream_version >= 5 )
                {
                    retval += sizeof( ndetector_type ) +
                        ( sizeof( positiondetector_type ) *
                          m_positionDetector.size( ) );
                    for ( namedetector_container_type::const_iterator
                              cur = m_nameDetector.begin( ),
                              last = m_nameDetector.end( );
                          cur != last;
                          ++cur )
                    {
                        retval += STRING::Bytes( *cur );
                    }

                    logger_log_debug(
                        logger( ),
                        30,
                        "FrTOCData::Bytes() detector info: nDetector=",
                        m_positionDetector.size( ) );
                }
                //-----------------------------------------------------------------
                // Return the results
                //-----------------------------------------------------------------
                logger_log_debug( logger( ),
                                  10,
                                  "FrTOCData::Bytes() final result: ",
                                  retval,
                                  " bytes" );

                return retval;
            }

            void
            FrTOCDataClassicIO::GetDescription(
                unsigned int         frame_stream_version,
                Common::Description& Desc ) const
            {
                using Common::Description;

                if ( frame_stream_version >= 9 )
                {
                    Desc( FrSE(
                        "fileBaseName",
                        Description::Ascii< v9::file_base_name_type >( ),
                        "The name of the file at the time of creation"
                        " including its extension but excluding any"
                        " directory components. (ex: /a/b/c.gwf becomes c.gwf)"
                        " If there is no corresponding file with the stream,"
                        " the empty string will be stored." ) );
                }
                if ( frame_stream_version < 9 )
                {
                    Desc( FrSE( "ULeapS",
                                Description::Ascii< v4::uleaps_type >( ),
                                "From the first FrameH in the file." ) );
                }
                if ( frame_stream_version <= 4 )
                {
                    Desc( FrSE( "localTime",
                                Description::Ascii< v4::localTime_type >( ),
                                "From first FrameH in the file." ) );
                }
                Desc( FrSE( "nFrame",
                            Description::Ascii< v4::nFrame_type >( ),
                            "Number of frames in the file" ) );
                if ( frame_stream_version >= 5 )
                {
                  Desc( FrSE(
                             "dataQuality",
                             ( frame_stream_version >= 8 )
                             ? Description::AsciiArray< v5::data_quality_type >(
                                                                                "nFrame" )
                             .c_str( )
                             : Description::Ascii< v5::data_quality_type* >( ),
                             "Array of integer QA words from each FrameH"
                             " (size of nFrames)" ) );
                }
                Desc( FrSE(
                    "GTimeS",
                    ( frame_stream_version >= 8 )
                        ? Description::AsciiArray< v4::gtimes_type >( "nFrame" )
                              .c_str( )
                        : Description::Ascii< v4::gtimes_type* >( ),
                    "Array of integer GPS frae times (size of nFrames" ) );
                Desc( FrSE(
                    "GTimeN",
                    ( frame_stream_version >= 8 )
                        ? Description::AsciiArray< v4::gtimen_type >( "nFrame" )
                              .c_str( )
                        : Description::Ascii< v4::gtimen_type* >( ),
                    "Array of integer GPS residual nanoseconds for "
                    "the frame"
                    " (size of nFrame" ) );
                Desc( FrSE(
                    "dt",
                    ( frame_stream_version >= 8 )
                        ? Description::AsciiArray< v4::dt_type >( "nFrame" )
                              .c_str( )
                        : Description::Ascii< v4::dt_type* >( ),
                    "Array of frame durations in seconds( size of "
                    "nFrames)" ) );
                if ( frame_stream_version < 9 )
                {
                    Desc( FrSE( "runs",
                                ( frame_stream_version >= 8 )
                                    ? Description::AsciiArray< v4::runs_type >(
                                          "nFrame" )
                                          .c_str( )
                                    : Description::Ascii< v4::runs_type* >( ),
                                "Array of run numbers (size of nFrame)." ) );
                    Desc( FrSE( "frame",
                                ( frame_stream_version >= 8 )
                                    ? Description::AsciiArray< v4::frame_type >(
                                          "nFrame" )
                                          .c_str( )
                                    : Description::Ascii< v4::frame_type* >( ),
                                "Array of frame numbers (size of nFrame)." ) );
                }
                Desc( FrSE( "positionH",
                            ( frame_stream_version >= 8 )
                                ? Description::AsciiArray< v4::positionh_type >(
                                      "nFrame" )
                                      .c_str( )
                                : Description::Ascii< v4::positionh_type* >( ),
                            "Array of FrameH positions, in bytes, from the"
                            " beginning of file (size of nFrame)" ) );
                //-----------------------------------------------------------------
                if ( frame_stream_version < 9 )
                {
                    Desc( FrSE(
                        "nFirstADC",
                        ( frame_stream_version >= 8 )
                            ? Description::AsciiArray< v4::nfirstadc_type >(
                                  "nFrame" )
                                  .c_str( )
                            : Description::Ascii< v4::nfirstadc_type* >( ),
                        "Array of first FrADCData positions, in bytes, from the"
                        " beginning of file (size of nFrame)" ) );
                    Desc( FrSE(
                        "nFirstSer",
                        ( frame_stream_version >= 8 )
                            ? Description::AsciiArray< v4::nfirstser_type >(
                                  "nFrame" )
                                  .c_str( )
                            : Description::Ascii< v4::nfirstser_type* >( ),
                        "Array of first FrSerData positions, in bytes, from the"
                        " beginning of file (size of nFrame)" ) );
                    Desc( FrSE(
                        "nFirstTable",
                        ( frame_stream_version >= 8 )
                            ? Description::AsciiArray< v4::nfirsttable_type >(
                                  "nFrame" )
                                  .c_str( )
                            : Description::Ascii< v4::nfirsttable_type* >( ),
                        "Array of first FrTable positions, in bytes, from the"
                        " beginning of file (size of nFrame)" ) );
                    Desc( FrSE(
                        "nFirstMsg",
                        ( frame_stream_version >= 8 )
                            ? Description::AsciiArray< v4::nfirstmsg_type >(
                                  "nFrame" )
                                  .c_str( )
                            : Description::Ascii< v4::nfirstmsg_type* >( ),
                        "Array of first FrMsg positions, in bytes, from the"
                        " beginning of file (size of nFrame)" ) );
                }
                //-----------------------------------------------------------------
                Desc( FrSE( "nSH",
                            Description::Ascii< v4::nsh_type >( ),
                            "Number of FrSH structures in the file" ) );
                Desc( FrSE(
                    "SHid",
                    ( frame_stream_version >= 8 )
                        ? Description::AsciiArray< v4::shid_type >( "nSH" )
                              .c_str( )
                        : Description::Ascii< v4::shid_type* >( ),
                    "Array of FrSH IDs (size of nSH)" ) );
                Desc( FrSE(
                    "SHname",
                    ( frame_stream_version >= 8 )
                        ? Description::AsciiArray< v4::shname_type >( "nSH" )
                              .c_str( )
                        : Description::Ascii< v4::shname_type* >( ),
                    "Array of FrSH names (size of nSH)" ) );
                //-----------------------------------------------------------------
                if ( frame_stream_version >= 5 )
                {
                    Desc( FrSE( "nDetector",
                                Description::Ascii< v5::ndetector_type >( ),
                                "Number of distinct types of FrDetector in the "
                                "file" ) );
                    Desc( FrSE(
                        "nameDetector",
                        ( frame_stream_version >= 8 )
                            ? Description::AsciiArray< v5::namedetector_type >(
                                  "nDetector" )
                                  .c_str( )
                            : Description::Ascii< v5::namedetector_type* >( ),
                        "Array of FrDetector names (size of nDetector)."
                        " They appear alphabetically" ) );
                    Desc( FrSE(
                        "positionDetector",
                        ( frame_stream_version >= 8 )
                            ? Description::AsciiArray<
                                  v5::positiondetector_type >( "nDetector" )
                                  .c_str( )
                            : Description::Ascii<
                                  v5::positiondetector_type* >( ),
                        "Array of FrDetector positions from the beginning"
                        " of file (size of nDetector)." ) );
                }
            }

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

                logger_log_debug( logger( ),
                                  10,
                                  "FrTOCData::Load() entry, stream version ",
                                  frame_stream_version,
                                  " INSTANCE_ID=",
                                  m_instance_id );

                nFrame_type nframe_local;

                if ( frame_stream_version >= 9 )
                {
                    Common::FrTOC::string_stream_type::Read( Stream,
                                                             fileBaseName );
                    logger_log_debug( logger( ),
                                      30,
                                      "FrTOCData::Load() read fileBaseName: ",
                                      fileBaseName );
                }
                if ( frame_stream_version < 9 )
                {
                    v4::uleaps_type uleaps;
                    Stream >> uleaps;
                }
                if ( frame_stream_version == 4 )
                  {
                    v4::localTime_type localTime;
                    Stream >> localTime;
                  }
                Stream >> nframe_local;
                nframe = nframe_local; // Update the reference parameter

                logger_log_debug( logger( ),
                                  20,
                                  "FrTOCData::Load() read nFrame=",
                                  nframe_local );

                if ( nframe_local )
                {
                    if ( frame_stream_version >= 5 )
                    {
                        m_dataQuality.resize( nframe_local );
                    }
                    m_GTimeS.resize( nframe_local );
                    m_GTimeN.resize( nframe_local );
                    m_dt.resize( nframe_local );
                    m_positionH.resize( nframe_local );

                    logger_log_debug( logger( ),
                                      30,
                                      "FrTOCData::Load() resized arrays for ",
                                      nframe_local,
                                      " frames" );

                    if ( frame_stream_version >= 5 )
                    {
                        Stream >> m_dataQuality;
                    }
                    Stream //
                        >> m_GTimeS //
                        >> m_GTimeN //
                        >> m_dt //
                        ;

                    logger_log_debug(
                        logger( ),
                        30,
                        "FrTOCData::Load() read frame timing arrays" );
                    if ( frame_stream_version < 9 )
                    {
                        m_runs.resize( nframe_local );
                        std::vector< v4::frame_type > io_frame( nframe_local );

                        Stream //
                            >> m_runs //
                            >> io_frame //
                            ;

                        logger_log_debug(
                            logger( ),
                            20,
                            "FrTOCData::Load() stored ", m_runs.size(), " run numbers for v<9" );
                    }
                    Stream //
                        >> m_positionH //
                        ;
                    if ( frame_stream_version < 9 )
                    {
                        std::vector< v4::nfirstadc_type > nfirstadc(
                            nframe_local );
                        std::vector< v4::nfirstser_type > nfirstser(
                            nframe_local );
                        std::vector< v4::nfirsttable_type > nfirsttable(
                            nframe_local );
                        std::vector< v4::nfirstmsg_type > nfirstmsg(
                            nframe_local );

                        Stream >> nfirstadc >> nfirstser >> nfirsttable >>
                            nfirstmsg;
                    }
                }
                nsh_type nsh;
                Stream >> nsh;

                logger_log_debug(
                    logger( ), 20, "FrTOCData::Load() read nSH=", nsh );

                if ( nsh > 0 )
                {
                    m_SHid.resize( nsh );
                    std::vector< io_shname_type > io_SHname( nsh );
                    Stream >> m_SHid >> io_SHname;
                    m_SHname.resize( nsh );
                    std::copy( io_SHname.begin( ),
                               io_SHname.end( ),
                               m_SHname.begin( ) );

                    logger_log_debug( logger( ),
                                      30,
                                      "FrTOCData::Load() read ",
                                      nsh,
                                      " SH structures" );
                }
                if ( frame_stream_version >= 5 )
                {
                    ndetector_type ndetector;
                    Stream >> ndetector;

                    logger_log_debug( logger( ),
                                      20,
                                      "FrTOCData::Load() read nDetector=",
                                      ndetector );

                    if ( ndetector > 0 )
                    {
                        std::vector< io_namedetector_type > io_nameDetector{
                            ndetector
                        };
                        m_positionDetector.resize( ndetector );
                        Stream >> io_nameDetector >> m_positionDetector;
                        m_nameDetector.resize( ndetector );
                        std::copy( io_nameDetector.begin( ),
                                   io_nameDetector.end( ),
                                   m_nameDetector.begin( ) );

                        logger_log_debug( logger( ),
                                          30,
                                          "FrTOCData::Load() read ",
                                          ndetector,
                                          " detector entries" );
                    }
                }

                logger_log_debug( logger( ),
                                  10,
                                  "FrTOCData::Load() complete, nFrame=",
                                  nframe );

            }

            void
            FrTOCDataClassicIO::forEach(
                Common::FrTOC::query_info_type Info,
                Common::FrTOC::FunctionBase&   Action ) const
            {
                switch ( Info )
                {
                case Common::FrTOC::TOC_DETECTOR:
                {
                    try
                    {
                        Common::FrTOC::FunctionC& action(
                            dynamic_cast< Common::FrTOC::FunctionC& >(
                                Action ) );

                        for ( namedetector_container_type::const_iterator
                                  cur = GetNameDetector( ).begin( ),
                                  last = GetNameDetector( ).end( );
                              cur != last;
                              ++cur )
                        {
                            try
                            {
                                const Common::DetectorNames::info_type& info(
                                    DetectorNameTable.Detector( *cur ) );

                                action( info.s_prefix[ 0 ] );
                            }
                            catch ( ... )
                            {
                            }
                        }
                    }
                    catch ( ... )
                    {
                        // Does not understand Action
                    }
                }
                break;
                case Common::FrTOC::TOC_FR_STRUCTS:
                {
                    try
                    {
                        Common::FrTOC::FunctionSI& action(
                            dynamic_cast< Common::FrTOC::FunctionSI& >(
                                Action ) );
                        shid_container_type::const_iterator
                            cur_id = GetSHid( ).begin( ),
                            last_id = GetSHid( ).end( );
                        shname_container_type::const_iterator
                            cur_name = GetSHname( ).begin( ),
                            last_name = GetSHname( ).end( );
                        while ( ( cur_id != last_id ) &&
                                ( cur_name != last_name ) )
                        {
                            action( *cur_name, *cur_id );
                            ++cur_name;
                            ++cur_id;
                        }
                    }
                    catch ( ... )
                    {
                    }
                }
                break;
                default:
                    //---------------------------------------------------------------
                    // ignore all other requests
                    //---------------------------------------------------------------
                    break;
                }
            }

            void
            FrTOCDataClassicIO::write( Common::OStream& Stream ) const
            {
                auto frame_stream_version = StreamVersion( Stream );

                logger_log_debug( logger( ),
                                  10,
                                  "FrTOCData::write() entry, stream version ",
                                  frame_stream_version );

                std::vector< io_shname_type > io_shname( m_SHname.size( ) );
                std::copy(
                    m_SHname.begin( ), m_SHname.end( ), io_shname.begin( ) );

                if ( frame_stream_version >= 9 )
                {
                    STRING::Write( Stream, fileBaseName );
                    logger_log_debug( logger( ),
                                      30,
                                      "FrTOCData::write() wrote fileBaseName: ",
                                      fileBaseName );
                }
                if ( frame_stream_version < 9 )
                {
                    GPSTime start_time;
                    if ( GetNFrame( ) )
                    {
                        start_time = GPSTime( m_GTimeS[ 0 ], m_GTimeN[ 0 ] );
                    }
                    Stream << v4::uleaps_type( start_time.GetLeapSeconds( ) );
                }
                if ( frame_stream_version == 4 )
                  {
                    v4::localTime_type localTime( 0 );

                    Stream << localTime;
                  }
                //-------------------------------------------------------
                logger_log_debug( logger( ),
                                  20,
                                  "FrTOCData::write() writing ",
                                  GetNFrame( ),
                                  " frames of timing data" );

                Stream //
                    << nFrame_type( m_dataQuality.size( ) ) //
                    ;
                if ( frame_stream_version >= 5 )
                {
                    Stream //
                        << m_dataQuality //
                        ;
                }
                Stream //
                    << m_GTimeS //
                    << m_GTimeN //
                    << m_dt //
                    ;
                if ( frame_stream_version < 9 )
                {
                    auto const nframe{ GetNFrame( ) };

                    std::vector< v4::frame_type > io_frame;
                    io_frame.resize( nframe, 0 );

                    // Write m_runs data for v<9 compatibility
                    // If m_runs is empty (e.g., newly created TOC), write zeros
                    // If m_runs has data from Load(), write it as-is
                    if ( m_runs.size() == nframe )
                    {
                        Stream //
                            << m_runs //
                            << io_frame //
                            ;

                        logger_log_debug(
                            logger( ),
                            20,
                            "FrTOCData::Write() wrote ", m_runs.size(), " run numbers for v<9" );
                    }
                    else
                    {
                        // m_runs is wrong size or empty - write zeros for compatibility
                        std::vector< v4::runs_type > io_runs;
                        io_runs.resize( nframe, 0 );

                        Stream //
                            << io_runs //
                            << io_frame //
                            ;

                        logger_log_debug(
                            logger( ),
                            20,
                            "FrTOCData::Write() m_runs size mismatch (", m_runs.size(),
                            " vs ", nframe, "), wrote zeros for v<9" );
                    }
                }
                Stream //
                    << m_positionH //
                    ;
                //-------------------------------------------------------
                if ( frame_stream_version < 9 )
                {
                    auto const nframe{ GetNFrame( ) };

                    std::vector< v4::nfirstadc_type >   nfirstadc( nframe, 0 );
                    std::vector< v4::nfirstser_type >   nfirstser( nframe, 0 );
                    std::vector< v4::nfirsttable_type > nfirsttable( nframe,
                                                                     0 );
                    std::vector< v4::nfirstmsg_type >   nfirstmsg( nframe, 0 );

                    Stream //
                        << nfirstadc //
                        << nfirstser //
                        << nfirsttable //
                        << nfirstmsg //
                        ;
                }
                //-------------------------------------------------------
                logger_log_debug( logger( ),
                                  20,
                                  "FrTOCData::write() writing ",
                                  m_SHid.size( ),
                                  " SH structures" );

                Stream << nsh_type( m_SHid.size( ) )
                       /* FrSH info */
                       << m_SHid << io_shname;
                //-------------------------------------------------------
                if ( frame_stream_version >= 5 )
                {
                    /* FrDetector info */
                    std::vector< io_namedetector_type > io_nameDetector(
                        m_nameDetector.size( ) );
                    std::copy( m_nameDetector.begin( ),
                               m_nameDetector.end( ),
                               io_nameDetector.begin( ) );
                    logger_log_debug( logger( ),
                                      20,
                                      "FrTOCData::write() writing ",
                                      io_nameDetector.size( ),
                                      " detector entries" );

                    Stream << ndetector_type( io_nameDetector.size( ) )
                           << io_nameDetector << m_positionDetector;
                }

                logger_log_debug(
                    logger( ), 10, "FrTOCData::write() complete" );
            }

            // Implementation of ClassicIO dependency injection logger method
            template<typename T>
            SimpleLogger* ClassicIO<T>::logger(const FrameCPP::Common::FrameCPPServices& services) {
                return services.logger_mgr.getLogger("framecpp.frtoc.classicio");
            }

            // Explicit instantiation for FrTOC template usage
            template class ClassicIO<FrTOC>;

        } // namespace FrTOCImpl

        //====================================================================
        // Helper functions to flatten complex structures for FrSE compatibility
        //====================================================================

        // Event data helpers
        std::vector< std::string >
        FrTOC::GetNameEventArray( ) const
        {
            std::vector< std::string > result;
            result.reserve( GetEvent().size() );
            for ( const auto& [name, events] : GetEvent() )
            {
                result.push_back( name );
            }
            return result;
        }

        std::vector< INT_4U >
        FrTOC::GetNEventArray( ) const
        {
            std::vector< INT_4U > result;
            result.reserve( GetEvent().size() );
            for ( const auto& [name, events] : GetEvent() )
            {
                result.push_back( static_cast< INT_4U >( events.size() ) );
            }
            return result;
        }

        INT_4U
        FrTOC::GetNTotalEvent( ) const
        {
            INT_4U total = 0;
            for ( const auto& [name, events] : GetEvent() )
            {
                total += static_cast< INT_4U >( events.size() );
            }
            return total;
        }

        std::vector< INT_4U >
        FrTOC::GetGTimeSEventArray( ) const
        {
            std::vector< INT_4U > result;
            for ( const auto& [name, events] : GetEvent() )
            {
                for ( const auto& evt : events )
                {
                    result.push_back( evt.GTime.GetSeconds() );
                }
            }
            return result;
        }

        std::vector< INT_4U >
        FrTOC::GetGTimeNEventArray( ) const
        {
            std::vector< INT_4U > result;
            for ( const auto& [name, events] : GetEvent() )
            {
                for ( const auto& evt : events )
                {
                    result.push_back( evt.GTime.GetNanoseconds() );
                }
            }
            return result;
        }

        std::vector< REAL_4 >
        FrTOC::GetAmplitudeEventArray( ) const
        {
            std::vector< REAL_4 > result;
            for ( const auto& [name, events] : GetEvent() )
            {
                for ( const auto& evt : events )
                {
                    result.push_back( evt.amplitudeEvent );
                }
            }
            return result;
        }

        std::vector< INT_8U >
        FrTOC::GetPositionEventArray( ) const
        {
            std::vector< INT_8U > result;
            for ( const auto& [name, events] : GetEvent() )
            {
                for ( const auto& evt : events )
                {
                    result.push_back( evt.positionEvent );
                }
            }
            return result;
        }

        // SimEvent data helpers
        std::vector< std::string >
        FrTOC::GetNameSimEventArray( ) const
        {
            std::vector< std::string > result;
            result.reserve( GetSimEvent().size() );
            for ( const auto& [name, events] : GetSimEvent() )
            {
                result.push_back( name );
            }
            return result;
        }

        std::vector< INT_4U >
        FrTOC::GetNSimEventArray( ) const
        {
            std::vector< INT_4U > result;
            result.reserve( GetSimEvent().size() );
            for ( const auto& [name, events] : GetSimEvent() )
            {
                result.push_back( static_cast< INT_4U >( events.size() ) );
            }
            return result;
        }

        INT_4U
        FrTOC::GetNTotalSEvent( ) const
        {
            INT_4U total = 0;
            for ( const auto& [name, events] : GetSimEvent() )
            {
                total += static_cast< INT_4U >( events.size() );
            }
            return total;
        }

        std::vector< INT_4U >
        FrTOC::GetGTimeSSimArray( ) const
        {
            std::vector< INT_4U > result;
            for ( const auto& [name, events] : GetSimEvent() )
            {
                for ( const auto& evt : events )
                {
                    result.push_back( evt.GTime.GetSeconds() );
                }
            }
            return result;
        }

        std::vector< INT_4U >
        FrTOC::GetGTimeNSimArray( ) const
        {
            std::vector< INT_4U > result;
            for ( const auto& [name, events] : GetSimEvent() )
            {
                for ( const auto& evt : events )
                {
                    result.push_back( evt.GTime.GetNanoseconds() );
                }
            }
            return result;
        }

        std::vector< REAL_4 >
        FrTOC::GetAmplitudeSimEventArray( ) const
        {
            std::vector< REAL_4 > result;
            for ( const auto& [name, events] : GetSimEvent() )
            {
                for ( const auto& evt : events )
                {
                    result.push_back( evt.amplitudeSimEvent );
                }
            }
            return result;
        }

        std::vector< INT_8U >
        FrTOC::GetPositionSimEventArray( ) const
        {
            std::vector< INT_8U > result;
            for ( const auto& [name, events] : GetSimEvent() )
            {
                for ( const auto& evt : events )
                {
                    result.push_back( evt.positionSimEvent );
                }
            }
            return result;
        }

        // ADC data helpers
        std::vector< std::string >
        FrTOC::GetNameADCArray( ) const
        {
            std::vector< std::string > result;
            result.reserve( GetADC().size() );
            for ( const auto& [name, adc] : GetADC() )
            {
                result.push_back( name );
            }
            return result;
        }

        std::vector< INT_8U >
        FrTOC::GetPositionADCFlat( ) const
        {
            std::vector< INT_8U > result;
            for ( const auto& [name, adc] : GetADC() )
            {
                for ( const auto& pos : adc.m_positionADC )
                {
                    result.push_back( pos );
                }
            }
            return result;
        }

        // Proc data helpers
        std::vector< std::string >
        FrTOC::GetNameProcArray( ) const
        {
            std::vector< std::string > result;
            result.reserve( GetProc().size() );
            for ( const auto& [name, positions] : GetProc() )
            {
                result.push_back( name );
            }
            return result;
        }

        std::vector< INT_8U >
        FrTOC::GetPositionProcFlat( ) const
        {
            std::vector< INT_8U > result;
            for ( const auto& [name, positions] : GetProc() )
            {
                for ( const auto& pos : positions )
                {
                    result.push_back( pos );
                }
            }
            return result;
        }

        // Sim data helpers
        std::vector< std::string >
        FrTOC::GetNameSimArray( ) const
        {
            std::vector< std::string > result;
            result.reserve( GetSim().size() );
            for ( const auto& [name, positions] : GetSim() )
            {
                result.push_back( name );
            }
            return result;
        }

        std::vector< INT_8U >
        FrTOC::GetPositionSimFlat( ) const
        {
            std::vector< INT_8U > result;
            for ( const auto& [name, positions] : GetSim() )
            {
                for ( const auto& pos : positions )
                {
                    result.push_back( pos );
                }
            }
            return result;
        }

        // Ser data helpers
        std::vector< std::string >
        FrTOC::GetNameSerArray( ) const
        {
            std::vector< std::string > result;
            result.reserve( GetSer().size() );
            for ( const auto& [name, positions] : GetSer() )
            {
                result.push_back( name );
            }
            return result;
        }

        std::vector< INT_8U >
        FrTOC::GetPositionSerFlat( ) const
        {
            std::vector< INT_8U > result;
            for ( const auto& [name, positions] : GetSer() )
            {
                for ( const auto& pos : positions )
                {
                    result.push_back( pos );
                }
            }
            return result;
        }

        // Summary data helpers
        std::vector< std::string >
        FrTOC::GetNameSumArray( ) const
        {
            std::vector< std::string > result;
            result.reserve( GetSummary().size() );
            for ( const auto& [name, positions] : GetSummary() )
            {
                result.push_back( name );
            }
            return result;
        }

        std::vector< INT_8U >
        FrTOC::GetPositionSumFlat( ) const
        {
            std::vector< INT_8U > result;
            for ( const auto& [name, positions] : GetSummary() )
            {
                for ( const auto& pos : positions )
                {
                    result.push_back( pos );
                }
            }
            return result;
        }

        // StatData helpers (v<9 only)
        std::vector< std::string >
        FrTOC::GetNameStatArray( ) const
        {
            std::vector< std::string > result;
            result.reserve( m_StatType.size() );
            for ( const auto& [key, stat] : m_StatType )
            {
                result.push_back( key.s_name );
            }
            return result;
        }

        std::vector< std::string >
        FrTOC::GetDetectorStatArray( ) const
        {
            std::vector< std::string > result;
            result.reserve( m_StatType.size() );
            for ( const auto& [key, stat] : m_StatType )
            {
                result.push_back( key.s_detector );
            }
            return result;
        }

        std::vector< INT_4U >
        FrTOC::GetNStatInstanceArray( ) const
        {
            std::vector< INT_4U > result;
            result.reserve( m_StatType.size() );
            for ( const auto& [key, stat] : m_StatType )
            {
                result.push_back( static_cast< INT_4U >( stat.tStart.size() ) );
            }
            return result;
        }

        INT_4U
        FrTOC::GetNTotalStat( ) const
        {
            INT_4U total = 0;
            for ( const auto& [key, stat] : m_StatType )
            {
                total += static_cast< INT_4U >( stat.tStart.size() );
            }
            return total;
        }

        std::vector< INT_4U >
        FrTOC::GetTStartArray( ) const
        {
            std::vector< INT_4U > result;
            for ( const auto& [key, stat] : m_StatType )
            {
                for ( const auto& t : stat.tStart )
                {
                    result.push_back( t );
                }
            }
            return result;
        }

        std::vector< INT_4U >
        FrTOC::GetTEndArray( ) const
        {
            std::vector< INT_4U > result;
            for ( const auto& [key, stat] : m_StatType )
            {
                for ( const auto& t : stat.tEnd )
                {
                    result.push_back( t );
                }
            }
            return result;
        }

        std::vector< INT_4U >
        FrTOC::GetVersionStatArray( ) const
        {
            std::vector< INT_4U > result;
            for ( const auto& [key, stat] : m_StatType )
            {
                for ( const auto& v : stat.version )
                {
                    result.push_back( v );
                }
            }
            return result;
        }

        std::vector< INT_8U >
        FrTOC::GetPositionStatArray( ) const
        {
            std::vector< INT_8U > result;
            for ( const auto& [key, stat] : m_StatType )
            {
                for ( const auto& pos : stat.positionStat )
                {
                    result.push_back( pos );
                }
            }
            return result;
        }

        // Detector/SH helpers
        std::vector< std::string >
        FrTOC::GetNameDetectorArray( ) const
        {
            return GetNameDetector();
        }

        std::vector< INT_8U >
        FrTOC::GetPositionDetectorArray( ) const
        {
            return GetPositionDetector();
        }

        std::vector< INT_2U >
        FrTOC::GetSHidArray( ) const
        {
            return GetSHid();
        }

        std::vector< std::string >
        FrTOC::GetSHnameArray( ) const
        {
            return GetSHname();
        }
    } // namespace Universal
} // namespace FrameCPP
