//
// 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
//

/// \file StreamRef.cc
/// \brief Implementation of StreamRef class
///
/// This file contains the implementation of the StreamRef class, which
/// provides a unified reference structure for frame objects across all
/// frame specification versions (v3 through v9).
///
/// \author LDAS Development Team
/// \date 2025

#include "framecpp/Universal/StreamRef.hh"
#include "framecpp/Universal/impl/FrameSpec.hh" // for FRAME_SPEC_CURRENT
#include "framecpp/Common/FrameCPPServices.hh"

namespace FrameCPP
{
    namespace Universal
    {
        using Common::Description;

        // Implementation of StreamRef static logger method
        SimpleLogger* StreamRef::logger() {
            static SimpleLogger* instance = get_channel_logger("framecpp.streamref");
            return instance;
        }

        // Implementation of StreamRef dependency injection logger method
        SimpleLogger* StreamRef::logger(const FrameCPP::Common::FrameCPPServices& services) {
            return services.logger_mgr.getLogger("framecpp.streamref");
        }

        StreamRef::StreamRef( )
            : StreamRefInterface( 0 ), m_length( 0 ), m_chkType( 0 ),
              m_class( 0 ), m_instance( 0 )
        {
        }

        StreamRef::StreamRef( Common::IStream& Stream )
            : StreamRefInterface( 0 ), m_length( 0 ), m_chkType( 0 ),
              m_class( 0 ), m_instance( 0 )
        {
            auto frame_stream_version = StreamVersion( Stream );
            
            // Log entry and version detection
            logger_log_debug(logger(), 10, "Universal::StreamRef::StreamRef() entry - frame_stream_version: ", 
                           frame_stream_version, ", stream position: ", Stream.tellg());

            if ( frame_stream_version >= 8 )
            {
                // >= v8: includes chkType field
                Stream >> m_length >> m_chkType >> m_class >> m_instance;
                if ( m_chkType )
                {
                    char buffer[ sizeof( m_length ) + sizeof( m_chkType ) +
                                 sizeof( m_class ) + sizeof( m_instance ) ];

                    {
                        char* pos = buffer;

                        // Helper lambda to copy data to buffer
                        auto copyToBuffer = [&pos]( const auto& var ) {
                            std::memcpy( pos, &var, sizeof( var ) );
                            pos += sizeof( var );
                        };

                        copyToBuffer( m_length );
                        copyToBuffer( m_chkType );
                        copyToBuffer( m_class );
                        copyToBuffer( m_instance );
                    }

                    if ( Stream.ByteSwapping( ) )
                    {
                        logger_log_debug(logger(), 30, "Read StreamRef: swapping bytes [ ", 
                                       __LINE__, ": ", __FILE__, " ]");
                        char* pos = buffer;

                        reverse< sizeof( m_length ) >( pos, 1 );
                        pos += sizeof( m_length );

                        reverse< sizeof( m_chkType ) >( pos, 1 );
                        pos += sizeof( m_chkType );

                        reverse< sizeof( m_class ) >( pos, 1 );
                        pos += sizeof( m_class );

                        reverse< sizeof( m_instance ) >( pos, 1 );
                    }

                    Stream.SetCheckSumObject(
                        static_cast< Common::CheckSum::kind_type >(
                            m_chkType ) );
                    Stream.GetCheckSumObject( )->Filter( &( buffer[ 0 ] ),
                                                         sizeof( buffer ) );

                    logger_log_debug(logger(), 30, "Read StreamRef: size: ", sizeof( buffer ));
                }
                logger_log_debug(logger(), 20, "Read StreamRef: m_length:", m_length, 
                               " m_chkType: ", (int)m_chkType, " m_class: ", (int)m_class, 
                               " m_instance: ", m_instance, " [ ", __LINE__, ": ", __FILE__, " ]");
            }
            else if ( frame_stream_version >= 5 )
            {
                // v5-v7: No chkType field
                logger_log_debug(logger(), 15, "Universal::StreamRef::StreamRef() using v5-v7 path, position before read: ", 
                               Stream.tellg());
                
                v5::internal_length_type   temp_length;
                v5::internal_class_type    temp_class;
                v5::internal_instance_type temp_instance;

                Stream >> temp_length >> temp_class >> temp_instance;
                
                logger_log_debug(logger(), 25, "Universal::StreamRef::StreamRef() v5-v7 raw values - temp_length: ", 
                               temp_length, ", temp_class: ", temp_class, ", temp_instance: ", temp_instance);
                
                m_length = temp_length;
                m_class = temp_class;
                m_instance = temp_instance;
                
                logger_log_debug(logger(), 15, "Universal::StreamRef::StreamRef() v5-v7 final values - m_length: ", 
                               m_length, ", m_class: ", m_class, ", m_instance: ", m_instance);
            }
            else if ( frame_stream_version >= 3 )
            {
                // v3-v4: No chkType field, smaller types
                logger_log_debug(logger(), 15, "Universal::StreamRef::StreamRef() using v3-v4 path, position before read: ", 
                               Stream.tellg());
                
                v3::internal_length_type   temp_length;
                v3::internal_class_type    temp_class;
                v3::internal_instance_type temp_instance;

                // Log before reading
                logger_log_debug(logger(), 20, "Universal::StreamRef::StreamRef() about to read sizeof(length)=", 
                               sizeof(temp_length), ", sizeof(class)=", sizeof(temp_class), 
                               ", sizeof(instance)=", sizeof(temp_instance));

                Stream >> temp_length >> temp_class >> temp_instance;
                
                // Log raw values read
                logger_log_debug(logger(), 25, "Universal::StreamRef::StreamRef() raw values read - temp_length: ", 
                               temp_length, ", temp_class: ", temp_class, ", temp_instance: ", temp_instance, 
                               ", position after read: ", Stream.tellg());
                
                m_length = temp_length;
                m_class = temp_class;
                m_instance = temp_instance;
                
                // Log final assigned values
                logger_log_debug(logger(), 15, "Universal::StreamRef::StreamRef() final values - m_length: ", 
                               m_length, ", m_class: ", m_class, ", m_instance: ", m_instance);
            }
            else
            {
                // Fallback case - unsupported version
                logger_log_error(logger(), "Universal::StreamRef::StreamRef() unsupported frame version: ", 
                               frame_stream_version);
            }
        }

