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

#ifndef FrameCPP__COMMON__StreamRef_HH
#define FrameCPP__COMMON__StreamRef_HH

#include <cstring> // for std::memcpy

#include "ldastoolsal/types.hh"
#include "ldastoolsal/reverse.hh"
#include "ldastoolsal/Logger.hh"

#include "framecpp/Common/FrameSpec.hh"
#include "framecpp/Common/FrameStream.hh"
#include "framecpp/Common/StringStream.hh"
#include "framecpp/Common/IOStream.hh"
#include "framecpp/Common/StreamRefInterface.hh"
#include "framecpp/Common/CheckSum.hh"
#include "framecpp/Common/Description.hh"

namespace FrameCPP
{
    namespace Universal
    {
        /// \brief Stream reference structure for frame objects
        ///
        /// StreamRef provides a unified reference structure for frame objects
        /// across all frame specification versions (v3 through v9). It handles
        /// the storage and management of object metadata including length,
        /// class type, instance number, and checksum information.
        ///
        /// This class consolidates the functionality of the previous StreamRef
        /// and StreamRef2 classes, providing version-aware serialization and
        /// checksum support for frame specification versions >= v8.
        ///
        /// \par Version Evolution:
        /// - v3-v4: Basic reference with INT_4U length, INT_2U class/instance
        /// - v5-v7: Extended length to INT_8U, instance to INT_4U
        /// - v8-v9: Added checksum type field (CHAR_U), class changed to CHAR_U
        ///
        /// \par Checksum Support:
        /// For frame versions >= v8, StreamRef includes a checksum type field
        /// that enables data integrity verification. When reading data with
        /// a non-zero checksum type, the class automatically configures the
        /// stream's checksum object and processes the reference data through
        /// the checksum filter.
        ///
        /// \note This class uses the largest type sizes internally (v9 types)
        /// and performs appropriate conversions when reading/writing older
        /// frame versions.
        ///
        /// \see StreamRefInterface
        /// \see FrameSpec
        class StreamRef : public Common::StreamRefInterface
        {
        public:
            // Bring base class GetDescription methods into scope
            using Common::FrameSpec::Object::GetDescription;
            using length_type = StreamRefInterface::length_type;
            using class_type = StreamRefInterface::class_type;
            using instance_type = StreamRefInterface::instance_type;

            /// \brief Default constructor
            ///
            /// Constructs an empty StreamRef with all fields initialized to
            /// zero.
            StreamRef( );

            /// \brief Destructor
            virtual ~StreamRef( );

            /// \brief Get the size of this object in memory
            ///
            /// Calculates the number of bytes this StreamRef will occupy when
            /// written to a stream of the specified version. The size varies
            /// based on the frame specification version:
            /// - v3-v4: 10 bytes (4+2+2+2)
            /// - v5-v7: 14 bytes (8+2+4)
            /// - v8-  : 14 bytes (8+1+1+4)
            ///
            /// \param[in] Stream The stream to calculate size for
            /// \return Size in bytes of the StreamRef object's data members
            virtual Common::FrameSpec::size_type
            SizeOf( stream_version_type Stream ) const override;

            /// \brief Calculate serialized size for a specific stream version
            ///
            /// Calculates the number of bytes this StreamRef will occupy when
            /// written to a stream of the specified version. The size varies
            /// based on the frame specification version:
            /// - v3-v4: 10 bytes (4+2+2+2)
            /// - v5-v7: 14 bytes (8+2+4)
            /// - v8-  : 14 bytes (8+1+1+4)
            ///
            /// \param[in] Stream The stream to calculate size for
            /// \return Number of bytes required for serialization
            Common::FrameSpec::size_type
            Bytes( const Common::StreamBase& Stream ) const override;

            /// \brief Get the checksum type
            ///
            /// Returns the checksum type field value. This field is only
            /// meaningful for frame versions >= v8. For earlier versions,
            /// this will always return 0.
            ///
            /// \return Checksum type identifier
            /// \note Valid checksum types are defined in CheckSum::kind_type
            virtual INT_2U GetChkType( ) const;

