//
// LDASTools frameCPP - Frame Inspection Utility with Flexible Input
// Architecture
//
// 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.
//

// Suppress false positive from GCC 14.3+ about uninitialized std::function members
// This occurs in <regex> implementation which uses std::function internally
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif

#include <iostream>
#include <fstream>
#include <memory>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include <stdexcept>
#include <sstream>
#include <cxxabi.h>
#include <regex>

// Boost libraries
#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>

// FrameCPP includes
#include "framecpp/FrameCPP.hh"
#include "framecpp/FrameCPPDebugManager.hh"
#include "framecpp/Common/FrameCPPServices.hh"
#include "framecpp/Common/StreamRefInterface.hh"
#include "framecpp/Common/SequentialFrameReader.hh"
#include "framecpp/IFrameStream.hh"
#include "framecpp/FrameDebugPrint.hh"

// Frame object includes
#include "framecpp/FrAdcData.hh"
#include "framecpp/FrDetector.hh"
#include "framecpp/FrEndOfFile.hh"
#include "framecpp/FrEndOfFrame.hh"
#include "framecpp/FrHeader.hh"
#include "framecpp/FrEvent.hh"
#include "framecpp/FrHistory.hh"
#include "framecpp/FrMsg.hh"
#include "framecpp/FrameH.hh"
#include "framecpp/FrProcData.hh"
#include "framecpp/FrRawData.hh"
#include "framecpp/FrSerData.hh"
#include "framecpp/FrSimData.hh"
#include "framecpp/FrSimEvent.hh"
#include "framecpp/FrStatData.hh"
#include "framecpp/FrSummary.hh"
#include "framecpp/FrTable.hh"
#include "framecpp/FrVect.hh"
#include "framecpp/FrSH.hh"
#include "framecpp/FrSE.hh"

// LDAS Tools includes
#include "ldastoolsal/MemChecker.hh"
#include "ldastoolsal/LoggerManager.hh"
#include "ldastoolsal/fstream.hh"
#include "ldastoolsal/util.hh"

using namespace FrameCPP;
using boost::format;
using FrameCPP::FrHeader; // Universal::FrHeader
namespace po = boost::program_options;
namespace alg = boost::algorithm;

//=======================================================================
// Utility Functions
//=======================================================================

/// \brief Demangle C++ type names for readable output
/// \param name Mangled type name from typeid().name()
/// \return Demangled type name or original if demangling fails
std::string
demangle( const char* name )
{
    int   status = 0;
    char* demangled = abi::__cxa_demangle( name, nullptr, nullptr, &status );
    if ( status == 0 && demangled )
    {
        std::string result( demangled );
        std::free( demangled );
        return result;
    }
    return std::string( name ); // Return original if demangling fails
}

/// \brief Extract version number from error messages using regex
/// \param error_msg Error message from deserialization
/// \return Version number, or 0 if not found
unsigned int
extractVersionFromError( const std::string& error_msg )
{
    // clang-format off
    std::regex version_regex( R"((?:for [Vv]ersion[:\s]*(\d+)|[Vv]ersion[:\s]*(\d+) of))" );
    // clang-format on

    std::smatch matches;
    if ( std::regex_search( error_msg, matches, version_regex ) )
    {
        // Check both capture groups
        std::string version_str = matches[ 1 ].matched ? matches[ 1 ].str( ) : matches[ 2 ].str( );
        if ( !version_str.empty( ) )
        {
            return static_cast< unsigned int >( std::stoul( version_str ) );
        }
    }
    return 0; // Version not found
}

//=======================================================================
// Abstract Input Interface for Future Extensibility
//=======================================================================

/// \brief Abstract interface for frame input sources
///
/// This interface abstracts the method of reading frame data, allowing
/// for seamless transition between current IFrameStream approach and
/// future archiver-based reading when available.
class FrameInputSource
{
public:
    virtual ~FrameInputSource( ) = default;

    /// \brief Read a complete frame by number
    virtual boost::shared_ptr< Common::FrameSpec::Object >
    readFrame( size_t frame_number ) = 0;

    /// \brief Read a specific object from a frame
    virtual boost::shared_ptr< Common::FrameSpec::Object >
    readFrameObject( size_t             frame_number,
                     const std::string& object_type,
                     const std::string& name = "" ) = 0;

    /// \brief Get total number of frames in source
    virtual size_t getFrameCount( ) const = 0;

    /// \brief Get frame specification version
    virtual unsigned int getFrameVersion( ) const = 0;

    /// \brief Get source information string
    virtual std::string getSourceInfo( ) const = 0;

    /// \brief Get available objects of specified type in a frame
    virtual std::vector< std::string >
    getAvailableObjects( size_t             frame_number,
                         const std::string& object_type ) const = 0;

    /// \brief Check if source supports random access
    virtual bool supportsRandomAccess( ) const = 0;

    /// \brief Set unknown object handling behavior (for sequential mode)
    virtual void setUnknownObjectHandling( bool skip_unknown,
                                           bool unknown_only ) = 0;

    /// \brief Get count of unknown objects encountered (for exit status)
    virtual size_t getUnknownObjectCount( ) const = 0;

    /// \brief Check if stream is at end of file
    virtual bool isAtEOF( ) const = 0;

    /// \brief Get the cached FrHeader from the stream (read-only)
    virtual const Common::FrHeader* getFrHeader( ) const = 0;
};

//=======================================================================
// SequentialFrameReader-based Implementation  
//=======================================================================

/// \brief Sequential reader implementation using shared SequentialFrameReader
class SequentialReaderSource : public FrameInputSource
{
private:
    const FrameCPP::Common::FrameCPPServices& m_services;
    std::unique_ptr<Common::SequentialFrameReader> m_reader;
    boost::shared_ptr<Common::FrameSpec::Object> m_current_object;
    mutable size_t m_unknown_object_count = 0;
    
    SimpleLogger* logger() const {
        return m_services.logger_mgr.getLogger("framecpp.inspector");
    }

public:
    SequentialReaderSource(const std::string& filename, 
                          const FrameCPP::Common::FrameCPPServices& services)
        : m_services(services)
    {
        logger_log_debug(logger(), 10, 
                        "SequentialReaderSource constructor: filename='", filename, "'");
        
        m_reader = std::make_unique<Common::SequentialFrameReader>(filename, services);
        
        // Set up unknown object callback
        m_reader->setUnknownObjectCallback(
            [this](int class_id, int instance, const std::string& object_name, const std::string& error) {
                m_unknown_object_count++;
                logger_log_warning(logger(), 
                                  "Unknown object encountered: class_id=", class_id,
                                  " (", object_name, "), instance=", instance,
                                  ", error: ", error);
            });
    }

    virtual ~SequentialReaderSource() = default;

    virtual boost::shared_ptr<Common::FrameSpec::Object> readNext()
    {
        if (!m_reader) {
            return nullptr;
        }
        
        m_current_object = m_reader->readNextObject();
        return m_current_object;
    }

    virtual boost::shared_ptr<Common::FrameSpec::Object> readFrame(size_t frame_number) override
    {
        // For sequential reading, frame_number is ignored - always read next object
        logger_log_debug(logger(), 5, "SequentialReaderSource::readFrame() called with frame_number=", frame_number);
        
        if (!m_reader) {
            logger_log_error(logger(), "SequentialReaderSource::readFrame() - m_reader is null!");
            return nullptr;
        }
        
        logger_log_debug(logger(), 5, "SequentialReaderSource::readFrame() calling m_reader->readNextObject()");
        auto result = m_reader->readNextObject();
        
        logger_log_debug(logger(), 5, "SequentialReaderSource::readFrame() readNextObject() returned: ", (result ? "object" : "null"));
        return result;
    }

    virtual boost::shared_ptr<Common::FrameSpec::Object> readFrameObject(size_t frame_number, 
                                                                        const std::string& object_type,
                                                                        const std::string& object_name) override
    {
        logger_log_error(logger(), "SequentialReaderSource::readFrameObject() called - NOT SUPPORTED! frame_number=", frame_number, ", object_type=", object_type, ", object_name=", object_name);
        throw std::runtime_error("SequentialReaderSource does not support random access");
    }