        StreamRef::StreamRef( const StreamRef& Source )
            : StreamRefInterface( Source ), m_length( Source.m_length ),
              m_chkType( Source.m_chkType ), m_class( Source.m_class ),
              m_instance( Source.m_instance )
        {
        }

        StreamRef::~StreamRef( )
        {
        }

        Common::FrameSpec::size_type
        StreamRef::SizeOf( stream_version_type StreamVersion ) const
        {
            Common::FrameSpec::size_type retval = 0;
            if ( StreamVersion >= 8 )
            {
                retval = //
                    sizeof( v8::internal_length_type ) // length
                    + sizeof( v8::internal_chkType_type ) // checksum type
                    + sizeof( v8::internal_class_type ) // class
                    + sizeof( v8::internal_instance_type ) // instance
                    ;
            }
            else if ( StreamVersion >= 5 )
            {
                retval = //
                    sizeof( v5::internal_length_type ) // length
                    + sizeof( v5::internal_class_type ) // class
                    + sizeof( v5::internal_instance_type ) // instance
                    ;
            }
            else if ( StreamVersion >= 3 )
            {
                retval = //
                    sizeof( v3::internal_length_type ) // length
                    + sizeof( v3::internal_class_type ) // class
                    + sizeof( v3::internal_instance_type ) // instance
                    ;
            }
            return ( retval );
        }

        Common::FrameSpec::size_type
        StreamRef::Bytes( const Common::StreamBase& Stream ) const
        {
            auto frame_stream_version = StreamVersion( Stream );

            return ( SizeOf( frame_stream_version ) );
        }

        INT_2U
        StreamRef::GetChkType( ) const
        {
            return m_chkType;
        }

        StreamRef::class_type
        StreamRef::GetStreamClass( ) const
        {
            return m_class;
        }

        StreamRef::instance_type
        StreamRef::GetInstance( ) const
        {
            return m_instance;
        }

        StreamRef::length_type
        StreamRef::GetLength( ) const
        {
            return m_length;
        }

        const Description*
        StreamRef::GetDescription( ) const
        {
            // FrSH is a meta-description class that doesn't have its own
            // description Return empty description
            static Description empty_description;
            return &empty_description;
        }

        const Description*
        StreamRef::GetDescription( unsigned int version ) const
        {
            // FrSH is a meta-description class that doesn't have its own
            // description Return empty description
            static Description empty_description;
            return &empty_description;
        }

        void
        StreamRef::SetChkType( INT_2U ChkType )
        {
            m_chkType = ChkType;
        }

        void
        StreamRef::SetClass( class_type Class )
        {
            m_class = Class;
        }

