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

///=======================================================================
// This program is useful in dumping out the information from a frame
// file.
//
// Currently supported modes are hex and post processed.
//
// :TODO: Currently the Version 6 frames are supported in the post
// :TODO:   process mode.
//
//=======================================================================
#if HAVE_CONFIG_H
#include <framecpp_config.h>
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <csignal>

#include <algorithm>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
#include <list>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>

#include <boost/shared_array.hpp>

#include "ldastoolsal/MemChecker.hh"
#include "ldastoolsal/CommandLineOptions.hh"
#include "ldastoolsal/fstream.hh"
#include "ldastoolsal/unordered_map.hh"
#include "ldastoolsal/regex.hh"
#include "ldastoolsal/regexmatch.hh"
#include "ldastoolsal/reverse.hh"
#include "ldastoolsal/types.hh"

#include "framecpp/Common/Verify.hh"
#include "framecpp/FrameCPP.hh"
#include "framecpp/FrameCPPDebugManager.hh"
#include "framecpp/IFrameStream.hh"
#include "ldastoolsal/Logger.hh"

#include "StandardOptions.hh"
#include "DynamicTOCPrinter.hh"
#include "LegacyTOCPrinter.hh"

using LDASTools::AL::MemChecker;
using LDASTools::AL::unordered_map;

typedef LDASTools::AL::CommandLineOptions CommandLineOptions;
typedef CommandLineOptions::Option        Option;
typedef CommandLineOptions::OptionSet     OptionSet;

typedef INT_2U class_id_type;
typedef INT_2U version_type;

enum
{
    CLASS_ID_COMMON_ELEMENTS = 0,
    CLASS_ID_FrSH = 1,
    CLASS_ID_FrSE = 2
};

enum data_type_type
{
    DATA_TYPE_UNKNOWN,
    DATA_TYPE_CHAR,
    DATA_TYPE_CHAR_U,
    DATA_TYPE_INT_2S,
    DATA_TYPE_INT_2U,
    DATA_TYPE_INT_4S,
    DATA_TYPE_INT_4U,
    DATA_TYPE_INT_8S,
    DATA_TYPE_INT_8U,
    DATA_TYPE_REAL_4,
    DATA_TYPE_REAL_8,
    DATA_TYPE_STRING,
    DATA_TYPE_PTR_STRUCT,
    DATA_TYPE_COMPLEX_8,
    DATA_TYPE_COMPLEX_16
};

class FrameStream;
class FrObject;
class SymbolTable;

//-----------------------------------------------------------------------
/// \brief Base class for all data values stored in Description vectors
///
/// This enables proper exception-safe memory management using std::unique_ptr
/// while maintaining type information for casting back to derived types.
///
/// Memory management strategy:
/// - Description::struct_data_type is std::vector<std::unique_ptr<DataValue>>
/// - FrameStream::Read() returns std::unique_ptr<DataValue> (exception-safe)
/// - Wrapper classes (StringValue, etc.) use std::unique_ptr for owned objects
/// - All cleanup is automatic via unique_ptr destructors (no manual delete)
//-----------------------------------------------------------------------
class DataValue
{
public:
    virtual ~DataValue() {}

    /// \brief Get the data type name for this value
    virtual std::string GetTypeName() const = 0;
};

/// \brief Template wrapper for primitive data types
template<typename T>
class PrimitiveValue : public DataValue
{
public:
    T value;

    explicit PrimitiveValue(T v) : value(v) {}

    std::string GetTypeName() const override
    {
        // Return type name based on actual type
        if (std::is_same<T, CHAR>::value) return "CHAR";
        if (std::is_same<T, CHAR_U>::value) return "CHAR_U";
        if (std::is_same<T, INT_2S>::value) return "INT_2S";
        if (std::is_same<T, INT_2U>::value) return "INT_2U";
        if (std::is_same<T, INT_4S>::value) return "INT_4S";
        if (std::is_same<T, INT_4U>::value) return "INT_4U";
        if (std::is_same<T, INT_8S>::value) return "INT_8S";
        if (std::is_same<T, INT_8U>::value) return "INT_8U";
        if (std::is_same<T, REAL_4>::value) return "REAL_4";
        if (std::is_same<T, REAL_8>::value) return "REAL_8";
        return "UNKNOWN";
    }
};

// Forward declarations for complex types
class STRING;
class PTR_STRUCT;
class ARRAY_TYPE;

/// \brief Wrapper for STRING objects
class StringValue : public DataValue
{
public:
    std::unique_ptr<STRING> value;

    explicit StringValue(STRING* v) : value(v) {}
    ~StringValue() override = default;

    std::string GetTypeName() const override { return "STRING"; }
};

/// \brief Wrapper for PTR_STRUCT objects
class PtrStructValue : public DataValue
{
public:
    std::unique_ptr<PTR_STRUCT> value;

    explicit PtrStructValue(PTR_STRUCT* v) : value(v) {}
    ~PtrStructValue() override = default;

    std::string GetTypeName() const override { return "PTR_STRUCT"; }
};

/// \brief Wrapper for ARRAY_TYPE objects
class ArrayValue : public DataValue
{
public:
    std::unique_ptr<ARRAY_TYPE> value;

    explicit ArrayValue(ARRAY_TYPE* v) : value(v) {}
    ~ArrayValue() override = default;

    std::string GetTypeName() const override { return "ARRAY_TYPE"; }
};

static INT_8U ClassSize( const std::string& Class );

class CommandLine;

inline void
depart( int ExitCode )
{
    exit( ExitCode );
}

//-----------------------------------------------------------------------
// Description declaration
//-----------------------------------------------------------------------
class Description
{
public:
    struct info
    {
        std::string s_name;
        std::string s_class;
        std::string s_comment;
    };

    typedef std::list< info >                         ses_type;
    typedef std::vector< std::unique_ptr<DataValue> > struct_data_type;

    void AddSE( const std::string& Name,
                const std::string& Class,
                const std::string& Comment );

    inline std::string
    Class( INT_4U Index ) const
    {
        std::string retval;

        if ( ( Index > 0 ) && ( Index < m_se.size( ) ) )
        {
            ses_type::const_iterator cur = m_se.begin( );
            while ( Index-- )
            {
                ++cur;
            }
            retval = cur->s_class;
        }
        return retval;
    }

    inline void
    ClassName( const std::string& ClassName )
    {
        m_class_name = ClassName;
    }

    inline const std::string&
    ClassName( ) const
    {
        return m_class_name;
    }

    void Dump( const struct_data_type& Data, const CommandLine& Options ) const;

    void DumpCommonElements( const struct_data_type& Data,
                             const std::string&      ClassName ) const;

    bool Read( FrameStream& FStream, struct_data_type& Data ) const;

    template < typename T >
    T Value( const struct_data_type& Data, INT_2U Offset ) const;

    inline const ses_type& GetSEs() const
    {
        return m_se;
    }

private:
    std::string m_class_name;
    ses_type    m_se;

    void* read( FrameStream& FStream, const std::string& Class );
};

//=======================================================================
// Pre knowledge - These are the classes that need to be known before
//   anything can be read.
//=======================================================================
class FrHeader
{
public:
    bool ByteSwapping( ) const;

    void Dump( ) const;

    void Read( );

    inline INT_2U
    Version( ) const
    {
        return m_data.core.ver;
    }

private:
    union data
    {
        CHAR_U raw[ 40 ];
        struct
        {
            CHAR_U orig[ 5 ];
            CHAR_U ver;
        } core;
        struct
        {
            CHAR_U orig[ 5 ];
            CHAR_U ver;
            CHAR_U rev;
            CHAR_U sizeof_int_2;
            CHAR_U sizeof_int_4;
            CHAR_U sizeof_int_8;
            CHAR_U sizeof_real_4;
            CHAR_U sizeof_real_8;
            CHAR_U order_2_byte[ 2 ];
            CHAR_U order_4_byte[ 4 ];
            CHAR_U order_8_byte[ 8 ];
            CHAR_U pi_4_byte[ 4 ];
            CHAR_U pi_8_byte[ 8 ];
            CHAR_U az[ 2 ];
        } v3;
        struct
        {
            CHAR_U orig[ 5 ];
            CHAR_U ver;
            CHAR_U rev;
            CHAR_U sizeof_int_2;
            CHAR_U sizeof_int_4;
            CHAR_U sizeof_int_8;
            CHAR_U sizeof_real_4;
            CHAR_U sizeof_real_8;
            CHAR_U order_2_byte[ 2 ];
            CHAR_U order_4_byte[ 4 ];
            CHAR_U order_8_byte[ 8 ];
            CHAR_U pi_4_byte[ 4 ];
            CHAR_U pi_8_byte[ 8 ];
            CHAR_U frame_lib;
            CHAR_U checksum_method;
        } v8;
    };

    data m_data;
};

class FrObject
{
public:
    typedef INT_4U class_id_type;
    typedef INT_8U length_type;

    FrObject( FrameStream& Stream );

    void AppendSE( Description& D ) const;

    void Dump( const CommandLine& Options ) const;

    class_id_type FrSHClassId( ) const;

    std::string FrSHClassName( ) const;

    inline length_type
    Length( ) const
    {
        return m_length;
    }

    inline class_id_type
    StructId( ) const
    {
        return m_struct_id;
    }

private:
    union
    {
        struct
        {
            class_id_type class_id;
            char const*   class_name;
        } FrSH;
        struct
        {
        } FrSE;
    } m_cache;

    std::streampos                m_start_pos;
    Description::struct_data_type m_common_elements;
    Description::struct_data_type m_data;

    length_type   m_length;
    class_id_type m_struct_id;
};

class ARRAY_TYPE
{
public:
    static INT_4U m_DumpNElements;

    ARRAY_TYPE( FrameStream&       Stream,
                const std::string& Class,
                const SymbolTable& Symbols );