    virtual size_t getFrameCount() const override
    {
        // For sequential reading, we can't know frame count ahead of time
        // Return 0 to indicate unknown count
        return 0;
    }

    virtual unsigned int getFrameVersion() const override
    {
        if (!m_reader) return 0;
        return m_reader->getStreamVersion();
    }

    virtual std::string getSourceInfo() const override
    {
        return "SequentialFrameReader-based source";
    }

    virtual void setSequentialMode(bool sequential_mode)
    {
        // Sequential mode is always enabled for SequentialFrameReader
        logger_log_debug(logger(), 20, 
                        "SequentialReaderSource::setSequentialMode(): always sequential");
    }

    virtual void seekToFrame(size_t frame_number)
    {
        logger_log_error(logger(), "SequentialReaderSource::seekToFrame() called - NOT SUPPORTED! frame_number=", frame_number);
        throw std::runtime_error("SequentialReaderSource does not support random access");
    }

    virtual std::vector<std::string> getAvailableObjects(size_t frame_number, 
                                                        const std::string& object_type) const override
    {
        logger_log_error(logger(), "SequentialReaderSource::getAvailableObjects() called - NOT SUPPORTED! frame_number=", frame_number, ", object_type=", object_type);
        throw std::runtime_error("SequentialReaderSource does not support random access");
    }

    virtual bool supportsRandomAccess() const override
    {
        return false;
    }

    virtual void setUnknownObjectHandling(bool skip_unknown, bool unknown_only) override
    {
        if (!m_reader) return;
        
        if (unknown_only) {
            m_reader->enableUnknownOnlyMode();
        } else if (skip_unknown) {
            m_reader->enableSkipUnknownMode();
        } else {
            m_reader->setMode(Common::SequentialFrameReader::Mode::FULL_PROCESSING);
        }
        
        logger_log_debug(logger(), 20, 
                        "SequentialReaderSource::setUnknownObjectHandling(): skip_unknown=", skip_unknown,
                        ", unknown_only=", unknown_only);
    }

    virtual size_t getUnknownObjectCount() const override
    {
        return m_unknown_object_count;
    }

    virtual bool isAtEOF() const override
    {
        return m_reader ? m_reader->eof() : true;
    }

    virtual const Common::FrHeader* getFrHeader() const override
    {
        if (!m_reader) return nullptr;
        
        auto frame_header = m_reader->getFrameHeader();
        return frame_header ? frame_header.get() : nullptr;
    }
};





//=======================================================================
// Current IFrameStream Implementation
//=======================================================================

/// \brief Current implementation using IFrameStream infrastructure
class IFrameStreamSource : public FrameInputSource
{
private:
    const FrameCPP::Common::FrameCPPServices& m_services;
    bool                                      m_sequential_mode;
    size_t                                    m_sequential_position = 0;
    bool                                      m_eof_reached = false;

    SimpleLogger*
    logger( ) const
    {
        return m_services.logger_mgr.getLogger( "framecpp.inspector" );
    }


public:
    IFrameStreamSource( const std::string&                        filename,
                        const FrameCPP::Common::FrameCPPServices& services,
                        bool sequential_mode = false )
        : m_services( services ), m_sequential_mode( sequential_mode ),
          m_filename( filename )
    {
        logger_log_debug(
            logger( ),
            10,
            "IFrameStreamSource::IFrameStreamSource() entry, filename='",
            filename,
            "', sequential_mode=",
            sequential_mode );

        // Create frame buffer and stream using existing pattern from FrQuery.cc
        logger_log_debug(
            logger( ),
            10,
            "IFrameStreamSource::IFrameStreamSource() creating frame buffer" );
        
        m_frame_buffer =
            std::make_unique< Common::FrameBuffer< std::filebuf > >(
                std::ios::in );
        
        logger_log_debug(
            logger( ),
            10,
            "IFrameStreamSource::IFrameStreamSource() frame buffer created, opening file" );
        
        m_frame_buffer->open( filename.c_str( ),
                              std::ios::in | std::ios::binary );

        if ( !m_frame_buffer->is_open( ) )
        {
            throw std::runtime_error( "Failed to open frame file: " +
                                      filename );
        }
        
        logger_log_debug(
            logger( ),
            10,
            "IFrameStreamSource::IFrameStreamSource() frame buffer opened successfully" );

        // Create appropriate stream type based on mode
        if ( m_sequential_mode )
        {
            logger_log_debug(
                logger( ),
                20,
                "IFrameStreamSource::IFrameStreamSource() creating "
                "SequentialObjectReader for sequential mode" );
            // Create SequentialObjectReader with version 0 to let IFrameStream
            // determine the version from the file header during init()
            INT_2U frame_version = 0; // Let IFrameStream determine from header
            logger_log_debug( logger( ),
                              30,
                              "IFrameStreamSource::IFrameStreamSource() "
                              "using version 0 to auto-detect from header: ",
                              frame_version );

            // Create SequentialObjectReader
            logger_log_debug( logger( ),
                              10,
                              "IFrameStreamSource::IFrameStreamSource() "
                              "about to create SequentialObjectReader" );
            
            // Check frame buffer before passing to SequentialObjectReader
            if ( !m_frame_buffer )
            {
                throw std::runtime_error( "Frame buffer is null before creating SequentialObjectReader" );
            }
            
            logger_log_debug( logger( ),
                              10,
                              "IFrameStreamSource::IFrameStreamSource() "
                              "frame buffer validated, creating SequentialObjectReader with buffer=",
                              static_cast< void* >( m_frame_buffer.get( ) ),
                              ", version=",
                              frame_version );
            
            
            // Create SequentialFrameReader with FrameBuffer
            // Transfer ownership of buffer to SequentialFrameReader
            m_sequential_reader = std::make_unique< Common::SequentialFrameReader >(
                m_frame_buffer.release(), // Transfer ownership to SequentialFrameReader constructor
                m_services,
                frame_version);
            
            
            logger_log_debug( logger( ),
                              20,
                              "IFrameStreamSource::IFrameStreamSource() "
                              "SequentialObjectReader created successfully" );

            // Initialize sequential reading
            logger_log_debug( logger( ),
                              10,
                              "IFrameStreamSource::IFrameStreamSource() "
                              "about to call initializeSequentialReading()" );
            
            // SequentialFrameReader initializes automatically on first read
            
            logger_log_debug( logger( ),
                              10,
                              "IFrameStreamSource::IFrameStreamSource() "
                              "sequential reading initialized successfully" );
        }
        else
        {
            logger_log_debug(
                logger( ),
                50,
                "IFrameStreamSource::IFrameStreamSource() creating "
                "IFrameStream for TOC mode" );
            
            // Create IFrameStreamWrapper - transfer buffer ownership to IFrameStream
            m_stream = std::make_unique< IFrameStream >(
                true,                     // AutoDelete=true - IFrameStream owns buffer
                m_frame_buffer.release() ); // Transfer ownership to IFrameStream constructor
            logger_log_debug( logger( ),
                              50,
                              "IFrameStreamSource::IFrameStreamSource() "
                              "IFrameStream created successfully" );
        }

        logger_log_debug(
            logger( ),
            10,
            "IFrameStreamSource::IFrameStreamSource() constructor complete" );
    }