        void
        StreamRef::SetInstance( instance_type Instance )
        {
            m_instance = Instance;
        }

        void
        StreamRef::SetLength( length_type Length )
        {
            m_length = Length;
        }

        StreamRef*
        StreamRef::Create( Common::IStream& Stream ) const
        {
            return new StreamRef( Stream );
        }

        StreamRef*
        StreamRef::Clone( ) const
        {
            return new StreamRef( *this );
        }

        const char*
        StreamRef::ObjectStructName( ) const
        {
            const char* const name = "StreamRef";
            return name;
        }

        void
        StreamRef::Write( Common::OStream& Stream ) const
        {
            auto frame_stream_version = StreamVersion( Stream );

            logger_log_debug( logger(),
                              50,
                              "StreamRef::Write() m_class=",
                              static_cast<int>( m_class ),
                              " m_instance=",
                              m_instance,
                              " m_length=",
                              m_length,
                              " at position: ",
                              Stream.tellp() );

            if ( frame_stream_version >= 8 )
            {
                // CRITICAL: Initialize chkType from stream's checksum object if available
                // This is the authoritative source for v8+ files
                internal_chkType_type chkType = Common::CheckSum::NONE;

                if ( Stream.GetCheckSumObject( ) )
                {
                    chkType = Stream.GetCheckSumObject( )->Type( );
                }
                else if ( m_chkType == Common::CheckSum::NONE || m_chkType == Common::CheckSum::CRC )
                {
                    // Only use m_chkType if it's valid and stream doesn't have a checksum object
                    chkType = m_chkType;
                }

                // Final validation: Ensure chkType is valid before writing
                if ( chkType != Common::CheckSum::NONE && chkType != Common::CheckSum::CRC )
                {
                    logger_log_error(logger(), "StreamRef::Write() INVALID chkType: ",
                                   static_cast<int>(chkType), " - using NONE");
                    chkType = Common::CheckSum::NONE;
                }

                auto position_before = Stream.tellp();
                Stream << m_length << chkType << m_class << m_instance;
            }
            else if ( frame_stream_version >= 5 )
            {
                Stream << v5::internal_length_type( m_length )
                       << v5::internal_class_type( m_class )
                       << v5::internal_instance_type( m_instance );
            }
            else if ( frame_stream_version >= 3 )
            {
                Stream << v3::internal_length_type( m_length )
                       << v3::internal_class_type( m_class )
                       << v3::internal_instance_type( m_instance );
            }
        }

        bool
        StreamRef::operator==( const StreamRef& RHS ) const
        {
            return ( ( m_length == RHS.m_length ) &&
                     ( m_chkType == RHS.m_chkType ) &&
                     ( m_class == RHS.m_class ) &&
                     ( m_instance == RHS.m_instance ) );
        }

        bool
        StreamRef::operator==( const Common::FrameSpec::Object& Obj ) const
        {
            try
            {
                const StreamRef& robj =
                    reinterpret_cast< const StreamRef& >( Obj );
                return ( *this == robj );
            }
            catch ( ... )
            {
            }
            return ( false );
        }

        void
        StreamRef::assign( Common::IStringStream& Stream,
                           stream_version_type    StreamVersion )
        {
            // TODO: IStringStream needs to be enhanced to support version
            // information For now, assume current version when reading from
            // string streams
            auto frame_stream_version = StreamVersion;

            if ( frame_stream_version >= 8 )
            {
                Stream >> m_length >> m_chkType >> m_class >> m_instance;
            }
            else if ( frame_stream_version >= 5 )
            {
                v5::internal_length_type   temp_length;
                v5::internal_class_type    temp_class;
                v5::internal_instance_type temp_instance;
                Stream >> temp_length >> temp_class >> temp_instance;
                m_length = temp_length;
                m_class = temp_class;
                m_instance = temp_instance;
                m_chkType = 0;
            }
            else if ( frame_stream_version >= 3 )
            {
                v3::internal_length_type   temp_length;
                v3::internal_class_type    temp_class;
                v3::internal_instance_type temp_instance;
                Stream >> temp_length >> temp_class >> temp_instance;
                m_length = temp_length;
                m_class = temp_class;
                m_instance = temp_instance;
                m_chkType = 0;
            }
        }

        //
        // NOTE: demote() and promote() method implementations removed.
        // Universal objects are version-agnostic - all version adaptation
        // happens at the I/O layer via pCreate/pWrite/pRead.
        //
    } // namespace Universal
} // namespace FrameCPP