    ~ARRAY_TYPE( );

    inline static bool
    Match( const std::string& Class )
    {
        RegexMatch m( 3 );

        if ( m.match( pattern, Class.c_str( ) ) )
        {
            return true;
        }
        return false;
    }

    std::string Value( ) const;

private:
    typedef boost::shared_array< CHAR_U > data_type;

    static Regex   pattern;
    static Regex   m_multidimensional_pattern;
    data_type      m_data;
    data_type_type m_data_type;
    INT_4U         m_size;

    template < typename TYPE >
    void dump( std::ostringstream& Stream ) const;

    template < typename TYPE >
    void read( FrameStream& Stream, INT_4U Size );
};

class STRING
{
public:
    STRING( );

    STRING( FrameStream& Stream );

    static inline bool
    Match( const std::string& ClassName )
    {
        return ( ClassName == "STRING" );
    }

    void Read( FrameStream& Stream );

    inline const CHAR*
    Value( ) const
    {
        if ( m_value.get( ) )
        {
            return m_value.get( );
        }
        return "<NULL>";
    }

private:
    INT_2U                      m_length;
    boost::shared_array< CHAR > m_value;
};

class PTR_STRUCT
{
public:
    PTR_STRUCT( FrameStream& Stream );

    static inline bool
    Match( const std::string& ClassName )
    {
        return ( ClassName.substr( 0, 10 ) == "PTR_STRUCT" );
    }

    inline std::string
    Value( ) const
    {
        std::ostringstream retval;

        retval << m_data_class << "-" << m_data_instance;
        return retval.str( );
    }

private:
    INT_2U m_data_class;
    INT_4U m_data_instance;
};

//=======================================================================
// Dynamic interface
//=======================================================================

class InputStream
{
public:
    typedef INT_8U position_type;
    InputStream( );
    ~InputStream( );

    std::istream& Advance( position_type Position );

    std::istream& AdvanceABS( position_type Position );

    inline position_type
    Position( ) const
    {
        return m_position;
    }

    std::istream& Read( void* Buffer, position_type Size );

    std::istream& operator( )( std::istream& Stream );

    inline bool
    good( )
    {
        return ( m_stream && m_stream->good( ) );
    }

private:
    std::istream* m_stream;
    position_type m_position;
};

class FrameStream
{
public:
    typedef INT_2U index_type;
    typedef INT_8U size_type;
    
    FrameStream( );
    ~FrameStream( );

    //---------------------------------------------------------------------
    /// \brief Add a description to list of known objects
    //---------------------------------------------------------------------
    void AddDescription( class_id_type Id, const Description& Description );

    //---------------------------------------------------------------------
    /// \brief Append an object's description
    //---------------------------------------------------------------------
    void AppendDescription( const FrObject& Object, bool Pre );

    //---------------------------------------------------------------------
    /// \brief Obtain the class id index within set of common elements
    //---------------------------------------------------------------------
    inline index_type
    ClassIdIndex( ) const
    {
        return m_class_id_index;
    }

    //---------------------------------------------------------------------
    /// \brief Obtain the class id index within set of common elements
    //---------------------------------------------------------------------
    inline size_type
    CommonElementSize( ) const
    {
        return m_common_element_size;
    }

    //---------------------------------------------------------------------
    /// \brief True if byte swapping is required
    //---------------------------------------------------------------------
    inline bool
    ByteSwapping( ) const
    {
        return m_header.ByteSwapping( );
    }

    //---------------------------------------------------------------------
    /// \brief Retrieve the class definition
    ///
    /// \param Id
    ///     The class id to lookup
    ///
    /// \return
    ///     A reference to the description object describing Id.
    //---------------------------------------------------------------------
    const Description& GetDescription( class_id_type Id ) const;

    //---------------------------------------------------------------------
    /// \brief Obtain the length index within set of common elements
    //---------------------------------------------------------------------
    inline index_type
    LengthIndex( ) const
    {
        return m_length_index;
    }

    //---------------------------------------------------------------------
    /// \brief Read an element from the stream
    ///
    /// \param Class
    ///     The name of the data type being read
    /// \param SymbolTable
    ///     Collection of objects already read which may be consulted
    ///     when reading array data.
    ///
    /// \return
    ///     A unique_ptr to DataValue wrapper containing the data read from the stream
    //---------------------------------------------------------------------
    std::unique_ptr<DataValue> Read( const std::string& Class, const SymbolTable& SymbolTable );

    std::string String( const std::string& Class, DataValue* Data ) const;

    //---------------------------------------------------------------------
    /// \brief True if byte swapping is required
    //---------------------------------------------------------------------
    inline INT_2U
    Version( ) const
    {
        return m_header.Version( );
    }

    //---------------------------------------------------------------------
    /// \brief Set the current filename being processed
    //---------------------------------------------------------------------
    inline void
    SetFilename( const std::string& filename )
    {
        m_filename = filename;
    }

    //---------------------------------------------------------------------
    /// \brief Get the current filename being processed
    //---------------------------------------------------------------------
    inline const std::string&
    GetFilename( ) const
    {
        return m_filename;
    }


    //---------------------------------------------------------------------
    /// \param Filename
    ///     The name of the input file
    //---------------------------------------------------------------------
    void operator( )( LDASTools::AL::ifstream& Filename );

    template < typename T >
    static T Value( const std::string& DataType, DataValue* Data );

private:
    typedef unordered_map< class_id_type, Description > structs_type;

    bool                        m_byte_swapping;
    FrHeader                    m_header;
    structs_type                m_structs;
    index_type                  m_class_id_index;
    index_type                  m_length_index;
    size_type                   m_common_element_size;
    std::string                 m_filename;

    /// \brief Reset all the data members
    void reset( );
};

//-----------------------------------------------------------------------
// SymbolTable
//-----------------------------------------------------------------------
class SymbolTable
{
public:
    typedef const Description::struct_data_type& data_type;
    typedef const Description::ses_type&         data_desc_type;
    SymbolTable( data_desc_type DataDesc, data_type Values )
        : m_data_desc( DataDesc ), m_data( Values )
    {
    }

    inline INT_4U
    size( ) const
    {
        return m_data_desc.size( );
    }

    inline INT_8U
    LookupInteger( const std::string& Key ) const
    {
        INT_8U                                        retval = 0;
        Description::struct_data_type::const_iterator symbol = m_data.begin( );

        std::istringstream m( Key );
        m >> retval;

        if ( retval == 0 )
        {
            for ( Description::ses_type::const_iterator
                      cur = m_data_desc.begin( ),
                      last = m_data_desc.end( );
                  cur != last;
                  ++cur, ++symbol )
            {
                if ( cur->s_name == Key )
                {
                    DataValue* data_ptr = symbol->get();
                    if ( cur->s_class == "CHAR" )
                    {
                        retval = static_cast< PrimitiveValue<CHAR>* >( data_ptr )->value;
                    }
                    else if ( cur->s_class == "CHAR_U" )
                    {
                        retval = static_cast< PrimitiveValue<CHAR_U>* >( data_ptr )->value;
                    }
                    else if ( cur->s_class == "INT_2S" )
                    {
                        retval = static_cast< PrimitiveValue<INT_2S>* >( data_ptr )->value;
                    }
                    else if ( cur->s_class == "INT_2U" )
                    {
                        retval = static_cast< PrimitiveValue<INT_2U>* >( data_ptr )->value;
                    }
                    else if ( cur->s_class == "INT_4S" )
                    {
                        retval = static_cast< PrimitiveValue<INT_4S>* >( data_ptr )->value;
                    }
                    else if ( cur->s_class == "INT_4U" )
                    {
                        retval = static_cast< PrimitiveValue<INT_4U>* >( data_ptr )->value;
                    }
                    else if ( cur->s_class == "INT_8S" )
                    {
                        retval = static_cast< PrimitiveValue<INT_8S>* >( data_ptr )->value;
                    }
                    else if ( cur->s_class == "INT_8U" )
                    {
                        retval = static_cast< PrimitiveValue<INT_8U>* >( data_ptr )->value;
                    }
                    break;
                }
            }
        }
        return retval;
    }

private:
    data_desc_type m_data_desc;
    data_type      m_data;
};

enum
{
    MODE_STRUCT,
    MODE_HEX
};

class Output
{
public:
    Output( std::ostream& Stream );
    ~Output( );

    template < class T >
    std::ostream& operator<<( const T& Data );

private:
    std::ostream& m_stream;
};

static InputStream IStream;
static FrameStream FStream;
static Output      Log( std::cout );

/// @brief Get logger for framecpp_dump_objects utility debugging
/// @return Pointer to logger for framecpp.dump_objects channel
static SimpleLogger* logger() {
    static SimpleLogger* instance = get_channel_logger("framecpp.dump_objects");
    return instance;
}