    ~IFrameStreamSource( )
    {
        // Add targeted debugging to track destruction order and segfault
        // location
        if ( logger( ) )
        {
            logger_log_debug(
                logger( ),
                50,
                "IFrameStreamSource::~IFrameStreamSource() entry, this=",
                static_cast< const void* >( this ) );
        }

        try
        {
            // Cleanup both stream types
            if ( logger( ) )
            {
                logger_log_debug(
                    logger( ),
                    50,
                    "IFrameStreamSource::~IFrameStreamSource() "
                    "cleaning up stream objects" );
                logger_log_debug(
                    logger( ),
                    50,
                    "IFrameStreamSource::~IFrameStreamSource() m_stream=",
                    static_cast< void* >( m_stream.get( ) ) );
                logger_log_debug(
                    logger( ),
                    50,
                    "IFrameStreamSource::~IFrameStreamSource() m_sequential_reader=",
                    static_cast< void* >( m_sequential_reader.get( ) ) );
            }

            // Cleanup both streams (only one will be non-null)
            m_stream.reset( );
            m_sequential_reader.reset( );
            m_frame_buffer
                .reset( ); // Always no-op since ownership was transferred

            if ( logger( ) )
            {
                logger_log_debug( logger( ),
                                  50,
                                  "IFrameStreamSource::~IFrameStreamSource() "
                                  "destruction complete" );
            }
        }
        catch ( const std::exception& e )
        {
            if ( logger( ) )
            {
                logger_log_debug( logger( ),
                                  50,
                                  "IFrameStreamSource::~IFrameStreamSource() "
                                  "caught exception: ",
                                  e.what( ) );
            }
        }
        catch ( ... )
        {
            if ( logger( ) )
            {
                logger_log_debug( logger( ),
                                  50,
                                  "IFrameStreamSource::~IFrameStreamSource() "
                                  "caught unknown exception" );
            }
        }
    }

    boost::shared_ptr< Common::FrameSpec::Object >
    readFrame( size_t frame_number ) override
    {
        try
        {
            if ( m_sequential_mode )
            {
                // In sequential mode, read objects one by one using
                // SequentialObjectReader Frame number is ignored - we read
                // objects sequentially
                if ( !m_sequential_reader )
                {
                    throw std::runtime_error(
                        "Sequential reader not initialized" );
                }
                return m_sequential_reader->readNextObject( );
            }
            else
            {
                // Normal TOC-based random access
                if ( !m_stream )
                {
                    throw std::runtime_error(
                        "Stream not initialized" );
                }
                return m_stream->ReadFrameN( frame_number );
            }
        }
        catch ( const std::exception& e )
        {
            throw std::runtime_error(
                str( format( "Failed to read frame %1% from %2%: %3%" ) %
                     frame_number % m_filename % e.what( ) ) );
        }
    }

    /// \brief Read the next frame sequentially without using TOC
    /// \deprecated This method is deprecated in favor of SequentialObjectReader
    boost::shared_ptr< Common::FrameSpec::Object >
    readNextFrameSequentially( )
    {
        if ( m_eof_reached )
        {
            throw std::runtime_error(
                "End of file reached in sequential mode" );
        }

        try
        {
            // Use ReadFrame() without frame number for sequential reading
            // This reads the next frame from the current stream position
            auto frame = m_stream->ReadFrame( );
            if ( !frame )
            {
                m_eof_reached = true;
                throw std::runtime_error( "No more frames available" );
            }
            m_sequential_position++;
            return frame;
        }
        catch ( const std::exception& e )
        {
            // Check if we've reached EOF
            m_eof_reached = true;
            throw std::runtime_error(
                str( format( "Sequential read error at position %1%: %2%" ) %
                     m_sequential_position % e.what( ) ) );
        }
    }

    boost::shared_ptr< Common::FrameSpec::Object >
    readFrameObject( size_t             frame_number,
                     const std::string& object_type,
                     const std::string& name ) override
    {
        try
        {
            // Check if m_stream is available (specific object reading requires
            // TOC mode)
            if ( !m_stream )
            {
                throw std::runtime_error(
                    "Specific object reading requires TOC mode - not available "
                    "in sequential mode" );
            }

            // Map object types to IFrameStream methods
            if ( alg::iequals( object_type, "adc" ) ||
                 alg::iequals( object_type, "FrAdcData" ) )
            {
                if ( name.empty( ) )
                {
                    throw std::invalid_argument(
                        "ADC object requires channel name" );
                }
                return m_stream->ReadFrAdcData( frame_number, name );
            }
            else if ( alg::iequals( object_type, "proc" ) ||
                      alg::iequals( object_type, "FrProcData" ) )
            {
                if ( name.empty( ) )
                {
                    throw std::invalid_argument(
                        "Proc object requires channel name" );
                }
                return m_stream->ReadFrProcData( frame_number, name );
            }
            else if ( alg::iequals( object_type, "sim" ) ||
                      alg::iequals( object_type, "FrSimData" ) )
            {
                if ( name.empty( ) )
                {
                    throw std::invalid_argument(
                        "Sim object requires channel name" );
                }
                return m_stream->ReadFrSimData( frame_number, name );
            }
            else if ( alg::iequals( object_type, "ser" ) ||
                      alg::iequals( object_type, "FrSerData" ) )
            {
                if ( name.empty( ) )
                {
                    throw std::invalid_argument(
                        "Ser object requires channel name" );
                }
                return m_stream->ReadFrSerData( frame_number, name );
            }
            else if ( alg::iequals( object_type, "event" ) ||
                      alg::iequals( object_type, "FrEvent" ) )
            {
                if ( name.empty( ) )
                {
                    throw std::invalid_argument(
                        "Event object requires event name" );
                }
                return m_stream->ReadFrEvent( frame_number, name );
            }
            else if ( alg::iequals( object_type, "simevent" ) ||
                      alg::iequals( object_type, "FrSimEvent" ) )
            {
                if ( name.empty( ) )
                {
                    throw std::invalid_argument(
                        "SimEvent object requires event name" );
                }
                return m_stream->ReadFrSimEvent( frame_number, name );
            }
            else if ( alg::iequals( object_type, "header" ) ||
                      alg::iequals( object_type, "FrameH" ) )
            {
                return m_stream->ReadFrameH( frame_number,
                                             Common::FrameH::DETECT_SIM |
                                                 Common::FrameH::DETECT_PROC |
                                                 Common::FrameH::HISTORY );
            }
            else
            {
                throw std::invalid_argument( "Unsupported object type: " +
                                             object_type );
            }
        }
        catch ( const std::exception& e )
        {
            throw std::runtime_error( str(
                format(
                    "Failed to read %1% object '%2%' from frame %3%: %4%" ) %
                object_type % name % frame_number % e.what( ) ) );
        }
    }