            /// \brief Get the stream-level class identifier
            ///
            /// Returns the class identifier as stored in the frame file stream.
            /// This is used for mapping frame-file class IDs to FSI IDs and may
            /// differ from GetClass() which returns the FrameSpec registration ID
            /// (FSI_COMMON_ELEMENTS).
            ///
            /// \return Stream-level class identifier from frame file
            virtual class_type GetStreamClass( ) const override;

            /// \brief Get the instance number
            ///
            /// Returns the instance number for the referenced object.
            /// This distinguishes between multiple instances of the same
            /// class type within a frame.
            ///
            /// \return Instance number
            virtual instance_type GetInstance( ) const override;

            /// \brief Get the data length
            ///
            /// Returns the length of the data associated with this reference.
            /// This typically represents the serialized size of the referenced
            /// object.
            ///
            /// \return Data length in bytes
            virtual length_type GetLength( ) const override;

            /// \brief Set the checksum type
            ///
            /// Sets the checksum type field. This should match one of the
            /// valid checksum types defined in CheckSum::kind_type.
            ///
            /// \param[in] ChkType Checksum type to set
            /// \note This field is only used for frame versions >= v8
            virtual void SetChkType( INT_2U ChkType );

            /// \brief Set the class identifier
            ///
            /// Sets the class type identifier for the referenced object.
            ///
            /// \param[in] Class Class type identifier to set
            virtual void SetClass( class_type Class ) override;

            /// \brief Set the instance number
            ///
            /// Sets the instance number for the referenced object.
            ///
            /// \param[in] Instance Instance number to set
            virtual void SetInstance( instance_type Instance ) override;

            /// \brief Set the data length
            ///
            /// Sets the length of the data associated with this reference.
            ///
            /// \param[in] Length Data length in bytes
            virtual void SetLength( length_type Length ) override;

            /// \brief Create a new StreamRef from a stream
            ///
            /// Factory method that creates a new StreamRef object by reading
            /// data from the provided input stream. The stream version is
            /// automatically detected and appropriate conversions are applied.
            ///
            /// \param[in] Stream Input stream to read from
            /// \return Pointer to newly created StreamRef object
            /// \throws std::bad_alloc if memory allocation fails
            virtual StreamRef* Create( Common::IStream& Stream ) const override;

            /// \brief Create a copy of this StreamRef
            ///
            /// Creates a deep copy of this StreamRef object.
            ///
            /// \return Pointer to newly created copy
            /// \throws std::bad_alloc if memory allocation fails
            virtual StreamRef* Clone( ) const override;

            //-----------------------------------------------------------------
            /// \brief Get FrSH/FrSE description of object
            ///
            /// FrSH is a meta-description class, so it returns an empty
            /// description
            //-----------------------------------------------------------------
            virtual const Common::Description* GetDescription( ) const override;

            //-----------------------------------------------------------------
            /// \brief Get FrSH/FrSE description of object for a specific
            /// version
            ///
            /// FrSH is a meta-description class, so it returns an empty
            /// description
            //-----------------------------------------------------------------
            virtual const Common::Description*
            GetDescription( unsigned int version ) const override;

            /// \brief Get the structure name
            ///
            /// Returns the name of this structure type for identification
            /// and debugging purposes.
            ///
            /// \return "StreamRef"
            virtual const char* ObjectStructName( ) const override;

            /// \brief Write StreamRef to an output stream
            ///
            /// Serializes the StreamRef data to the provided output stream.
            /// The format depends on the stream's frame version:
            /// - v3-v4: Writes with v3 type sizes
            /// - v5-v7: Writes with v5 type sizes
            /// - v8-v9: Includes checksum type field
            ///
            /// For v8+, if the stream has an active checksum object, its
            /// type will be used instead of the stored checksum type.
            ///
            /// \param[in,out] Stream Output stream to write to
            virtual void Write( Common::OStream& Stream ) const override;

            /// \brief Compare two StreamRef objects for equality
            ///
            /// Compares all fields (length, chkType, class, instance) for
            /// equality.
            ///
            /// \param[in] RHS StreamRef to compare with
            /// \return true if all fields match, false otherwise
            virtual bool operator==( const StreamRef& RHS ) const;