/// @brief Atexit handler to debug static destruction order issues
static void debug_static_destruction() {
    // Only output debug messages if debug level > 0
    // Use cached level that survives static destruction
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        // Use write() instead of cout/logger to avoid accessing potentially destroyed objects
        const char* msg = "DEBUG: Entering static destruction phase\n";
        if (write(STDERR_FILENO, msg, strlen(msg)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
}

/// @brief Signal handler for segfaults during static destruction
static void segfault_handler(int sig) {
    const char* msg = "DEBUG: SEGFAULT detected during static destruction\n";
    if (write(STDERR_FILENO, msg, strlen(msg)) < 0) {
        // Write failed - ignore silently during static destruction
    }
    _exit(1);  // Use _exit to avoid calling atexit handlers
}

/// @brief Global counter for tracking static destruction order
static int static_destruction_counter = 0;

//-----------------------------------------------------------------------
/// \brief Class to handle command line options for this application
//-----------------------------------------------------------------------
class CommandLine : protected CommandLineOptions
{
public:
    using CommandLineOptions::argc_type;
    using CommandLineOptions::argv_type;

    typedef std::list< std::string > channel_container_type;

    CommandLine( argc_type ArgC, argv_type ArgV );

    inline bool
    BadOption( ) const
    {
        bool retval = false;

        for ( const_iterator cur = begin( ), last = end( ); cur != last; ++cur )
        {
            if ( ( *cur )[ 0 ] == '-' )
            {
                std::cerr << "ABORT: Bad option: " << *cur << std::endl;
                retval = true;
            }
        }
        return retval;
    }

    inline INT_4U
    BlockSize( ) const
    {
        return m_block_size;
    }

    inline INT_4U
    MaximumDataElements( ) const
    {
        return m_maximum_data_elements;
    }

    inline INT_8U
    DataMaxNBytes( ) const
    {
        return m_data_max_n_bytes;
    }

    inline bool
    DataMaxNBytesSet( ) const
    {
        return m_data_max_n_bytes_set;
    }

    inline bool
    MemoryMappedIO( ) const
    {
        return m_memory_mapped_io;
    }

    inline bool
    Silent( ) const
    {
        return m_silent;
    }

    inline bool
    SilentData( ) const
    {
        return m_silent_data;
    }

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

    using CommandLineOptions::empty;
    using CommandLineOptions::Pop;
    using CommandLineOptions::size;

private:
    enum option_types
    {
        OPT_BLOCK_SIZE,
        OPT_DATA_MAX_N_BYTES,
        OPT_DEBUG_CHANNELS,
        OPT_DEBUG_LEVEL,
        OPT_DEBUG_SHOW,
        OPT_HELP,
        OPT_MAXIMUM_DATA_ELEMENTS,
        OPT_MEMORY_MAPPED_IO,
        OPT_SILENT,
        OPT_SILENT_DATA
    };

    OptionSet m_options;
    INT_4U    m_block_size;
    INT_4U    m_maximum_data_elements;
    INT_8U    m_data_max_n_bytes;
    bool      m_data_max_n_bytes_set;
    bool      m_memory_mapped_io;
    bool      m_silent;
    bool      m_silent_data;
};

CommandLine::CommandLine( argc_type ArgC, argv_type ArgV )
    : CommandLineOptions( ArgC, ArgV ), m_block_size( 256 ),
      m_maximum_data_elements( 0 ), m_data_max_n_bytes( 0 ),
      m_data_max_n_bytes_set( false ), m_memory_mapped_io( false ),
      m_silent( false ), m_silent_data( false )
{
    INT_4U multiplier = 1024;

    //---------------------------------------------------------------------
    // Setup the options that will be recognized.
    //---------------------------------------------------------------------
    std::ostringstream desc;

    m_options.Synopsis( "[options] <file> [<file> ...]" );

    m_options.Summary(
        "This command will dump the contents of the selected frame files." );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    desc.str( "" );
    desc << "Specify the size in bytes of the input and output buffer."
            " If the number is followed by the letter 'b', 'k' or 'M',"
            " then the buffer size is multiplied by 1, 1024 or"
            " 1048576 respectively. (Default: "
         << BlockSize( ) << ( ( multiplier == 1024 ) ? "k" : "M" ) << " )";

    m_options.Add( Option( OPT_BLOCK_SIZE,
                           "block-size",
                           Option::ARG_REQUIRED,
                           desc.str( ),
                           "size" ) );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    m_options.Add(
        Option( OPT_HELP, "help", Option::ARG_NONE, "Display this message" ) );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    m_options.Add( Option( OPT_DEBUG_CHANNELS,
                           "debug-channels",
                           Option::ARG_REQUIRED,
                           "Comma-separated list of channel:level pairs for debug output (e.g., \"framecpp.verify:30,framecpp.verify.magic:40\")",
                           "channels" ) );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    m_options.Add( Option( OPT_DEBUG_LEVEL,
                           "debug-level",
                           Option::ARG_REQUIRED,
                           "Global debug level (0-100, default: 0)",
                           "level" ) );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    m_options.Add( Option( OPT_DEBUG_SHOW,
                           "debug-show",
                           Option::ARG_NONE,
                           "Show current debug configuration and exit" ) );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    desc.str( "" );
    desc << "Restricts the number of data elements from the FrVect's data "
            "structure"
            " that are displayed."
            " A value of zero (0) is interpreted as unlimited."
            " (Default: "
         << MaximumDataElements( ) << " )";
    m_options.Add( Option( OPT_MAXIMUM_DATA_ELEMENTS,
                           "maximum-data-elements",
                           Option::ARG_REQUIRED,
                           desc.str( ),
                           "size" ) );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    desc.str( "" );
    desc << "Limit the dump of an FrVect's data field to at most the first"
            " N bytes of its element array; a truncation marker including"
            " the full nBytes is appended when truncation occurs. The data"
            " field is CHAR_U for all current FrameSpec versions, so one"
            " byte equals one element. A value of 0 prints the marker only."
            " When the option is omitted, no limit is applied. Has no"
            " effect when --silent-data is also set.";
    m_options.Add( Option( OPT_DATA_MAX_N_BYTES,
                           "data-max-n-bytes",
                           Option::ARG_REQUIRED,
                           desc.str( ),
                           "nbytes" ) );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    desc.str( "" );
    desc << "Enables memory mapped i/o for the input data."
            " (Default: "
         << ( ( MemoryMappedIO( ) ) ? "enabled" : "disabled" ) << " )";
    m_options.Add( Option( OPT_MEMORY_MAPPED_IO,
                           "memory-mapped-io",
                           Option::ARG_NONE,
                           desc.str( ) ) );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    desc.str( "" );
    desc << "Supress the output of the data structures."
            " (Default: "
         << ( ( Silent( ) ) ? "silent" : "verbose" ) << " )";
    m_options.Add(
        Option( OPT_SILENT, "silent", Option::ARG_NONE, desc.str( ) ) );

    //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    StandardOption< STANDARD_OPTION_SILENT_DATA >(
        m_options, OPT_SILENT_DATA, false );

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

    try
    {
        std::string arg_name;
        std::string arg_value;
        bool        parsing = true;

        while ( parsing )
        {
            const int cmd_id( Parse( m_options, arg_name, arg_value ) );

            switch ( cmd_id )
            {
            case CommandLineOptions::OPT_END_OF_OPTIONS:
                parsing = false;
                break;
            case OPT_HELP:
            {
                Usage( 0 );
            }
            break;
            case OPT_DEBUG_CHANNELS:
                FrameCPP::Utilities::FrameCPPDebugManager::parse_channel_spec( arg_value );
                break;
            case OPT_DEBUG_LEVEL:
            {
                std::istringstream level_stream( arg_value );
                int level;
                level_stream >> level;
                FrameCPP::Utilities::FrameCPPDebugManager::set_global_level_with_cache( level );
            }
            break;
            case OPT_DEBUG_SHOW:
                FrameCPP::Utilities::FrameCPPDebugManager::print_configuration();
                depart( 0 );
                break;
            case OPT_BLOCK_SIZE:
            {
                if ( arg_value.length( ) >= 1 )
                {
                    switch ( arg_value[ arg_value.length( ) - 1 ] )
                    {
                    case 'b':
                        multiplier = 1;
                        break;
                    case 'k':
                        multiplier = 1024;
                        break;
                    case 'M':
                        multiplier = 1024 * 1024;
                        break;
                    default:
                        break;
                    }
                }

                std::istringstream num( arg_value.substr(
                    0,
                    ( multiplier == 1 ) ? std::string::npos
                                        : arg_value.length( ) - 1 ) );
                num >> m_block_size;
            }
            break;
            case OPT_MAXIMUM_DATA_ELEMENTS:
            {
                std::istringstream num( arg_value );

                num >> m_maximum_data_elements;
            }
            break;
            case OPT_DATA_MAX_N_BYTES:
            {
                std::istringstream num( arg_value );

                num >> m_data_max_n_bytes;
                m_data_max_n_bytes_set = true;
            }
            break;
            case OPT_MEMORY_MAPPED_IO:
                m_memory_mapped_io = true;
                break;
            case OPT_SILENT:
                m_silent = true;
                break;
            case OPT_SILENT_DATA:
                m_silent_data = true;
                break;
            }
        }
    }
    catch ( ... )
    {
    }

    m_block_size *= multiplier;
}

int
main( int ArgC, const char** ArgV )
try
{
    // Register debugging handlers for static destruction issues
    atexit(debug_static_destruction);
    signal(SIGSEGV, segfault_handler);
    
    MemChecker::Trigger         gc_trigger( true );
    CommandLine                 cl( ArgC, ArgV );
    boost::shared_array< CHAR > buffer( new CHAR[ cl.BlockSize( ) ] );
    int                         exit_status( 0 );

    if ( cl.empty( ) || cl.BadOption( ) )
    {
        cl.Usage( 1 );
    }

    // Initialize debug manager for environment variables and final setup
    FrameCPP::Utilities::FrameCPPDebugManager::load_from_environment();

    FrameCPP::Initialize( );

    logger_log_debug(logger(), 10, "framecpp_dump_objects initialized, processing ", cl.size(), " file(s)");

    while ( cl.empty( ) == false )
    {
        //-------------------------------------------------------------------
        // Open the stream
        //-------------------------------------------------------------------
        std::string             filename( cl.Pop( ) );
        logger_log_debug(logger(), 10, "Processing file: ", filename);
        
        // Check current working directory
        char cwd_buffer[4096];
        if (getcwd(cwd_buffer, sizeof(cwd_buffer)) != nullptr) {
            logger_log_debug(logger(), 30, "Current working directory: ", cwd_buffer);
        } else {
            logger_log_debug(logger(), 20, "Failed to get current working directory, errno: ", errno);
        }
        
        // Check if file exists using filesystem
        struct stat file_stat;
        if (stat(filename.c_str(), &file_stat) != 0) {
            logger_log_debug(logger(), 20, "File does not exist or stat() failed, errno: ", errno, " (", strerror(errno), ")");
        } else {
            logger_log_debug(logger(), 30, "File exists, size: ", file_stat.st_size, " bytes, mode: ", std::oct, file_stat.st_mode, std::dec);
            logger_log_debug(logger(), 30, "File readable: ", (access(filename.c_str(), R_OK) == 0 ? "yes" : "no"));
        }
        
        LDASTools::AL::ifstream ifile;

        logger_log_debug(logger(), 30, "Setting buffer size: ", cl.BlockSize(), " bytes");
        ifile.rdbuf( )->pubsetbuf( buffer.get( ), cl.BlockSize( ) );
        
        logger_log_debug(logger(), 30, "Setting memory mapped I/O: ", cl.MemoryMappedIO());
        ifile.rdbuf( )->UseMemoryMappedIO( cl.MemoryMappedIO( ) );
        
        logger_log_debug(logger(), 30, "Opening file: ", filename);
        ifile.open( filename.c_str( ) );
        logger_log_debug(logger(), 20, "File stream opened, checking status for: ", filename);

        // Detailed stream state debugging
        logger_log_debug(logger(), 40, "Stream state - good(): ", ifile.good(), ", fail(): ", ifile.fail(), ", bad(): ", ifile.bad(), ", eof(): ", ifile.eof());
        logger_log_debug(logger(), 40, "Stream rdstate(): ", ifile.rdstate());
        
        if ( !ifile.good( ) )
        {
            logger_log_debug(logger(), 10, "Failed to open file, skipping: ", filename);
            if (ifile.fail()) {
                logger_log_debug(logger(), 20, "Stream fail bit set - file may not exist or permissions issue");
            }
            if (ifile.bad()) {
                logger_log_debug(logger(), 20, "Stream bad bit set - serious I/O error");
            }
            if (ifile.eof()) {
                logger_log_debug(logger(), 20, "Stream EOF bit set - unexpected end of file");
            }
            continue;
        }
        logger_log_debug(logger(), 20, "File stream is good, proceeding with: ", filename);

        try
        {
            //-----------------------------------------------------------------
            // Read the FrameStream structure and write out the information
            //-----------------------------------------------------------------
            logger_log_debug(logger(), 20, "Reading FrameStream structure from: ", filename);
            FStream.SetFilename( filename );
            FStream( ifile );
            logger_log_debug(logger(), 30, "FrameStream structure read successfully");

            //-----------------------------------------------------------------
            // Read and then write all of the next elements.
            //-----------------------------------------------------------------
            logger_log_debug(logger(), 20, "Starting object reading loop");
            int object_count = 0;
            while ( 1 )
            {
                std::streampos oldpos = ifile.tellg( );
                logger_log_debug(logger(), 50, "Reading object #", object_count + 1, " at stream position: ", oldpos);

                try
                {
                    logger_log_debug(logger(), 50, "Creating FrObject from stream");
                    FrObject o( FStream );
                    logger_log_debug(logger(), 50, "FrObject created successfully");

                    if ( ifile.eof( ) || ( !ifile.good( ) ) ||
                         ( oldpos == ifile.tellg( ) ) )
                    {
                        logger_log_debug(logger(), 30, "End of stream reached, processed ", object_count, " objects");
                        break;
                    }
                    logger_log_debug(logger(), 40, "Dumping object #", object_count + 1);
                    o.Dump( cl );
                    ++object_count;
                    logger_log_debug(logger(), 40, "Object #", object_count, " dumped successfully");
                }
                catch ( const std::range_error& Error )
                {
                    logger_log_debug(logger(), 20, "Range error in object reading loop, processed ", object_count, " objects: ", Error.what());
                    break;
                }
            }
            logger_log_debug(logger(), 20, "Completed processing file: ", filename, ", total objects processed: ", object_count);
        }
        catch ( const std::exception& e )
        {
            //-----------------------------------------------------------------
            // Report failure back to user
            //-----------------------------------------------------------------
            logger_log_debug(logger(), 10, "Exception while processing file ", filename, ": ", e.what());
            std::cerr << "FAILURE: " << filename << ": " << e.what( )
                      << std::endl;
            exit_status = 1;
        }
        catch ( ... )
        {
            //-----------------------------------------------------------------
            // An unexpected error occured while reading the file.
            // Abandon further reading of the file.
            //-----------------------------------------------------------------
            logger_log_debug(logger(), 10, "Unknown exception while processing file: ", filename);
            std::cerr << "FAILURE: unknown exception" << std::endl;
            exit_status = 1;
        }
        //-------------------------------------------------------------------
        // Close the stream
        //-------------------------------------------------------------------
        logger_log_debug(logger(), 30, "Closing file stream for: ", filename);
        ifile.close( );
    }
    logger_log_debug(logger(), 10, "framecpp_dump_objects completed processing all files, exit status: ", exit_status);
    depart( exit_status );
}
catch ( std::exception& e )
{
    std::cerr << "ABORT: Caught exception: " << e.what( ) << std::endl;
    depart( 1 );
}

//=======================================================================
// Description - Definition
//=======================================================================
//-----------------------------------------------------------------------
/// Add an FrSE element to the definition
///
/// \param Name
///     Name of an element of the structure being described.
/// \param Class
///     Contains the string representation of the data type
/// \param Comment
///     Description of the element and its purpose.
//-----------------------------------------------------------------------
void
Description::AddSE( const std::string& Name,
                    const std::string& Class,
                    const std::string& Comment )
{
    info i;

    i.s_name = Name;
    i.s_class = Class;
    i.s_comment = Comment;

    m_se.push_back( i );
}

//-----------------------------------------------------------------------
/// \brief write out description
//-----------------------------------------------------------------------
void
Description::Dump( const struct_data_type& Data,
                   const CommandLine&      Options ) const
{
    size_t index = 0;
    bool   is_fr_vect( ClassName( ).compare( "FrVect" ) == 0 );
    INT_8U fr_vect_nbytes = 0;

    for ( ses_type::const_iterator cur = m_se.begin( ), last = m_se.end( );
          cur != last;
          ++cur, ++index )
    {
        Log << "  " << cur->s_name << ": ";
        if ( ( is_fr_vect ) && ( cur->s_name.compare( "data" ) == 0 ) )
        {
            if ( Options.SilentData( ) )
            {
                Log << "...";
            }
            else if ( Options.DataMaxNBytesSet( )
                      && Options.DataMaxNBytes( ) == 0 )
            {
                // explicit zero: print marker only, never materialize the array
                Log << "[truncated; " << fr_vect_nbytes
                    << " total bytes, --data-max-n-bytes=0]";
            }
            else
            {
                INT_4U old_max = ARRAY_TYPE::m_DumpNElements;
                INT_4U effective_max = Options.MaximumDataElements( );
                if ( Options.DataMaxNBytesSet( ) )
                {
                    // FrVect.data is CHAR_U for all v3-v9, so 1 byte = 1 element.
                    const INT_8U raw = Options.DataMaxNBytes( );
                    const INT_4U byte_cap =
                        ( raw > std::numeric_limits< INT_4U >::max( ) )
                        ? std::numeric_limits< INT_4U >::max( )
                        : static_cast< INT_4U >( raw );
                    effective_max = ( effective_max == 0 )
                        ? byte_cap
                        : std::min( effective_max, byte_cap );
                }
                ARRAY_TYPE::m_DumpNElements = effective_max;
                Log << FStream.String( cur->s_class, Data[index].get() );
                ARRAY_TYPE::m_DumpNElements = old_max;

                if ( Options.DataMaxNBytesSet( )
                     && fr_vect_nbytes > Options.DataMaxNBytes( ) )
                {
                    Log << " ... [truncated; " << fr_vect_nbytes
                        << " total bytes, --data-max-n-bytes="
                        << Options.DataMaxNBytes( ) << "]";
                }
            }
        }
        else
        {
            std::string formatted =
                FStream.String( cur->s_class, Data[index].get() );
            Log << formatted;
            if ( is_fr_vect && cur->s_name.compare( "nBytes" ) == 0 )
            {
                std::istringstream is( formatted );
                is >> fr_vect_nbytes;
            }
        }
        Log << "" << std::endl;
    }
}

//-----------------------------------------------------------------------
/// \brief write out description
//-----------------------------------------------------------------------
void
Description::DumpCommonElements( const struct_data_type& Data,
                                 const std::string&      ClassName ) const
{
    size_t index = 0;

    for ( ses_type::const_iterator cur = m_se.begin( ), last = m_se.end( );
          cur != last;
          ++cur, ++index )
    {
        Log << " " << cur->s_name;
        if ( cur->s_name == "class" )
        {
            Log << " (" << ClassName << ")";
        }
        Log << ": " << FStream.String( cur->s_class, Data[index].get() );
    }
}

//-----------------------------------------------------------------------
/// Read data from the stream using the given description.
//-----------------------------------------------------------------------
bool
Description::Read( FrameStream& FStream, struct_data_type& Data ) const
{
    Data.resize( m_se.size( ) );
    size_t index = 0;

    SymbolTable symbols( m_se, Data );
    bool        complete( true );

    ses_type::const_iterator cur = m_se.begin( ), last = m_se.end( );
    while ( cur != last )
    {
        try
        {
            Data[index] = FStream.Read( cur->s_class, symbols );
            ++cur;
            ++index;
        }
        catch ( ... )
        {
            complete = false;
            break;
        }
    }
    if ( !complete )
    {
        while ( cur != last )
        {
            Data[index].reset( nullptr );
            ++cur;
            ++index;
        }
    }

    return complete;
}

template < typename T >
T
Description::Value( const struct_data_type& Data, INT_2U Offset ) const
{
    ses_type::const_iterator cur = m_se.begin( ), last = m_se.end( );
    INT_2U                   x = Offset;

    while ( ( cur != last ) && x )
    {
        ++cur;
        --x;
    }
    return FrameStream::Value< T >( cur->s_class, Data[ Offset ].get() );
}

//=======================================================================
// FrHeader
//=======================================================================
bool
FrHeader::ByteSwapping( ) const
{
    //---------------------------------------------------------------------
    // Determinate byte ordering
    //---------------------------------------------------------------------
    INT_4U sx12345678 = 0;

    if ( Version( ) >= 8 )
    {
        ::memcpy( &sx12345678, m_data.v8.order_4_byte, sizeof( sx12345678 ) );
    }
    else if ( Version( ) >= 3 )
    {
        ::memcpy( &sx12345678, m_data.v3.order_4_byte, sizeof( sx12345678 ) );
    }
    else
    {
        std::ostringstream msg;

        msg << "FrHeader::ByteSwapping: not supported for version: "
            << Version( );
        throw std::range_error( msg.str( ) );
    }

    return ( sx12345678 != 0x12345678 );
}

void
FrHeader::Dump( ) const
{
    Log << "Bytes: 0-4:   " << m_data.core.orig << std::endl
        << "Bytes: 5:     " << int( m_data.core.ver ) << std::endl;
    if ( Version( ) >= 8 )
    {
        std::uint16_t order_2_bytes;
        std::uint32_t order_4_bytes;
        std::uint64_t order_8_bytes;

        std::memcpy( &order_2_bytes,
                     m_data.v8.order_2_byte,
                     sizeof( order_2_bytes ) ); // 12 - 13
        std::memcpy( &order_4_bytes,
                     m_data.v8.order_4_byte,
                     sizeof( order_4_bytes ) ); // 14 - 17
        std::memcpy( &order_8_bytes,
                     m_data.v8.order_8_byte,
                     sizeof( order_8_bytes ) ); // 18 - 25
        Log << "Bytes: 6:     " << int( m_data.v8.rev ) << std::endl
            << "Bytes: 7:     " << int( m_data.v8.sizeof_int_2 ) << std::endl
            << "Bytes: 8:     " << int( m_data.v8.sizeof_int_4 ) << std::endl
            << "Bytes: 9:     " << int( m_data.v8.sizeof_int_8 ) << std::endl
            << "Bytes: 10:    " << int( m_data.v8.sizeof_real_4 ) << std::endl
            << "Bytes: 11:    " << int( m_data.v8.sizeof_real_8 ) << std::endl
            << "Bytes: 12-13  0x" << std::hex << order_2_bytes << std::dec
            << std::endl
            << "Bytes: 14-17  0x" << std::hex << order_4_bytes << std::dec
            << std::endl
            << "Bytes: 18-25  0x" << std::hex << order_8_bytes << std::dec
            << std::endl
            << "Bytes: 38:    " << int( m_data.v8.frame_lib ) << std::endl
            << "Bytes: 39:    " << int( m_data.v8.checksum_method )
            << std::endl;
    }
    else if ( Version( ) >= 3 )
    {
        std::uint16_t order_2_bytes;
        std::uint32_t order_4_bytes;
        std::uint64_t order_8_bytes;

        std::memcpy( &order_2_bytes,
                     m_data.v3.order_2_byte,
                     sizeof( order_2_bytes ) ); // 12 - 13
        std::memcpy( &order_4_bytes,
                     m_data.v3.order_4_byte,
                     sizeof( order_4_bytes ) ); // 14 - 17
        std::memcpy( &order_8_bytes,
                     m_data.v3.order_8_byte,
                     sizeof( order_8_bytes ) ); // 18 - 25
        Log << "Bytes: 6:     " << int( m_data.v3.rev ) << std::endl
            << "Bytes: 7:     " << int( m_data.v3.sizeof_int_2 ) << std::endl
            << "Bytes: 8:     " << int( m_data.v3.sizeof_int_4 ) << std::endl
            << "Bytes: 9:     " << int( m_data.v3.sizeof_int_8 ) << std::endl
            << "Bytes: 10:    " << int( m_data.v3.sizeof_real_4 ) << std::endl
            << "Bytes: 11:    " << int( m_data.v3.sizeof_real_8 ) << std::endl
            << "Bytes: 12-13  0x" << std::hex << order_2_bytes << std::dec
            << std::endl
            << "Bytes: 14-17  0x" << std::hex << order_4_bytes << std::dec
            << std::endl
            << "Bytes: 18-25  0x" << std::hex << order_8_bytes << std::dec
            << std::endl
            << "Bytes: 38-39: " << m_data.v3.az[ 0 ] << m_data.v3.az[ 1 ]
            << std::endl;
    }
}

void
FrHeader::Read( )
{
    static const INT_2U core_size( sizeof( m_data.core ) );

    FrameCPP::Common::Verify verifier;

    IStream.Read( &m_data.core, core_size );

    if ( IStream.good( ) == false )
    {
        throw std::runtime_error( "Failed to read FrHeader from the stream" );
    }

    if ( Version( ) < 8 )
    {
        IStream.Read( &m_data.v3.rev, sizeof( m_data.v3 ) - core_size );
        verifier.ExamineMagicNumber< 2 >( m_data.v3.order_2_byte );
        verifier.ExamineMagicNumber< 4 >( m_data.v3.order_4_byte );
        verifier.ExamineMagicNumber< 8 >( m_data.v3.order_8_byte );
    }
    else
    {
        IStream.Read( &m_data.v8.rev, sizeof( m_data.v8 ) - core_size );
        verifier.ExamineMagicNumber< 2 >( m_data.v8.order_2_byte );
        verifier.ExamineMagicNumber< 4 >( m_data.v8.order_4_byte );
        verifier.ExamineMagicNumber< 8 >( m_data.v8.order_8_byte );
    }

    if ( IStream.good( ) == false )
    {
        throw std::runtime_error( "Failed to read FrHeader from the stream" );
    }
}

//=======================================================================
// FrObject class definition
//=======================================================================

FrObject::FrObject( FrameStream& Stream )
{
    //---------------------------------------------------------------------
    // Record where this object started
    //---------------------------------------------------------------------
    m_start_pos = IStream.Position( );
    //---------------------------------------------------------------------
    // Read the common elements
    //---------------------------------------------------------------------
    const Description& ce_desc =
        Stream.GetDescription( CLASS_ID_COMMON_ELEMENTS );

    ce_desc.Read( Stream, m_common_elements );
    if ( !IStream.good( ) )
    {
        throw std::range_error( "EOF encountered" );
    }
    m_struct_id = ce_desc.Value< class_id_type >( m_common_elements,
                                                  FStream.ClassIdIndex( ) );
    m_length = ce_desc.Value< length_type >( m_common_elements,
                                             FStream.LengthIndex( ) );
    //---------------------------------------------------------------------
    // Read the remainder of the frame object
    //---------------------------------------------------------------------
    Stream.AppendDescription( *this, true );
    try
    {
        const Description& obj_desc = Stream.GetDescription( StructId( ) );

        length_type end = ( IStream.Position( ) +
                            ( Length( ) - FStream.CommonElementSize( ) ) );

        bool complete = obj_desc.Read( Stream, m_data );
        if ( !complete )
        {
            // If the object has not been defined, then advance to the start
            //   of the next object
            IStream.AdvanceABS( end );
        }
        if ( ( StructId( ) == CLASS_ID_FrSH ) ||
             ( StructId( ) == CLASS_ID_FrSE ) )
        {
            if ( StructId( ) == CLASS_ID_FrSH )
            {
                //---------------------------------------------------------------
                // Get the class id
                //---------------------------------------------------------------
                const Description& d = Stream.GetDescription( CLASS_ID_FrSH );

                std::istringstream s(
                    Stream.String( d.Class( 1 ), m_data[ 1 ].get() ) );

                s >> m_cache.FrSH.class_id;
                //---------------------------------------------------------------
                // Get teh class name
                //---------------------------------------------------------------
                StringValue* name_wrapper = static_cast< StringValue* >( m_data[ 0 ].get() );
                m_cache.FrSH.class_name = name_wrapper->value->Value( );
            }
            FStream.AppendDescription( *this, false );
        }
    }
    catch ( ... )
    {
        // If the object has not been defined, then advance to the start
        //   of the next object
        INT_8U length = ce_desc.Value< INT_8U >( m_common_elements,
                                                 FStream.LengthIndex( ) );
        IStream.Advance( length - FStream.CommonElementSize( ) );
    }
}

void
FrObject::AppendSE( Description& D ) const
{
    D.AddSE( FStream.String( "STRING", m_data[ 0 ].get() ),
             FStream.String( "STRING", m_data[ 1 ].get() ),
             FStream.String( "STRING", m_data[ 2 ].get() ) );
}

FrObject::class_id_type
FrObject::FrSHClassId( ) const
{
    class_id_type retval = 0;

    if ( StructId( ) == CLASS_ID_FrSH )
    {
        retval = m_cache.FrSH.class_id;
    }

    return retval;
}

std::string
FrObject::FrSHClassName( ) const
{
    std::string retval;

    if ( StructId( ) == CLASS_ID_FrSH )
    {
        retval = m_cache.FrSH.class_name;
    }

    return retval;
}

void
FrObject::Dump( const CommandLine& Options ) const
{
    std::string        classname( "Unknown" );
    const Description& ce_desc =
        FStream.GetDescription( CLASS_ID_COMMON_ELEMENTS );

    const Description* obj_desc = (const Description*)NULL;
    try
    {
        INT_4U             cid = ce_desc.Value< INT_4U >( m_common_elements,
                                              FStream.ClassIdIndex( ) );
        const Description& d = FStream.GetDescription( cid );

        obj_desc = &d;
        classname = d.ClassName( );
    }
    catch ( ... )
    {
    }

    Log << "Position: " << m_start_pos;
    ce_desc.DumpCommonElements( m_common_elements, classname );
    Log << "" << std::endl;

    // Special handling for FrTOC - use version-appropriate printer
    if ( classname == "FrTOC" && !Options.Silent() )
    {
        try
        {
            // Create a temporary IFrameFStream to read the TOC
            FrameCPP::IFrameFStream temp_stream( FStream.GetFilename().c_str() );

            // Get the TOC (returns boost::shared_ptr<const Universal::FrTOC>)
            auto toc_ptr = temp_stream.GetTOC();

            if ( toc_ptr )
            {
                // Dynamically cast from Common::FrTOC to Universal::FrTOC
                const FrameCPP::Universal::FrTOC* universal_toc =
                    dynamic_cast<const FrameCPP::Universal::FrTOC*>( toc_ptr.get() );

                if ( universal_toc )
                {
                    unsigned int version = FStream.Version();

                    // Dispatch based on version
                    if ( version >= 8 )
                    {
                        // v8+: Try to use DynamicTOCPrinter with Description from FStream
                        try
                        {
                            const Description& toc_desc = FStream.GetDescription( StructId() );

                            // Create DynamicTOCPrinter and print using local Description
                            FrameCPP::Test::DynamicTOCPrinter printer( std::cout );

                            std::cout << "=== FrTOC - Description-Driven Printer ===" << std::endl;
                            std::cout << "Structure: " << toc_desc.ClassName() << std::endl << std::endl;

                            // Iterate over Description's ses_type list
                            for ( const auto& se : toc_desc.GetSEs() )
                            {
                                auto it = printer.GetFieldPrinters().find( se.s_name );
                                if ( it != printer.GetFieldPrinters().end() )
                                {
                                    std::cout << std::setw(25) << std::left << se.s_name << ": ";
                                    try
                                    {
                                        it->second( universal_toc, std::cout );
                                        std::cout << std::endl;
                                    }
                                    catch ( const std::exception& e )
                                    {
                                        std::cout << "<error: " << e.what() << ">" << std::endl;
                                    }
                                }
                                else
                                {
                                    std::cout << std::setw(25) << std::left << se.s_name << ": <no helper>" << std::endl;
                                }
                            }
                            std::cout << std::endl;
                        }
                        catch ( const std::exception& e )
                        {
                            std::cout << "Warning: Could not get FrTOC Description: " << e.what() << std::endl;
                            std::cout << "Falling back to LegacyTOCPrinter" << std::endl;
                            FrameCPP::Test::LegacyTOCPrinter printer( std::cout );
                            printer.print( universal_toc, version );
                        }
                    }
                    else
                    {
                        // v<8: Use LegacyTOCPrinter
                        FrameCPP::Test::LegacyTOCPrinter printer( std::cout );
                        printer.print( universal_toc, version );
                    }
                }
                else
                {
                    std::cout << "Warning: TOC is not a Universal::FrTOC, falling back to raw dump" << std::endl;
                    if ( obj_desc )
                    {
                        obj_desc->Dump( m_data, Options );
                    }
                }
            }
            else
            {
                std::cout << "Warning: No TOC available in file" << std::endl;
                if ( obj_desc )
                {
                    obj_desc->Dump( m_data, Options );
                }
            }
        }
        catch ( const std::exception& e )
        {
            std::cout << "Warning: Could not format TOC: " << e.what() << std::endl;
            std::cout << "Falling back to raw dump:" << std::endl;
            if ( obj_desc )
            {
                obj_desc->Dump( m_data, Options );
            }
        }
    }
    else if ( obj_desc && ( Options.Silent( ) == false ) )
    {
        obj_desc->Dump( m_data, Options );
    }

    if ( classname.compare( "Unknown" ) == 0 )
    {
        throw std::runtime_error( "Unknown object" );
    }
}

//=======================================================================
// InputStream class definition
//=======================================================================

InputStream::InputStream( )
{
    // Only output debug messages if debug level > 0
    // Use cached level that survives static destruction
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        const char* msg = "DEBUG: InputStream constructor called\n";
        if (write(STDERR_FILENO, msg, strlen(msg)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
}

InputStream::~InputStream( )
{
    // Only output debug messages if debug level > 0
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        char msg[100];
        snprintf(msg, sizeof(msg), "DEBUG: InputStream destructor started (order #%d)\n", ++static_destruction_counter);
        if (write(STDERR_FILENO, msg, strlen(msg)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
    
    // Safe cleanup - don't access potentially destroyed objects
    m_stream = nullptr;
    m_position = 0;
    
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        const char* msg2 = "DEBUG: InputStream destructor completed\n";
        if (write(STDERR_FILENO, msg2, strlen(msg2)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
}

std::istream&
InputStream::Advance( position_type Position )
{
    m_stream->seekg( Position, std::ios_base::cur );
    m_position += Position;
    return *m_stream;
}

std::istream&
InputStream::AdvanceABS( position_type Position )
{
    m_stream->seekg( Position, std::ios_base::beg );
    m_position = Position;
    return *m_stream;
}

std::istream&
InputStream::Read( void* Buffer, position_type Size )
{
    m_stream->read( (char*)Buffer, Size );

    m_position += m_stream->gcount( );

    return *m_stream;
}

std::istream&
InputStream::operator( )( std::istream& Stream )
{
    m_stream = &Stream;
    m_position = position_type( 0 );
    return *m_stream;
}

//=======================================================================
// FrameStream class definition
//=======================================================================

FrameStream::FrameStream( )
{
    // Only output debug messages if debug level > 0
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        const char* msg = "DEBUG: FrameStream constructor called\n";
        if (write(STDERR_FILENO, msg, strlen(msg)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
}

FrameStream::~FrameStream( )
{
    // Only output debug messages if debug level > 0
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        char msg[100];
        snprintf(msg, sizeof(msg), "DEBUG: FrameStream destructor started (order #%d)\n", ++static_destruction_counter);
        if (write(STDERR_FILENO, msg, strlen(msg)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
    
    // Safe cleanup - clear internal containers
    try {
        m_structs.clear();
        m_class_id_index = 0;
        if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
            const char* msg2 = "DEBUG: FrameStream internal cleanup completed\n";
            if (write(STDERR_FILENO, msg2, strlen(msg2)) < 0) {
                // Write failed - ignore silently during static destruction
            }
        }
    } catch (...) {
        if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
            const char* err = "DEBUG: FrameStream internal cleanup failed with exception\n";
            if (write(STDERR_FILENO, err, strlen(err)) < 0) {
                // Write failed - ignore silently during static destruction
            }
        }
    }
    
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        const char* msg3 = "DEBUG: FrameStream destructor completed\n";
        if (write(STDERR_FILENO, msg3, strlen(msg3)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
}

void
FrameStream::AddDescription( class_id_type Id, const Description& Description )
{
    m_structs[ Id ] = Description;
}

void
FrameStream::AppendDescription( const FrObject& Object, bool Pre )
{
    static class_id_type                  id = 0;
    static std::unique_ptr< Description > d( (Description*)NULL );

    if ( Pre )
    {
        if ( ( Object.StructId( ) > 2 ) && ( d.get( ) ) )
        {
            AddDescription( id, *d );
            id = 0;
            d.reset( (Description*)NULL );
        }
    }
    else
    {
        if ( Object.StructId( ) == 1 )
        {
            if ( d.get( ) != (Description*)NULL )
            {
                AddDescription( id, *d );
            }
            d.reset( new Description );
            id = Object.FrSHClassId( );
            d->ClassName( Object.FrSHClassName( ) );
        }
        else if ( Object.StructId( ) == 2 )
        {
            if ( d.get( ) != (Description*)NULL )
            {
                Object.AppendSE( *d );
            }
        }
    }
}

const Description&
FrameStream::GetDescription( class_id_type Id ) const
{
    structs_type::const_iterator cur = m_structs.find( Id );

    if ( cur == m_structs.end( ) )
    {
        throw std::range_error( "Object description does not exist" );
    }
    return cur->second;
}

std::unique_ptr<DataValue>
FrameStream::Read( const std::string& Class, const SymbolTable& Symbols )
{
    std::unique_ptr<DataValue> retval;

#define READER( T )                                                            \
    ( Class == #T )                                                            \
    {                                                                          \
        T raw_value;                                                           \
        IStream.Read( &raw_value, sizeof( T ) );                               \
        if ( m_byte_swapping )                                                 \
        {                                                                      \
            reverse< sizeof( T ) >( &raw_value, 1 );                           \
        }                                                                      \
        retval = std::make_unique<PrimitiveValue<T>>(raw_value);              \
    }

    if
        READER( CHAR )
    else if
        READER( CHAR_U )
    else if
        READER( INT_2U )
    else if
        READER( INT_2S )
    else if
        READER( INT_4U )
    else if
        READER( INT_4S )
    else if
        READER( INT_8U )
    else if
        READER( INT_8S )
    else if
        READER( REAL_4 )
    else if
        READER( REAL_8 )
    else if ( STRING::Match( Class ) )
    {
        retval = std::make_unique<StringValue>( new STRING( *this ) );
    }
    else if ( PTR_STRUCT::Match( Class ) )
    {
        retval = std::make_unique<PtrStructValue>( new PTR_STRUCT( *this ) );
    }
    else if ( ARRAY_TYPE::Match( Class ) )
    {
        retval = std::make_unique<ArrayValue>( new ARRAY_TYPE( *this, Class, Symbols ) );
    }
    else
    {
        throw std::runtime_error( "Unable to read class" );
    }

#undef READER

    return retval;
}

std::string
FrameStream::String( const std::string& Class, DataValue* Data ) const
{
    std::ostringstream retval( "" );

#define CONVERT( T )                                                           \
    ( Class.compare( #T ) == 0 )                                               \
    {                                                                          \
        PrimitiveValue<T>* wrapper = static_cast< PrimitiveValue<T>* >( Data );\
        retval << wrapper->value;                                              \
    }

    if ( Data == nullptr )
    {
        retval << "NULL";
    }
    else if
        CONVERT( INT_2U )
    else if
        CONVERT( INT_2S )
    else if
        CONVERT( INT_4U )
    else if
        CONVERT( INT_4S )
    else if
        CONVERT( INT_8U )
    else if
        CONVERT( INT_8S )
    else if
        CONVERT( REAL_4 )
    else if
        CONVERT( REAL_8 )
    else if ( STRING::Match( Class ) )
    {
        StringValue* wrapper = static_cast< StringValue* >( Data );
        retval << wrapper->value->Value( );
    }
    else if ( PTR_STRUCT::Match( Class ) )
    {
        PtrStructValue* wrapper = static_cast< PtrStructValue* >( Data );
        retval << wrapper->value->Value( );
    }
    else if ( ARRAY_TYPE::Match( Class ) )
    {
        ArrayValue* wrapper = static_cast< ArrayValue* >( Data );
        retval << wrapper->value->Value( );
    }
    else if ( Class.compare( "CHAR" ) == 0 )
    {
        PrimitiveValue<CHAR>* wrapper = static_cast< PrimitiveValue<CHAR>* >( Data );
        retval << INT_2S( wrapper->value );
    }
    else if ( Class.compare( "CHAR_U" ) == 0 )
    {
        PrimitiveValue<CHAR_U>* wrapper = static_cast< PrimitiveValue<CHAR_U>* >( Data );
        retval << INT_2U( wrapper->value );
    }
    else
    {
        retval << "Unknown class: " << Class;
    }
#undef CONVERT

    return retval.str( );
}

template <>
INT_4U
FrameStream::Value( const std::string& DataType, DataValue* Data )
{
    INT_4U retval;

#define CONVERT( T )                                                           \
    ( DataType == #T )                                                         \
    {                                                                          \
        retval = static_cast< PrimitiveValue<T>* >( Data )->value;            \
    }

    if
        CONVERT( CHAR )
    else if
        CONVERT( CHAR_U )
    else if
        CONVERT( INT_2S )
    else if
        CONVERT( INT_2U )
    else if
        CONVERT( INT_4S )
    else if
        CONVERT( INT_4U )
    else
    {
        throw std::runtime_error( "Unable to convert value to INT_4U" );
    }
    return retval;
}

template <>
INT_8U
FrameStream::Value( const std::string& DataType, DataValue* Data )
{
    INT_8U retval;

#define CONVERT( T )                                                           \
    ( DataType == #T )                                                         \
    {                                                                          \
        retval = static_cast< PrimitiveValue<T>* >( Data )->value;            \
    }

    if
        CONVERT( CHAR )
    else if
        CONVERT( CHAR_U )
    else if
        CONVERT( INT_2S )
    else if
        CONVERT( INT_2U )
    else if
        CONVERT( INT_4S )
    else if
        CONVERT( INT_4U )
    else if
        CONVERT( INT_8S )
    else if
        CONVERT( INT_8U )
    else
    {
        throw std::runtime_error( "Unable to convert value to INT_4U" );
    }
    return retval;
}

void
FrameStream::operator( )( LDASTools::AL::ifstream& File )
{
    //---------------------------------------------------------------------
    // Open the file
    //---------------------------------------------------------------------
    IStream( File );
    //---------------------------------------------------------------------
    // Read the header
    //---------------------------------------------------------------------
    m_header.Read( );
    m_header.Dump( );
    m_byte_swapping = m_header.ByteSwapping( );
    //---------------------------------------------------------------------
    // Seed the Descriptions for FrSH and FrSE
    //---------------------------------------------------------------------
    {
        Description ce;
        Description sh;
        Description se;

        if ( m_header.Version( ) < 8 )
        {
            sh.ClassName( "FrSH" );
            sh.AddSE( "name", "STRING", "" );
            sh.AddSE( "class", "INT_2U", "" );
            sh.AddSE( "comment", "STRING", "" );

            se.ClassName( "FrSE" );
            se.AddSE( "name", "STRING", "" );
            se.AddSE( "class", "STRING", "" );
            se.AddSE( "comment", "STRING", "" );
        }
        else
        {
            sh.ClassName( "FrSH" );
            sh.AddSE( "name", "STRING", "" );
            sh.AddSE( "class", "INT_2U", "" );
            sh.AddSE( "comment", "STRING", "" );
            sh.AddSE( "chkSum", "INT_4U", "" );

            se.ClassName( "FrSE" );
            se.AddSE( "name", "STRING", "" );
            se.AddSE( "class", "STRING", "" );
            se.AddSE( "comment", "STRING", "" );
            se.AddSE( "chkSum", "INT_4U", "" );
        }
        //-------------------------------------------------------------------
        // Establish the look of the common elements
        //-------------------------------------------------------------------
        ce.ClassName( "CommonElements" );
        if ( m_header.Version( ) >= 8 )
        {
            ce.AddSE( "length", "INT_8U", "" );
            ce.AddSE( "chkType", "CHAR_U", "" );
            ce.AddSE( "class", "CHAR_U", "" );
            ce.AddSE( "instance", "INT_4U", "" );

            m_length_index = 0;
            m_class_id_index = 2;
            m_common_element_size = sizeof( INT_8U ) + sizeof( CHAR_U ) +
                sizeof( CHAR_U ) + sizeof( INT_4U );
        }
        else if ( m_header.Version( ) >= 5 )
        {
            ce.AddSE( "length", "INT_8U", "" );
            ce.AddSE( "class", "INT_2U", "" );
            ce.AddSE( "instance", "INT_4U", "" );

            m_length_index = 0;
            m_class_id_index = 1;
            m_common_element_size = sizeof( INT_8U ) + sizeof( CHAR_U ) +
                sizeof( CHAR_U ) + sizeof( INT_4U );
        }
        else if ( m_header.Version( ) >= 3 )
        {
            ce.AddSE( "length", "INT_4U", "" );
            ce.AddSE( "class", "INT_2U", "" );
            ce.AddSE( "instance", "INT_2U", "" );

            m_length_index = 0;
            m_class_id_index = 1;
            m_common_element_size =
                sizeof( INT_4U ) + sizeof( INT_2U ) + sizeof( INT_2U );
        }

        AddDescription( 0, ce );
        AddDescription( 1, sh );
        AddDescription( 2, se );
    }
}

//=======================================================================
// Output class definition
//=======================================================================

Output::Output( std::ostream& Stream ) : m_stream( Stream )
{
    // Only output debug messages if debug level > 0
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        const char* msg = "DEBUG: Output constructor called\n";
        if (write(STDERR_FILENO, msg, strlen(msg)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
}

Output::~Output( )
{
    // Only output debug messages if debug level > 0
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        char msg[100];
        snprintf(msg, sizeof(msg), "DEBUG: Output destructor started (order #%d)\n", ++static_destruction_counter);
        if (write(STDERR_FILENO, msg, strlen(msg)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
    
    // Note: m_stream is a reference, don't modify it
    
    if (FrameCPP::Utilities::FrameCPPDebugManager::get_safe_global_level() > 0) {
        const char* msg2 = "DEBUG: Output destructor completed\n";
        if (write(STDERR_FILENO, msg2, strlen(msg2)) < 0) {
            // Write failed - ignore silently during static destruction
        }
    }
}

template < class T >
std::ostream&
Output::operator<<( const T& Data )
{
    m_stream << Data;
    return m_stream;
}

//=======================================================================
// PTR_STRUCT - Definition
//=======================================================================
PTR_STRUCT::PTR_STRUCT( FrameStream& Stream )
{
    if ( Stream.Version( ) >= 5 )
    {
        // v5+ uses INT_2U class, INT_4U instance
        INT_2U c;
        INT_4U i;

        IStream.Read( &c, sizeof( c ) );
        IStream.Read( &i, sizeof( i ) );

        if ( Stream.ByteSwapping( ) )
        {
            reverse< sizeof( c ) >( &c, 1 );
            reverse< sizeof( i ) >( &i, 1 );
        }

        m_data_class = c;
        m_data_instance = i;
    }
    else if ( Stream.Version( ) >= 3 )
    {
        // v3-v4 uses INT_2U class, INT_2U instance
        INT_2U c;
        INT_2U i;

        IStream.Read( &c, sizeof( c ) );
        IStream.Read( &i, sizeof( i ) );

        if ( Stream.ByteSwapping( ) )
        {
            reverse< sizeof( c ) >( &c, 1 );
            reverse< sizeof( i ) >( &i, 1 );
        }

        m_data_class = c;
        m_data_instance = i;
    }
    else
    {
        throw std::runtime_error( "Unable to read PTR_STRUCT" );
    }
}
//=======================================================================
// ARRAY_TYPE
//=======================================================================
INT_4U ARRAY_TYPE::m_DumpNElements = 0;

Regex ARRAY_TYPE::pattern( "(CHAR|"
                           "CHAR_U|"
                           "INT_2S|"
                           "INT_2U|"
                           "INT_4S|"
                           "INT_4U|"
                           "INT_8S|"
                           "INT_8U|"
                           "REAL_4|"
                           "REAL_8|"
                           "STRING|"
                           "PTR_STRUCT|"
                           "COMPLEX_8|"
                           "COMPLEX_16)"
                           "[[:space:]]*\\[[[:space:]]*"
                           "([^[:space:]]+)"
                           "[[:space:]]*\\]",
                           REG_EXTENDED );

Regex ARRAY_TYPE::m_multidimensional_pattern(
    "^([[:alpha:][:digit:]_]+)\\]*\\[*(.*)$", REG_EXTENDED );

template < typename TYPE >
void
ARRAY_TYPE::dump( std::ostringstream& Stream ) const
{
    const TYPE* d = reinterpret_cast< const TYPE* >( m_data.get( ) );
    if ( d )
    {
        const INT_4U end =
            ( ( m_DumpNElements == 0 ) ? m_size
                                       : std::min( m_size, m_DumpNElements ) );

        for ( INT_4U x = 0; x < end; ++x, ++d )
        {
            if ( x )
            {
                Stream << ", ";
            }
            Stream << *d;
        }
    }
}

template <>
void
ARRAY_TYPE::dump< STRING >( std::ostringstream& Stream ) const
{
    const STRING* d = reinterpret_cast< const STRING* >( m_data.get( ) );
    const INT_4U  end =
        ( ( m_DumpNElements == 0 ) ? m_size
                                   : std::min( m_size, m_DumpNElements ) );

    for ( INT_4U x = 0; x < end; ++x, ++d )
    {
        if ( x )
        {
            Stream << ", ";
        }
        Stream << d->Value( );
    }
}

template <>
void
ARRAY_TYPE::dump< CHAR >( std::ostringstream& Stream ) const
{
    const CHAR*  d = reinterpret_cast< const CHAR* >( m_data.get( ) );
    const INT_4U end =
        ( ( m_DumpNElements == 0 ) ? m_size
                                   : std::min( m_size, m_DumpNElements ) );

    for ( INT_4U x = 0; x < end; ++x, ++d )
    {
        if ( x )
        {
            Stream << ", ";
        }
        Stream << ( ( isprint( *d ) ) ? *d : INT_2S( *d ) );
    }
}

template <>
void
ARRAY_TYPE::dump< CHAR_U >( std::ostringstream& Stream ) const
{
    const CHAR_U* d = static_cast< const CHAR_U* >( m_data.get( ) );
    const INT_4U  end =
        ( ( m_DumpNElements == 0 ) ? m_size
                                   : std::min( m_size, m_DumpNElements ) );

    for ( INT_4U x = 0; x < end; ++x, ++d )
    {
        if ( x )
        {
            Stream << ", ";
        }
        Stream << ( ( isprint( *d ) ) ? *d : INT_2U( *d ) );
    }
}

template < typename TYPE >
void
ARRAY_TYPE::read( FrameStream& Stream, INT_4U Size )
{
    // boost::shared_array< TYPE >	data( new TYPE[ Size ] );
    data_type data( new data_type::element_type[ Size * sizeof( TYPE ) ] );

    IStream.Read( data.get( ), sizeof( TYPE ) * Size );
    if ( Stream.ByteSwapping( ) )
    {
        reverse< sizeof( TYPE ) >( data.get( ), Size );
    }

    m_data = data;
}

#if WORKING
template <>
void
ARRAY_TYPE::read< STRING >( FrameStream& Stream, INT_4U Size )
{
    boost::shared_array< STRING > data( new STRING[ Size ] );
    STRING*                       data_pos( data.get( ) );

    for ( INT_4U x = 0; x != Size; ++x, ++data_pos )
    {
        data_pos->Read( Stream );
    }
    m_data = data;
}
#endif /* WORKING */

template <>
void
ARRAY_TYPE::read< COMPLEX_8 >( FrameStream& Stream, INT_4U Size )
{
    read< REAL_4 >( Stream, Size * 2 );
}

template <>
void
ARRAY_TYPE::read< COMPLEX_16 >( FrameStream& Stream, INT_4U Size )
{
    read< REAL_8 >( Stream, Size * 2 );
}

ARRAY_TYPE::ARRAY_TYPE( FrameStream&       Stream,
                        const std::string& Class,
                        const SymbolTable& Symbols )
    : m_data_type( DATA_TYPE_UNKNOWN ), m_size( 0 )
{
    RegexMatch m( 3 );

    if ( m.match( pattern, Class.c_str( ) ) )
    {
        const std::string t( m.getSubString( 1 ) );
        std::string       size( m.getSubString( 2 ) );
        INT_4U            value = 0;
        RegexMatch        md( 3 );

        if ( md.match( m_multidimensional_pattern, size.c_str( ) ) )
        {
            value = 1;
            do
            {
                std::string symbol( md.getSubString( 1 ) );
                size = md.getSubString( 2 );
                INT_4U s( Symbols.LookupInteger( symbol ) );
                if ( s == ~INT_4U( 0 ) )
                {
                    s = 0;
                }
                value *= s;
            } while ( md.match( m_multidimensional_pattern, size.c_str( ) ) );
        }

        if ( value )
        {

#undef READ
#define READ( TYPE )                                                           \
    ( t.compare( #TYPE ) == 0 )                                                \
    {                                                                          \
        read< TYPE >( Stream, value );                                         \
        m_data_type = DATA_TYPE_##TYPE;                                        \
        m_size = value;                                                        \
    }

            /* if READ( STRING )
               else */
            if
                READ( CHAR )
            else if
                READ( CHAR_U )
            else if
                READ( INT_2S )
            else if
                READ( INT_2U )
            else if
                READ( INT_4S )
            else if
                READ( INT_4U )
            else if
                READ( INT_8S )
            else if
                READ( INT_8U )
            else if
                READ( REAL_4 )
            else if
                READ( REAL_8 )
            else if
                READ( COMPLEX_8 )
            else if
                READ( COMPLEX_16 )
            else
            {
                INT_4U cs = ClassSize( t );
                if ( cs )
                {
                    IStream.Advance( value * cs );
                }
                else
                {
                    throw std::runtime_error( "Unable to read array_type" );
                }
            }
#undef READ
        }
    }
}

ARRAY_TYPE::~ARRAY_TYPE( )
{
#if 0
  if ( m_data )
  {

#undef DELETE
#define DELETE( TYPE )                                                         \
    case DATA_TYPE_##TYPE:                                                     \
        delete[] static_cast< TYPE* >( m_data );                               \
        break

    //-------------------------------------------------------------------
    // Check the datatype assoicated with the array and deallocate
    // the memory accordingly.
    //
    // NOTE: COMPLEX_8 and COMPLEX_16 were allocated as REAL_4 and
    //    REAL_8 arrays respectively with twice the number of elements
    //    as specified by size. As a result, they can use the
    //    appropriate REAL_ deallocaters.
    //-------------------------------------------------------------------  
    switch( m_data_type )
    {
      DELETE( CHAR );
      DELETE( CHAR_U );
      DELETE( INT_2S );
      DELETE( INT_2U );
      DELETE( INT_4S );
      DELETE( INT_4U );
      DELETE( INT_8S );
    case DATA_TYPE_COMPLEX_8:
      DELETE( REAL_4 );
    case DATA_TYPE_COMPLEX_16:
      DELETE( REAL_8 );
      DELETE( STRING );
      DELETE( PTR_STRUCT );
    default:
      delete[] static_cast< char* >( m_data );
    }

#undef DELETE
  }
#endif /* 0 */
}

std::string
ARRAY_TYPE::Value( ) const
{
    std::ostringstream retval;

#undef DUMP
#define DUMP( TYPE )                                                           \
    ( DATA_TYPE_##TYPE == m_data_type )                                        \
    {                                                                          \
        dump< TYPE >( retval );                                                \
    }

    if ( m_data )
    {
        if
            DUMP( STRING )
        else if
            DUMP( CHAR )
        else if
            DUMP( CHAR_U )
        else if
            DUMP( INT_2S )
        else if
            DUMP( INT_2U )
        else if
            DUMP( INT_4S )
        else if
            DUMP( INT_4U )
        else if
            DUMP( INT_8S )
        else if
            DUMP( INT_8U )
        else if
            DUMP( REAL_4 )
        else if
            DUMP( REAL_8 )
    }

#undef DUMP

    if ( ( ( m_size > m_DumpNElements ) && ( m_DumpNElements > 0 ) ) ||
         ( retval.str( ).length( ) <= 0 ) )
    {
        if ( retval.str( ).length( ) > 0 )
        {
            retval << ", ";
        }
        retval << "...";
    }
    return retval.str( );
}

//=======================================================================
// STRING - Definition
//=======================================================================
STRING::STRING( )
{
}

STRING::STRING( FrameStream& Stream )
{
    Read( Stream );
}

static INT_8U
ClassSize( const std::string& Class )
{
    INT_8U retval = 0;

    if ( Class == "CHAR" )
    {
        retval = sizeof( CHAR );
    }
    else if ( Class == "CHAR_U" )
    {
        retval = sizeof( CHAR_U );
    }
    else if ( Class == "INT_2S" )
    {
        retval = sizeof( INT_2S );
    }
    else if ( Class == "INT_2U" )
    {
        retval = sizeof( INT_2U );
    }
    else if ( Class == "INT_4S" )
    {
        retval = sizeof( INT_4S );
    }
    else if ( Class == "INT_4U" )
    {
        retval = sizeof( INT_4U );
    }
    else if ( Class == "INT_8S" )
    {
        retval = sizeof( INT_8S );
    }
    else if ( Class == "INT_8U" )
    {
        retval = sizeof( INT_8U );
    }
    else if ( Class == "REAL_4" )
    {
        retval = sizeof( REAL_4 );
    }
    else if ( Class == "REAL_8" )
    {
        retval = sizeof( REAL_8 );
    }
    return retval;
}

void
STRING::Read( FrameStream& Stream )
{
    if ( Stream.Version( ) >= 3 )
    {
        INT_2U length;
        IStream.Read( &length, sizeof( length ) );
        if ( Stream.ByteSwapping( ) )
        {
            reverse< sizeof( length ) >( &length, 1 );
        }

        m_length = length;

        if ( m_length > 0 )
        {
            m_value.reset( new CHAR[ m_length ] );
            IStream.Read( m_value.get( ), m_length );
            m_value[ m_length - 1 ] = '\0';
        }
    }
    else
    {
        throw std::runtime_error( "STRING: Unsupported frame specification" );
    }
}