    size_t
    getFrameCount( ) const override
    {
        if ( m_sequential_mode )
        {
            // In sequential mode, we don't know the frame count until we read
            // them all
            logger_log_debug( logger( ),
                              30,
                              "IFrameStreamSource::getFrameCount() sequential "
                              "mode - returning SIZE_MAX" );
            return SIZE_MAX; // Unknown until EOF
        }

        logger_log_debug( logger( ),
                          50,
                          "IFrameStreamSource::getFrameCount() about to call "
                          "GetNumberOfFrames()" );

        // Add additional debugging for frame count=0 issue investigation
        try
        {
            logger_log_debug( logger( ),
                              30,
                              "IFrameStreamSource::getFrameCount() checking "
                              "stream validity" );

            // Check if m_stream is available (only in TOC mode)
            if ( !m_stream )
            {
                throw std::runtime_error( "getFrameCount() requires TOC mode - "
                                          "not available in sequential mode" );
            }

            // Check stream version and basic info
            auto version = m_stream->Version( );
            logger_log_debug(
                logger( ),
                30,
                "IFrameStreamSource::getFrameCount() stream version: ",
                version );

            // Try to get TOC information
            try
            {
                auto toc = m_stream->GetTOC( );
                if ( toc )
                {
                    logger_log_debug(
                        logger( ),
                        30,
                        "IFrameStreamSource::getFrameCount() TOC exists, "
                        "attempting to get frame count from TOC" );
                }
                else
                {
                    logger_log_debug(
                        logger( ),
                        20,
                        "IFrameStreamSource::getFrameCount() WARNING: TOC is "
                        "null - this may explain frame count=0" );
                }
            }
            catch ( const std::exception& e )
            {
                logger_log_debug( logger( ),
                                  20,
                                  "IFrameStreamSource::getFrameCount() "
                                  "Exception getting TOC: ",
                                  e.what( ) );
            }
            catch ( ... )
            {
                logger_log_debug( logger( ),
                                  20,
                                  "IFrameStreamSource::getFrameCount() Unknown "
                                  "exception getting TOC" );
            }

            size_t count = m_stream->GetNumberOfFrames( );
            logger_log_debug( logger( ),
                              50,
                              "IFrameStreamSource::getFrameCount() "
                              "GetNumberOfFrames() returned: ",
                              count );

            if ( count == 0 )
            {
                logger_log_debug( logger( ),
                                  20,
                                  "IFrameStreamSource::getFrameCount() "
                                  "ANALYSIS: Frame count is 0" );
                logger_log_debug( logger( ),
                                  20,
                                  "IFrameStreamSource::getFrameCount() File: ",
                                  m_filename );
                logger_log_debug( logger( ),
                                  20,
                                  "IFrameStreamSource::getFrameCount() This "
                                  "could indicate:" );
                logger_log_debug( logger( ),
                                  20,
                                  "IFrameStreamSource::getFrameCount() 1. "
                                  "Empty or corrupted frame file" );
                logger_log_debug( logger( ),
                                  20,
                                  "IFrameStreamSource::getFrameCount() 2. File "
                                  "is not a valid GWF frame file" );
                logger_log_debug( logger( ),
                                  20,
                                  "IFrameStreamSource::getFrameCount() 3. TOC "
                                  "is missing or corrupted" );
                logger_log_debug( logger( ),
                                  20,
                                  "IFrameStreamSource::getFrameCount() 4. "
                                  "Frame file has no actual frame data" );
            }

            return count;
        }
        catch ( const std::exception& e )
        {
            logger_log_debug( logger( ),
                              10,
                              "IFrameStreamSource::getFrameCount() EXCEPTION: ",
                              e.what( ) );
            throw;
        }
        catch ( ... )
        {
            logger_log_debug( logger( ),
                              10,
                              "IFrameStreamSource::getFrameCount() Unknown "
                              "exception during frame count analysis" );
            throw;
        }
    }

    unsigned int
    getFrameVersion( ) const override
    {
        // Both sequential and TOC modes can use Version() method
        if ( m_sequential_mode )
        {
            if ( !m_sequential_reader )
            {
                throw std::runtime_error(
                    "getFrameVersion() requires sequential reader access" );
            }
            return m_sequential_reader->getStreamVersion();
        }
        else
        {
            if ( !m_stream )
            {
                throw std::runtime_error(
                    "getFrameVersion() requires stream access" );
            }
            return m_stream->Version( );
        }
    }

    std::string
    getSourceInfo( ) const override
    {
        std::ostringstream info;
        info << "IFrameStream source: " << m_filename;
        info << " (Version " << getFrameVersion( );

        // Frame count is only available in TOC mode
        if ( m_sequential_mode )
        {
            info << ", sequential mode)";
        }
        else
        {
            info << ", " << getFrameCount( ) << " frames)";
        }

        // Add library information from stream header
        try
        {
            if ( m_stream )
            {
                // Both sequential and TOC modes can use GetFrHeader()
                const auto& header = m_stream->GetFrHeader( );
                info << " [Library: " << header.GetFrameLibraryName( )
                     << ", Revision: " << header.GetLibraryRevision( )
                     << ", Originator: " << header.GetOriginator( ) << "]";
            }
        }
        catch ( ... )
        {
            // Header information not available
        }

        return info.str( );
    }

    std::vector< std::string >
    getAvailableObjects( size_t             frame_number,
                         const std::string& object_type ) const override
    {
        // This would require TOC parsing - simplified for now
        // Future enhancement: parse TOC to get actual channel lists
        std::vector< std::string > objects;

        if ( alg::iequals( object_type, "adc" ) )
        {
            objects.push_back(
                "(Use --list-channels adc to see available ADC channels)" );
        }
        else if ( alg::iequals( object_type, "proc" ) )
        {
            objects.push_back(
                "(Use --list-channels proc to see available Proc channels)" );
        }
        // ... etc for other types

        return objects;
    }

    bool
    supportsRandomAccess( ) const override
    {
        return !m_sequential_mode; // Random access only in non-sequential mode
    }

    /// \brief Set unknown object handling flags for sequential mode
    void
    setUnknownObjectHandling( bool skip_unknown, bool unknown_only ) override
    {
        if ( m_sequential_mode && m_sequential_reader )
        {
            if ( skip_unknown && unknown_only )
            {
                m_sequential_reader->enableUnknownOnlyMode();
            }
            else if ( skip_unknown )
            {
                m_sequential_reader->enableSkipUnknownMode();
            }
            // If neither flag is set, use default FULL_PROCESSING mode
        }
    }

    /// \brief Get the count of unknown objects encountered in sequential mode
    size_t
    getUnknownObjectCount( ) const override
    {
        if ( m_sequential_mode && m_sequential_reader )
        {
            return m_sequential_reader->getStatistics().objects_skipped; // Use stats for unknown object count
        }
        return 0;
    }

    /// \brief Check if stream is at EOF
    bool
    isAtEOF( ) const override
    {
        if ( m_sequential_mode && m_sequential_reader )
        {
            return m_sequential_reader->eof( );
        }
        // For non-sequential mode, check if we've reached EOF through the
        // stream
        return m_eof_reached;
    }

    /// \brief Get the cached FrHeader from the stream (read-only)
    const Common::FrHeader*
    getFrHeader( ) const override
    {
        if ( m_stream )
        {
            return &m_stream->GetFrHeader( ); // Returns const FrHeader&, we take address for const*
        }
        return nullptr;
    }

private:
    using IFrameStream =
        Common::IFrameStreamWrapper< 0,
                                     Common::FrameSpec::Object, // FrAdcData
                                     Common::FrameSpec::Object, // FrameH
                                     Common::FrameSpec::Object, // FrProcData
                                     Common::FrameSpec::Object, // FrSerData
                                     Common::FrameSpec::Object, // FrSimData
                                     Common::FrameSpec::Object, // FrRawData
                                     Common::FrameSpec::Object, // FrEvent
                                     Common::FrameSpec::Object // FrSimEvent
                                     >;

    std::string                                            m_filename;
    std::unique_ptr< Common::FrameBuffer< std::filebuf > > m_frame_buffer;
    std::unique_ptr< IFrameStream >                        m_stream;
    std::unique_ptr< Common::SequentialFrameReader >      m_sequential_reader;
};

//=======================================================================
// Future Archiver Implementation (Stub for Future Development)
//=======================================================================

/// \brief Future implementation using archiver infrastructure
/// This is a placeholder/stub that will be implemented when archiver becomes
/// available
class ArchiverSource : public FrameInputSource
{
public:
    ArchiverSource( const std::string& filename ) : m_filename( filename )
    {
        // Future: Initialize archiver-based reading
        throw std::runtime_error( "Archiver-based reading not yet implemented. "
                                  "Use --input-method stream for now." );
    }

    // Stub implementations - will be completed when archiver is available
    boost::shared_ptr< Common::FrameSpec::Object >
    readFrame( size_t frame_number ) override
    {
        throw std::runtime_error( "Archiver implementation not available" );
    }

    boost::shared_ptr< Common::FrameSpec::Object >
    readFrameObject( size_t             frame_number,
                     const std::string& object_type,
                     const std::string& name ) override
    {
        throw std::runtime_error( "Archiver implementation not available" );
    }

    size_t
    getFrameCount( ) const override
    {
        return 0;
    }
    unsigned int
    getFrameVersion( ) const override
    {
        return 9;
    }
    std::string
    getSourceInfo( ) const override
    {
        return "Archiver source (not implemented)";
    }
    std::vector< std::string >
    getAvailableObjects( size_t             frame_number,
                         const std::string& object_type ) const override
    {
        return {};
    }
    bool
    supportsRandomAccess( ) const override
    {
        return true;
    }