            /// \brief Compare with a generic frame object
            ///
            /// Attempts to cast the provided object to StreamRef and compare.
            ///
            /// \param[in] Obj Frame object to compare with
            /// \return true if object is a StreamRef and all fields match
            virtual bool
            operator==( const Common::FrameSpec::Object& Obj ) const override;

        protected:
            /// @brief Get the logger for StreamRef debugging (static version)
            /// @return Static logger instance (thread-safe, lazy initialization)
            /// Moved to StreamRef.cc to avoid ODR violations from function-local statics
            static SimpleLogger* logger();

            /// @brief Get the logger for StreamRef debugging (dependency injection version)
            /// @param services Services context containing logger manager
            /// @return Logger instance from services context
            static SimpleLogger* logger(const FrameCPP::Common::FrameCPPServices& services);

            /// \brief Assign values from a string stream
            ///
            /// Reads StreamRef data from a string stream. The stream version
            /// is automatically detected and appropriate type conversions are
            /// performed based on the version.
            ///
            /// \param[in] Stream String stream to read from
            /// \note This method is used internally for deserialization
            virtual void assign( Common::IStringStream& Stream,
                                 stream_version_type    StreamVersion ) override;

        private:
            //
            // NOTE: demote() and promote() override methods removed.
            // Universal objects are version-agnostic - all version adaptation
            // happens at the I/O layer via pCreate/pWrite/pRead.
            //
            /// \brief Version-specific type definitions
            ///
            /// These structures define the data types used for each frame
            /// specification version. The types evolve across versions to
            /// support larger values and additional fields.

            /// \brief v3 type definitions (frame spec v3-v4)
            struct v3
            {
                using internal_length_type = INT_4U; ///< 32-bit length
                using internal_class_type = INT_2U; ///< 16-bit class ID
                using internal_instance_type = INT_2U; ///< 16-bit instance
            };
            using v4 = v3; ///< v4 uses same types as v3

            /// \brief v5 type definitions (frame spec v5-v7)
            struct v5
            {
                using internal_length_type = INT_8U; ///< 64-bit length
                using internal_class_type =
                    v4::internal_class_type; ///< 16-bit class ID
                using internal_instance_type = INT_4U; ///< 32-bit instance
            };
            using v6 = v5; ///< v6 uses same types as v5
            using v7 = v6; ///< v7 uses same types as v6

            /// \brief v8 type definitions (frame spec v8-v9)
            struct v8
            {
                using internal_length_type = INT_8U; ///< 64-bit length
                using internal_chkType_type = CHAR_U; ///< 8-bit checksum type
                using internal_class_type = CHAR_U; ///< 8-bit class ID
                using internal_instance_type = INT_4U; ///< 32-bit instance
            };
            using v9 = v8; ///< v9 uses same types as v8

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

            /// \brief Current version type alias
            using current_version = v9;

            /// \brief Internal type aliases for current version
            /// @{
            using internal_length_type = current_version::internal_length_type;
            using internal_chkType_type =
                current_version::internal_chkType_type;
            using internal_class_type = current_version::internal_class_type;
            using internal_instance_type =
                current_version::internal_instance_type;
            /// @}

            using Object::Create;

            /// \brief Data members
            /// @{
            internal_length_type   m_length; ///< Data length in bytes
            internal_chkType_type  m_chkType; ///< Checksum type (>= v8)
            internal_class_type    m_class; ///< Object class identifier
            internal_instance_type m_instance; ///< Object instance number
            /// @}

            /// \brief Construct from input stream
            ///
            /// Reads StreamRef data from an input stream and initializes
            /// all fields. Handles version detection and checksum processing
            /// for v8+ streams.
            ///
            /// \param[in] Stream Input stream to read from
            StreamRef( Common::IStream& Stream );

            /// \brief Copy constructor
            ///
            /// Creates a deep copy of another StreamRef object.
            ///
            /// \param[in] Source StreamRef to copy from
            StreamRef( const StreamRef& Source );

        }; // class - StreamRef

    } // namespace Universal
} // namespace FrameCPP
#endif /* FrameCPP__COMMON__StreamRef_HH */