    /// \brief Set unknown object handling behavior (stub implementation)
    void
    setUnknownObjectHandling( bool skip_unknown, bool unknown_only ) override
    {
        // Archiver implementation doesn't support unknown object handling
        // This is a no-op stub
    }

    /// \brief Get count of unknown objects encountered (stub implementation)
    size_t
    getUnknownObjectCount( ) const override
    {
        // Archiver implementation doesn't track unknown objects
        return 0;
    }

    /// \brief Check if stream is at EOF (stub implementation)
    bool
    isAtEOF( ) const override
    {
        // Archiver implementation doesn't provide EOF checking
        return false;
    }

    /// \brief Get the cached FrHeader from the stream (stub implementation)
    const Common::FrHeader*
    getFrHeader( ) const override
    {
        // Archiver implementation doesn't have direct FrHeader access
        return nullptr;
    }

private:
    std::string m_filename;
    // Future: std::unique_ptr<FrameCPP::Archive::InputArchive> m_archiver;
};

//=======================================================================
// Factory Pattern for Input Source Creation
//=======================================================================

/// \brief Factory for creating appropriate input source based on method and
/// availability
class FrameInputSourceFactory
{
public:
    enum class InputMethod
    {
        AUTO, ///< Automatically select best available method
        STREAM, ///< Force IFrameStream method
        ARCHIVER ///< Force archiver method (future)
    };

    static std::unique_ptr< FrameInputSource >
    create( const std::string&                        filename,
            const FrameCPP::Common::FrameCPPServices& services,
            InputMethod method = InputMethod::AUTO,
            bool        sequential = false )
    {
        switch ( method )
        {
        case InputMethod::STREAM:
            if (sequential) {
                return std::make_unique< SequentialReaderSource >(filename, services);
            } else {
                return std::make_unique< IFrameStreamSource >(filename, services, sequential);
            }

        case InputMethod::ARCHIVER:
            if ( isArchiverAvailable( ) )
            {
                return std::make_unique< ArchiverSource >( filename );
            }
            else
            {
                throw std::runtime_error( "Archiver method requested but not "
                                          "available. Use 'stream' method." );
            }

        case InputMethod::AUTO:
            // Future: Implement intelligent selection
            // For now, always use IFrameStream
            if ( isArchiverAvailable( ) && preferArchiver( filename ) )
            {
                return std::make_unique< ArchiverSource >( filename );
            }
            else
            {
                if (sequential) {
                    return std::make_unique< SequentialReaderSource >(filename, services);
                } else {
                    return std::make_unique< IFrameStreamSource >(filename, services, sequential);
                }
            }

        default:
            throw std::invalid_argument( "Unknown input method" );
        }
    }

    static InputMethod
    parseMethod( const std::string& method_str )
    {
        std::string normalized = method_str;
        alg::to_lower( normalized );

        if ( normalized == "auto" )
            return InputMethod::AUTO;
        if ( normalized == "stream" )
            return InputMethod::STREAM;
        if ( normalized == "archiver" )
            return InputMethod::ARCHIVER;

        throw std::invalid_argument( "Invalid input method: " + method_str +
                                     " (must be: auto, stream, archiver)" );
    }

    static bool
    isArchiverAvailable( )
    {
        // Future: Check if archiver library is available
        return false; // Currently not implemented
    }

private:
    static bool
    preferArchiver( const std::string& filename )
    {
        // Future: Implement logic to determine if archiver is preferred
        // e.g., based on file size, format, performance characteristics
        return false;
    }
};

//=======================================================================
// Configuration and Utilities
//=======================================================================

/// \brief Configuration structure for inspection operations
struct InspectionConfig
{
    std::string                          filename;
    FrameInputSourceFactory::InputMethod input_method =
        FrameInputSourceFactory::InputMethod::AUTO;
    std::vector< size_t > frame_numbers;
    std::string           object_type;
    std::string           channel_name;
    Debug::OutputFormat   output_format = Debug::OutputFormat::DEBUG;
    std::string           output_filename;
    unsigned int          forced_version = 0; // 0 = auto-detect
    bool                  verbose = false;
    bool                  list_objects = false;
    std::string           list_channels_type;
    bool                  silent_data = false;
    bool                  sequential = false; // Sequential mode flag
    bool skip_unknown = false; // Skip unknown objects gracefully
    bool unknown_only = false; // Process only unknown objects

    // FramePrinter configuration
    Debug::AlignmentMode alignment = Debug::AlignmentMode::SEPARATOR;
    int                  indentation = 2;
    size_t               max_field_width = 0;
    std::string          field_filter_pattern;
    
    // Walking configuration
    bool walk_objects = false;     // Enable walking into container objects
    bool walk_containers = false;  // Enable walking into PTR_STRUCT containers
    int  walk_depth = 3;           // Maximum recursion depth for walking
};

/// \brief Parse frame range specification (e.g., "5", "1-10", "1,3,5-7")
std::vector< size_t >
parseFrameRange( const std::string& range_str, size_t max_frames )
{
    std::vector< size_t >      frames;
    std::vector< std::string > parts;
    alg::split( parts, range_str, alg::is_any_of( "," ) );

    for ( const auto& part : parts )
    {
        std::string trimmed = alg::trim_copy( part );

        if ( trimmed.find( '-' ) != std::string::npos )
        {
            // Range specification: "1-10"
            std::vector< std::string > range_parts;
            alg::split( range_parts, trimmed, alg::is_any_of( "-" ) );

            if ( range_parts.size( ) != 2 )
            {
                throw std::invalid_argument( "Invalid range specification: " +
                                             trimmed );
            }

            size_t start = std::stoull( alg::trim_copy( range_parts[ 0 ] ) );
            size_t end = std::stoull( alg::trim_copy( range_parts[ 1 ] ) );

            if ( start > end )
            {
                throw std::invalid_argument( "Invalid range: start > end in " +
                                             trimmed );
            }

            for ( size_t i = start; i <= end && i < max_frames; ++i )
            {
                frames.push_back( i );
            }
        }
        else
        {
            // Single frame number
            size_t frame_num = std::stoull( trimmed );
            if ( frame_num < max_frames )
            {
                frames.push_back( frame_num );
            }
        }
    }

    // Remove duplicates and sort
    std::sort( frames.begin( ), frames.end( ) );
    frames.erase( std::unique( frames.begin( ), frames.end( ) ),
                  frames.end( ) );

    return frames;
}

//=======================================================================
// Helper Functions
//=======================================================================

/// \brief Print frame object by casting to concrete type
/// The base FrameSpec::Object class doesn't have serialization, so we need to
/// cast to the concrete derived types that do have serialization methods.
void
printFrameObject( Debug::FramePrinter&       printer,
                  Common::FrameSpec::Object* obj,
                  unsigned int               version,
                  const FrameCPP::Common::FrameCPPServices* services = nullptr )
{
    if ( !obj )
        return;
    
    // Log object type being printed
    if ( services )
    {
        auto* logger = services->logger_mgr.getLogger( "framecpp.inspector.walking" );
        logger_log_debug( logger, 25, "printFrameObject() called for object type: ", 
                         demangle( typeid( *obj ).name( ) ) );
    }

    // Try casting to known concrete types that have serialization
    // These are the main frame object types that FramePrinter can handle

    // Try FrameH (frame header)
    if ( auto* frameH = dynamic_cast< Universal::FrameH* >( obj ) )
    {
        printer( *frameH, version );
        return;
    }

    // Try FrSH (Structure Header - metadata describing frame structures)
    if ( auto* frsh = dynamic_cast< Universal::FrSH* >( obj ) )
    {
        printer( *frsh, version );
        return;
    }

    // Try FrSE (Structure End - metadata marking end of structure definitions)
    if ( auto* frse = dynamic_cast< Universal::FrSE* >( obj ) )
    {
        printer( *frse, version );
        return;
    }

    // Try FrAdcData
    if ( auto* adcData = dynamic_cast< Universal::FrAdcData* >( obj ) )
    {
        printer( *adcData, version );
        return;
    }

    // Try FrProcData
    if ( auto* procData = dynamic_cast< Universal::FrProcData* >( obj ) )
    {
        printer( *procData, version );
        return;
    }

    // Try FrSimData
    if ( auto* simData = dynamic_cast< Universal::FrSimData* >( obj ) )
    {
        printer( *simData, version );
        return;
    }

    // Try FrSerData
    if ( auto* serData = dynamic_cast< Universal::FrSerData* >( obj ) )
    {
        printer( *serData, version );
        return;
    }

    // Try FrEvent
    if ( auto* event = dynamic_cast< Universal::FrEvent* >( obj ) )
    {
        printer( *event, version );
        return;
    }

    // Try FrSimEvent
    if ( auto* simEvent = dynamic_cast< Universal::FrSimEvent* >( obj ) )
    {
        printer( *simEvent, version );
        return;
    }

    // Try FrDetector
    if ( auto* detector = dynamic_cast< Universal::FrDetector* >( obj ) )
    {
        printer( *detector, version );
        return;
    }

    // Try FrHistory
    if ( auto* history = dynamic_cast< Universal::FrHistory* >( obj ) )
    {
        printer( *history, version );
        return;
    }

    // Try FrTable
    if ( auto* table = dynamic_cast< Universal::FrTable* >( obj ) )
    {
        printer( *table, version );
        return;
    }

    // Try FrVect
    if ( auto* vect = dynamic_cast< Universal::FrVect* >( obj ) )
    {
        printer( *vect, version );
        return;
    }

    // Try FrStatData
    if ( auto* statData = dynamic_cast< Universal::FrStatData* >( obj ) )
    {
        printer( *statData, version );
        return;
    }

    // Try FrRawData
    if ( auto* rawData = dynamic_cast< Universal::FrRawData* >( obj ) )
    {
        printer( *rawData, version );
        return;
    }

    // Try FrMsg
    if ( auto* msg = dynamic_cast< Universal::FrMsg* >( obj ) )
    {
        printer( *msg, version );
        return;
    }

    // Try FrSummary
    if ( auto* summary = dynamic_cast< Universal::FrSummary* >( obj ) )
    {
        printer( *summary, version );
        return;
    }

    // Try FrEndOfFrame
    if ( auto* endOfFrame = dynamic_cast< Universal::FrEndOfFrame* >( obj ) )
    {
        printer( *endOfFrame, version );
        return;
    }

    // Try FrEndOfFile
    if ( auto* endOfFile = dynamic_cast< Universal::FrEndOfFile* >( obj ) )
    {
        printer( *endOfFile, version );
        return;
    }

    // If none of the concrete types match, fall back to basic info
    std::cout << "Unknown frame object type: "
              << demangle( typeid( *obj ).name( ) ) << std::endl;
    std::cout << "Object class: " << obj->GetClass( ) << std::endl;
}

/// \brief Print object separator in verbose mode
/// \param output Output stream to write separator to
/// \param verbose Whether verbose mode is enabled
void
printObjectSeparator( std::ostream* output, bool verbose )
{
    if ( verbose && output )
    {
        *output << "\n" << std::string(50, '-') << "\n" << std::endl;
    }
}

//======================================================================
// Core Processing Functions
//======================================================================

/// \brief Process and display frame information using configured printer
void
processFrames( FrameInputSource& source, const InspectionConfig& config, const FrameCPP::Common::FrameCPPServices& services )
{
    // Get logger for walking diagnostics
    auto* logger = services.logger_mgr.getLogger( "framecpp.inspector.walking" );
    logger_log_debug( logger, 10, "processFrames() entry - walk_objects=", config.walk_objects ? "true" : "false",
                     ", walk_containers=", config.walk_containers ? "true" : "false", 
                     ", walk_depth=", config.walk_depth );
    
    // Create output stream
    std::unique_ptr< std::ofstream > file_stream;
    std::ostream*                    output = &std::cout;

    if ( !config.output_filename.empty( ) )
    {
        file_stream =
            std::make_unique< std::ofstream >( config.output_filename );
        if ( !file_stream->is_open( ) )
        {
            throw std::runtime_error( "Failed to open output file: " +
                                      config.output_filename );
        }
        output = file_stream.get( );
    }

    // Configure FramePrinter based on output format - avoid assignment due to
    // reference member
    std::unique_ptr< Debug::FramePrinter > printer;

    if ( config.output_format == Debug::OutputFormat::JSON )
    {
        printer = std::make_unique< Debug::FramePrinter >(
            Debug::jsonPrinter( *output ) );
    }
    else
    {
        printer = std::make_unique< Debug::FramePrinter >( *output, services );
        printer->setAlignment( config.alignment )
            .setIndentation( config.indentation )
            .setMaxFieldWidth( config.max_field_width )
            .showTypeInfo( !config.silent_data )
            .setWalkObjects( config.walk_objects )
            .setWalkContainers( config.walk_containers )
            .setWalkDepth( config.walk_depth );
        
        logger_log_debug( logger, 15, "FramePrinter configured with walking settings - objects=", 
                         config.walk_objects ? "enabled" : "disabled",
                         ", containers=", config.walk_containers ? "enabled" : "disabled",
                         ", depth=", config.walk_depth );
    }

    // Set frame version (auto-detect or forced)
    unsigned int version = ( config.forced_version > 0 )
        ? config.forced_version
        : source.getFrameVersion( );
    printer->setVersion( version );
    
    logger_log_debug( logger, 20, "Frame version set to ", version );

    // Display FrHeader first if available
    if ( const auto* frHeader = source.getFrHeader( ) )
    {
        if ( config.verbose )
        {
            *output << "File Header Information:" << std::endl;
        }
        // Cast to Universal::FrHeader for proper printing (read-only)
        if ( const auto* header = dynamic_cast< const Universal::FrHeader* >( frHeader ) )
        {
            ( *printer )( *header, version );
        }
    }

    // Process frames/objects based on mode
    if ( config.sequential )
    {
        // Sequential mode: read all objects until EOF
        if ( config.verbose )
        {
            *output << "Sequential mode: reading all objects until EOF..."
                    << std::endl;
        }

        size_t object_count = 0;
        try
        {
            while ( true )
            {
                boost::shared_ptr< Common::FrameSpec::Object > frame_object;

                if ( config.object_type.empty( ) )
                {
                    // Read next object sequentially
                    frame_object = source.readFrame(
                        0 ); // Frame number ignored in sequential mode
                    if ( !frame_object )
                    {
                        // Check if this is true EOF or just a skipped unknown
                        // object
                        if ( source.isAtEOF( ) )
                        {
                            // True EOF - stop processing
                            break;
                        }
                        else
                        {
                            // Skipped unknown object - continue processing
                            continue;
                        }
                    }

                    object_count++;
                    if ( config.verbose )
                    {
                        *output
                            << str( format( "Object %1%: " ) % object_count );
                    }

                    // Cast to concrete type for FramePrinter - Object base
                    // class doesn't have serialization
                    printFrameObject( *printer, frame_object.get( ), version, &services );
                    
                    // Add separator between objects in verbose mode
                    printObjectSeparator( output, config.verbose );
                }
                else
                {
                    // Sequential mode doesn't support specific object type
                    // filtering since we read objects one by one without
                    // knowing their type in advance
                    throw std::runtime_error(
                        "Object type filtering not supported in sequential "
                        "mode. "
                        "Use TOC mode (without --sequential) for object type "
                        "filtering." );
                }
            }
        }
        catch ( const std::exception& e )
        {
            // This might be EOF or a real error - let's distinguish
            std::string error_msg = e.what( );
            if ( error_msg.find( "EOF" ) != std::string::npos ||
                 error_msg.find( "end of file" ) != std::string::npos ||
                 error_msg.find( "No more" ) != std::string::npos )
            {
                // Normal EOF - not an error
                if ( config.verbose )
                {
                    *output << str( format( "Reached end of file. Total "
                                            "objects read: %1%" ) %
                                    object_count )
                            << std::endl;
                }
            }
            else
            {
                // Real error
                std::cerr << str( format( "Error during sequential reading "
                                          "(after %1% objects): %2%" ) %
                                  object_count % e.what( ) )
                          << std::endl;
            }
        }

        if ( config.verbose )
        {
            *output << str( format( "Sequential reading complete. Total "
                                    "objects processed: %1%" ) %
                            object_count )
                    << std::endl;
        }
    }
    else
    {
        // TOC mode: process each requested frame
        for ( size_t frame_num : config.frame_numbers )
        {
            if ( config.verbose )
            {
                *output << str( format( "Processing frame %1%..." ) %
                                frame_num )
                        << std::endl;
            }

            try
            {
                boost::shared_ptr< Common::FrameSpec::Object > frame_object;

                if ( config.object_type.empty( ) )
                {
                    // Read entire frame
                    frame_object = source.readFrame( frame_num );
                    if ( frame_object )
                    {
                        // Cast to concrete type for FramePrinter - Object base
                        // class doesn't have serialization
                        printFrameObject(
                            *printer, frame_object.get( ), version, &services );
                        
                        // Add separator between objects in verbose mode
                        printObjectSeparator( output, config.verbose );
                    }
                }
                else
                {
                    // Read specific object type
                    frame_object = source.readFrameObject(
                        frame_num, config.object_type, config.channel_name );
                    if ( frame_object )
                    {
                        // Cast to concrete type for FramePrinter - Object base
                        // class doesn't have serialization
                        printFrameObject(
                            *printer, frame_object.get( ), version, &services );
                        
                        // Add separator between objects in verbose mode
                        printObjectSeparator( output, config.verbose );
                    }
                    else if ( config.verbose )
                    {
                        *output
                            << str( format(
                                        "No %1% object found in frame %2%" ) %
                                    config.object_type % frame_num )
                            << std::endl;
                    }
                }
            }
            catch ( const std::exception& e )
            {
                std::cerr << str( format( "Error processing frame %1%: %2%" ) %
                                  frame_num % e.what( ) )
                          << std::endl;
            }
        }
    }
}

/// \brief Display source information and available objects
void
displaySourceInfo( FrameInputSource& source, const InspectionConfig& config )
{
    std::cout << "Frame Source Information:" << std::endl;
    std::cout << "=========================" << std::endl;
    std::cout << source.getSourceInfo( ) << std::endl;
    std::cout << "Random Access: "
              << ( source.supportsRandomAccess( ) ? "Yes" : "No" ) << std::endl;
    std::cout << std::endl;

    if ( config.list_objects )
    {
        std::cout << "Available object types: adc, proc, sim, ser, event, "
                     "simevent, header"
                  << std::endl;

        // Future enhancement: actually parse TOC to show real available objects
        if ( config.verbose )
        {
            std::cout << "\nNote: Use --list-channels <type> to see specific "
                         "channel names"
                      << std::endl;
        }
    }
}

//=======================================================================
// Main Function
//=======================================================================

int
main( int argc, char* argv[] )
{
    FrameCPP::Initialize( );
    LDASTools::AL::MemChecker::Trigger gc_trigger( true );

    try
    {
        // SERVICES-BASED APPROACH: Following Peter Muldoon's "Retiring the
        // Singleton Pattern"
        // - Explicit service lifetime management
        // - No global/static state that can cause destructor ordering issues
        // - Eliminates need for std::_Exit workarounds
        auto logger_mgr = std::make_unique< LDASTools::AL::LoggerManager >( );
        FrameCPP::Common::FrameCPPServices services( *logger_mgr );

        // Initialize debug system with services (eliminates global static
        // state)
        if ( !FrameCPP::Utilities::FrameCPPDebugManager::
                 initialize_with_services(
                     argc, argv, services, "framecpp_inspector" ) )
        {
            // Clean shutdown before return (no std::_Exit needed)
            logger_mgr->shutdown( );
            return 0; // Help was requested
        }

        FrameCPP::Initialize( );

        InspectionConfig config;

        // Command-line options
        po::options_description desc(
            "framecpp_inspector - Flexible Frame File Inspector\n\nOptions" );
        desc.add_options( )( "help,h", "Show this help message" )(
            "input-method,i",
            po::value< std::string >( )->default_value( "auto" ),
            "Input method: auto (default), stream, archiver" )(
            "frame",
            po::value< std::string >( ),
            "Frame number(s) to inspect (e.g., '5', '1-10', '1,3,5-7')" )(
            "object,o",
            po::value< std::string >( &config.object_type ),
            "Object type to inspect: adc, proc, sim, ser, event, simevent, "
            "header" )( "channel,c",
                        po::value< std::string >( &config.channel_name ),
                        "Specific channel/object name to inspect" )(
            "format",
            po::value< std::string >( )->default_value( "debug" ),
            "Output format: debug (default), json" )(
            "output",
            po::value< std::string >( &config.output_filename ),
            "Output file (default: stdout)" )(
            "version,v",
            po::value< unsigned int >( &config.forced_version ),
            "Force frame specification version (default: auto-detect)" )(
            "alignment",
            po::value< std::string >( )->default_value( "separator" ),
            "Debug format alignment: none, separator, columns" )(
            "indent",
            po::value< int >( &config.indentation )->default_value( 2 ),
            "Indentation spaces for debug format" )(
            "max-field-width",
            po::value< size_t >( &config.max_field_width ),
            "Maximum field name width (0=auto)" )(
            "field-filter",
            po::value< std::string >( &config.field_filter_pattern ),
            "Field filter pattern (future enhancement)" )(
            "list-objects,l", "List available object types" )(
            "list-channels",
            po::value< std::string >( &config.list_channels_type ),
            "List channels of specified type" )(
            "silent-data,s", "Suppress large data arrays in output" )(
            "sequential",
            "Sequential mode - read frames from start without using TOC" )(
            "verbose", "Enable verbose diagnostic output" )(
            "skip-unknown",
            "Skip unknown objects and continue processing (shows object "
            "info)" )(
            "unknown-only",
            "Process only unknown objects for identification purposes" )(
            "walk-objects",
            "Enable walking into container objects for recursive display" )(
            "walk-containers", 
            "Enable walking into PTR_STRUCT containers" )(
            "walk-depth",
            po::value< int >( &config.walk_depth )->default_value( 3 ),
            "Maximum recursion depth for object walking (0=unlimited)" );

        // Add debug options
        po::variables_map vm;
        FrameCPP::Utilities::FrameCPPDebugManager::add_debug_options( desc,
                                                                      vm );

        // Hidden options for positional arguments
        po::options_description hidden("Hidden options");
        hidden.add_options()
            ("input-files", po::value<std::vector<std::string>>(), "Input frame files");
        
        po::options_description all;
        all.add(desc).add(hidden);
        
        // Positional options
        po::positional_options_description pos;
        pos.add("input-files", -1);

        // Parse command line
        po::store( po::command_line_parser(argc, argv)
                    .options(all)
                    .positional(pos)
                    .run(), vm );

        if ( vm.count( "help" ) || argc == 1 )
        {
            std::cout << desc << std::endl;
            std::cout << "\nUsage: framecpp_inspector [options] file [file ...]" << std::endl;
            std::cout << "\nExamples:" << std::endl;
            std::cout << "  framecpp_inspector data.gwf                       "
                         "# Inspect all frames"
                      << std::endl;
            std::cout << "  framecpp_inspector --frame 5 data.gwf             "
                         "# Inspect frame 5"
                      << std::endl;
            std::cout << "  framecpp_inspector --frame 1-5 data.gwf           "
                         "# Inspect frames 1-5"
                      << std::endl;
            std::cout << "  framecpp_inspector -o header --format json data.gwf "
                         "# Header to JSON"
                      << std::endl;
            std::cout << "  framecpp_inspector -o adc -c \"L1:LSC-STRAIN\" data.gwf "
                         "# Specific ADC channel"
                      << std::endl;
            std::cout << "  framecpp_inspector --list-objects data.gwf        "
                         "# Show available objects"
                      << std::endl;
            std::cout << "  framecpp_inspector --walk-containers data.gwf     "
                         "# Walk PTR_STRUCT containers"
                      << std::endl;
            std::cout << "  framecpp_inspector --walk-objects --walk-depth 2 data.gwf "
                         "# Walk objects (max depth 2)"
                      << std::endl;
            std::cout << "  framecpp_inspector data1.gwf data2.gwf data3.gwf  "
                         "# Process multiple files"
                      << std::endl;
            logger_mgr->shutdown( );
            return 0; // Clean shutdown - no std::_Exit needed with services
        }

        po::notify( vm );
        
        // Validate input files
        if (!vm.count("input-files")) {
            std::cerr << "Error: No input files specified" << std::endl;
            std::cerr << "Use --help for usage information." << std::endl;
            logger_mgr->shutdown();
            return 1;
        }
        
        // Get input files
        std::vector<std::string> input_files = vm["input-files"].as<std::vector<std::string>>();

        // Configure debug manager
        FrameCPP::Utilities::FrameCPPDebugManager::configure_from_options( vm );

        // Process options
        config.input_method = FrameInputSourceFactory::parseMethod(
            vm[ "input-method" ].as< std::string >( ) );

        if ( vm.count( "format" ) )
        {
            std::string format_str = vm[ "format" ].as< std::string >( );
            alg::to_lower( format_str );
            if ( format_str == "json" )
            {
                config.output_format = Debug::OutputFormat::JSON;
            }
            else if ( format_str == "debug" )
            {
                config.output_format = Debug::OutputFormat::DEBUG;
            }
            else
            {
                throw std::invalid_argument( "Invalid format: " + format_str +
                                             " (must be: debug, json)" );
            }
        }

        if ( vm.count( "alignment" ) )
        {
            std::string align_str = vm[ "alignment" ].as< std::string >( );
            alg::to_lower( align_str );
            if ( align_str == "none" )
                config.alignment = Debug::AlignmentMode::NONE;
            else if ( align_str == "separator" )
                config.alignment = Debug::AlignmentMode::SEPARATOR;
            else if ( align_str == "columns" )
                config.alignment = Debug::AlignmentMode::COLUMNS;
            else
                throw std::invalid_argument( "Invalid alignment: " +
                                             align_str );
        }

        config.verbose = vm.count( "verbose" ) > 0;
        config.list_objects = vm.count( "list-objects" ) > 0;
        config.silent_data = vm.count( "silent-data" ) > 0;
        config.sequential = vm.count( "sequential" ) > 0;
        config.skip_unknown = vm.count( "skip-unknown" ) > 0;
        config.unknown_only = vm.count( "unknown-only" ) > 0;
        
        // Walking options
        config.walk_objects = vm.count( "walk-objects" ) > 0;
        config.walk_containers = vm.count( "walk-containers" ) > 0;
        // walk_depth is already handled by po::value with default

        // Process all input files
        for (const auto& filename : input_files) {
            if (input_files.size() > 1) {
                std::cout << std::string(60, '=') << std::endl;
                std::cout << "Processing file: " << filename << std::endl;
                std::cout << std::string(60, '=') << std::endl;
            }
            
            // Update config for current file
            config.filename = filename;

            try {
                // Create input source
                auto source = FrameInputSourceFactory::create(
                    config.filename, services, config.input_method, config.sequential );

        // Configure unknown object handling for sequential mode
        if ( config.sequential &&
             ( config.skip_unknown || config.unknown_only ) )
        {
            source->setUnknownObjectHandling( config.skip_unknown,
                                              config.unknown_only );
            if ( config.verbose )
            {
                std::cout << "Unknown object handling: "
                          << ( config.skip_unknown
                                   ? "skip and continue"
                                   : "process only unknown objects" )
                          << std::endl;
            }
        }

        if ( config.verbose )
        {
            std::cout << "Using input method: "
                      << ( config.input_method ==
                                   FrameInputSourceFactory::InputMethod::STREAM
                               ? "IFrameStream"
                               : "Auto-detected" )
                      << std::endl;
        }

        // Parse frame range or default to all frames
        if ( vm.count( "frame" ) )
        {
            if ( config.sequential )
            {
                std::cerr << "Warning: --frame option ignored in sequential mode. Reading all objects until EOF." << std::endl;
                config.frame_numbers.push_back( 0 ); // Dummy entry for loop compatibility
            }
            else
            {
                config.frame_numbers = parseFrameRange(
                    vm[ "frame" ].as< std::string >( ), source->getFrameCount( ) );
            }
        }
        else
        {
            if ( config.sequential )
            {
                // In sequential mode, we read all objects until EOF
                // Frame numbers are not meaningful in this context
                config.frame_numbers.push_back(
                    0 ); // Dummy entry for loop compatibility
                if ( config.verbose )
                {
                    std::cout
                        << "Sequential mode: reading all objects until EOF."
                        << std::endl;
                }
            }
            else
            {
                // Default to all frames (limit to first 10 for safety in TOC
                // mode)
                size_t max_default_frames =
                    std::min( source->getFrameCount( ), size_t( 10 ) );
                for ( size_t i = 0; i < max_default_frames; ++i )
                {
                    config.frame_numbers.push_back( i );
                }
                if ( config.verbose && source->getFrameCount( ) > 10 )
                {
                    std::cout
                        << "Defaulting to first 10 frames. Use --frame to "
                           "specify range."
                        << std::endl;
                }
            }
        }

                // Display source info if requested  
                if ( config.list_objects || config.verbose )
                {
                    displaySourceInfo( *source, config );
                    if ( config.list_objects )
                    {
                        // For list-objects mode, just show info and continue to next file
                        source.reset( );
                        continue;
                    }
                }
                
                // Process frames
                if ( !config.frame_numbers.empty( ) )
                {
                    processFrames( *source, config, services );
                }

                // Check if unknown objects were encountered for this file
                size_t unknown_count = 0;
                if ( source )
                {
                    unknown_count = source->getUnknownObjectCount( );
                    if ( unknown_count > 0 && input_files.size() > 1 )
                    {
                        std::cout << "\nWarning: " << unknown_count
                                  << " unknown object(s) encountered in " << filename << std::endl;
                    }
                }

                // Explicitly destroy source before processing next file
                source.reset( );
                
            } catch (const std::exception& e) {
                std::cerr << "Error processing file '" << filename << "': " << e.what() << std::endl;
                if (input_files.size() == 1) {
                    // Single file mode - exit immediately on error
                    logger_mgr->shutdown();
                    return 1;
                }
                // Multiple file mode - continue with next file
                continue;
            }
        } // End of file processing loop

        // Clean shutdown with explicit service lifetime management (Muldoon pattern)
        logger_mgr->shutdown( );

        return 0; // Normal return - explicit shutdown eliminates static destructor issues
    }
    catch ( const po::required_option& e )
    {
        std::cerr << "Error: Missing required option: " << e.get_option_name( )
                  << std::endl;
        std::cerr << "Use --help for usage information." << std::endl;
        return 1; // Normal error return - services approach eliminates segfault
                  // issues
    }
    catch ( const po::error& e )
    {
        std::cerr << "Command line error: " << e.what( ) << std::endl;
        return 1; // Normal error return - services approach eliminates segfault
                  // issues
    }
    catch ( const std::exception& e )
    {
        std::cerr << "Error: " << e.what( ) << std::endl;
        return 1; // Normal error return - services approach eliminates segfault
                  // issues
    }
    catch ( ... )
    {
        std::cerr << "Unknown error occurred." << std::endl;
        return 1; // Normal error return - services approach eliminates segfault
                  // issues
    }
}

// Restore GCC diagnostic state
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
