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

#include <framecpp_config.h>

#include <iostream>

#include "framecpp/Common/IOStream.hh"
#include "framecpp/Common/FrameStream.hh"
#include "framecpp/Common/CheckSumFilter.hh"
#include "framecpp/Common/Description.hh"
#include "framecpp/Common/Verify.hh"
#include "framecpp/Common/FrameSpec.tcc"
#include "framecpp/Common/FrameCPPServices.hh"

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

#include "framecpp/Universal/FrEndOfFile.hh"
#include "framecpp/Universal/FrHeader.hh"
#include "framecpp/Universal/FrSE.hh"
#include "framecpp/Universal/FrSH.hh"


using FrameCPP::Common::CheckSum;
using FrameCPP::Common::CheckSumFilter;
using FrameCPP::Common::Description;
using FrameCPP::Common::FrameSpec;
using FrameCPP::Common::OFrameStream;
using FrameCPP::Common::VerifyException;

//=======================================================================
// Static
//=======================================================================

FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( FrEndOfFileImpl,
                                      FSI_FR_END_OF_FILE,
                                      "FrEndOfFile",
                                      "End of File Data Structure" )

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

        FrEndOfFile::FrEndOfFile( )
        {
        }

        FrEndOfFile::FrEndOfFile( istream_type& Stream )
        {
            readFromStream( Stream );
        }

        FrEndOfFile::~FrEndOfFile( )
        {
        }

        FrEndOfFile::chkSum_cmn_type
        FrEndOfFile::Filter( const istream_type& Stream,
                             Common::CheckSum&   Filt,
                             chkType_cmn_type&   Type,
                             void*               Buffer,
                             INT_8U              Size ) const
        {
            chkSumFile_type value = 0;

            if ( Bytes( Stream ) != Size )
            {
                std::ostringstream msg;
                msg << "FrEndOfFile::Filter: "
                    << " Expected a buffer of size: " << Bytes( Stream )
                    << " but received a buffer of size: " << Size;
                throw std::length_error( msg.str( ) );
            }
            auto frame_stream_version = Stream.GetFrHeader( ).GetVersion( );

            const size_t relativeChkSumOffset(
                ( ( frame_stream_version >= 8 )
                      ? ( sizeof( chkSumFile_type ) )
                      : ( ( frame_stream_version >= 5 )
                              ? ( sizeof( v5::chkSum_type ) +
                                  sizeof( v5::seekTOC_type ) )
                              : ( ( frame_stream_version >= 4 )
                                      ? ( sizeof( v4::chkSum_type ) +
                                          sizeof( v4::seekTOC_type ) )
                                      : ( sizeof( v3::chkSum_type ) ) ) ) ) );
            const size_t chkSumOffset( Size - relativeChkSumOffset );

            //-----------------------------------------------------------
            // Calculate the checksum for the FrEndOfFile structure
            //   that is in the buffer
            //-----------------------------------------------------------
            if ( frame_stream_version >= 8 )
            {
                Filt.calc( Buffer, chkSumOffset );
            }
            else if ( frame_stream_version >= 5 )
            {
                v5::chkSum_type zero_checksum = 0;
                Filt.calc( Buffer, chkSumOffset );
                Filt.calc( &zero_checksum, sizeof( zero_checksum ) );
                Filt.calc( static_cast< char* >( Buffer ) +
                               ( chkSumOffset + sizeof( v5::chkSum_type ) ),
                           sizeof( v5::seekTOC_type ) );
            }
            else if ( frame_stream_version >= 4 )
            {
                Filt.calc( Buffer, chkSumOffset - sizeof( v4::chkFlag_type ) );
                Filt.calc( static_cast< char* >( Buffer ) +
                               ( chkSumOffset + sizeof( v4::chkSum_type ) ),
                           sizeof( v4::seekTOC_type ) );
            }

            else if ( frame_stream_version >= 3 )
            {
                Filt.calc( Buffer, chkSumOffset - sizeof( v3::chkFlag_type ) );
            }

            std::copy( (const char*)Buffer + chkSumOffset,
                       (const char*)Buffer + chkSumOffset + sizeof( value ),
                       (char*)&value );

#ifdef FRAMECPP_DEBUG
            // DIAGNOSTIC: Log raw value before byte-swap
            std::cerr << "FrEndOfFile::Filter DEBUG: "
                      << "frame_version=" << frame_stream_version
                      << ", raw_value_from_buffer=0x" << std::hex << value << std::dec
                      << ", Stream.ByteSwapping()=" << (Stream.ByteSwapping() ? "TRUE" : "FALSE")
                      << std::endl;
#endif /* FRAMECPP_DEBUG */

            if ( Stream.ByteSwapping( ) )
            {
                reverse< sizeof( value ) >( &value, 1 );
#ifdef FRAMECPP_DEBUG
                std::cerr << "FrEndOfFile::Filter DEBUG: "
                          << "Byte-swap APPLIED, value_after_swap=0x"
                          << std::hex << value << std::dec
                          << std::endl;
#endif /* FRAMECPP_DEBUG */
            }
#ifdef FRAMECPP_DEBUG
            else
            {
                std::cerr << "FrEndOfFile::Filter DEBUG: "
                          << "Byte-swap NOT applied (flag is false)"
                          << std::endl;
            }
#endif /* FRAMECPP_DEBUG */

#ifdef FRAMECPP_DEBUG
            std::cerr << "FrEndOfFile::Filter DEBUG: "
                      << "returning final value=0x" << std::hex << value << std::dec
                      << " (" << value << ")"
                      << std::endl;
#endif /* FRAMECPP_DEBUG */

            Type = FrameCPP::Common::CheckSum::UNSET;

            return value;
        }

        FrEndOfFile::nBytes_cmn_type
        FrEndOfFile::NBytes( ) const
        {
            return nBytes;
        }

        FrEndOfFile::nFrames_cmn_type
        FrEndOfFile::NFrames( ) const
        {
            return nFrames;
        }

        void
        FrEndOfFile::VerifyObject( Common::Verify&       Verifier,
                                   Common::IFrameStream& Stream ) const
        {
            Verifier.SeenFrEndOfFile( true );

            // For v8+ files, calculate FrHeader checksum and pass to Verifier
            unsigned int version = Stream.Version();

            if (version >= 8)
            {
                // Calculate FrHeader checksum for verification
                chkSumFrHeader_type calc_chksum = calculateFrHeaderChecksumForValidation(Stream, version);
                Verifier.SetFrHeaderChecksum(calc_chksum);
            }

            Verifier.ExamineFrHeaderChecksum( chkSumFrHeader );

            // Get checksum type based on version:
            // - v8+: From FrHeader.checksumScheme
            // - v<8: From legacyChecksumType (read from chkType/chkFlag in FrEndOfFile)
            INT_4U checksumType;

            if (version >= 8)
            {
                // Get checksumScheme from FrHeader for v8+
                const auto& base_hdr = Stream.GetFrHeader();
                const FrHeader* hdr = dynamic_cast<const FrHeader*>(&base_hdr);

                if (hdr)
                {
                    checksumType = static_cast<INT_4U>(hdr->GetChecksumScheme());
                }
                else
                {
                    checksumType = 0;  // Fallback if cast fails
                }
            }
            else
            {
                // For v<8, use checksum type read from FrEndOfFile (chkType/chkFlag)
                checksumType = legacyChecksumType;
            }

            Verifier.ExamineFrEndOfFileChecksum( Stream, checksumType, chkSumFile );
        }

        bool
        FrEndOfFile::operator==( const Common::FrameSpec::Object& RHS ) const
        {
            return FrameCPP::compare( *this, RHS );
        }

        FrEndOfFile::chkSumFrHeader_type
        FrEndOfFile::getFrHeaderChecksum( ostream_type& Stream ) const
        {
            // Simply return the stored FrHeader checksum value that was
            // calculated when FrHeader was written through the checksum filter
            chkSumFrHeader_type retval = Stream.GetFrHeaderChecksumValue();

            logger_log_debug(FrEndOfFileImpl::Data::logger(), 25,
                "FrEndOfFile::getFrHeaderChecksum() - returning stored checksum: ",
                FrEndOfFileImpl::Data::pretty_print_checksum(retval));

            return retval;
        }

        //-----------------------------------------------------------------
        /// @brief Helper template function to calculate FrHeader checksum for validation
        /// @param stream The input stream containing the FrHeader
        /// @param version The frame specification version
        /// @return The calculated FrHeader checksum value
        ///
        /// This helper exists to allow the policy template in FrEndOfFileData.hh
        /// to calculate FrHeader checksums without needing access to Universal::FrHeader
        ///
        /// CRITICAL: This function must serialize FrHeader fields in EXACTLY the same
        /// order as FrHeaderClassicIO::Write() to produce matching checksums.
        //-----------------------------------------------------------------
        template<typename StreamType>
        inline FrEndOfFile::chkSumFrHeader_type
        calculateFrHeaderChecksumForValidation(const StreamType& stream, unsigned int version)
        {
            using namespace FrameCPP::Universal;

            FrEndOfFile::chkSumFrHeader_type retval = 0;

            // IStream::GetFrHeader() returns const Common::FrHeader&, but for v8+ files
            // the actual object is Universal::FrHeader. Use dynamic_cast to get the
            // correct type that has GetChecksumScheme()
            const auto& base_hdr = stream.GetFrHeader();
            const FrHeader* hdr = dynamic_cast<const FrHeader*>(&base_hdr);

            if (hdr)
            {
                // Get the checksum scheme from the FrHeader
                // Per frame spec: "The checksum type is the same as the file
                // checksum specified in FrHeader"
                auto checksumScheme = hdr->GetChecksumScheme();

                // Only calculate if checksum scheme is not NONE
                if (checksumScheme != CheckSum::NONE)
                {
                    Common::CheckSumFilter checksum(static_cast<CheckSum::kind_type>(checksumScheme));

                    // CRITICAL: Serialize fields in SAME ORDER as FrHeaderClassicIO::Write()
                    // to produce matching checksums

                    // 1. Base FrHeader structure (bytes 0-6) - use Filter() for base class
                    base_hdr.Filter(checksum);

                    // 2. Frame-specific fields (bytes 7-37)
                    auto sizeOf_INT_2 = hdr->GetSizeOf_INT_2();
                    auto sizeOf_INT_4 = hdr->GetSizeOf_INT_4();
                    auto sizeOf_INT_8 = hdr->GetSizeOf_INT_8();
                    auto sizeOf_REAL_4 = hdr->GetSizeOf_REAL_4();
                    auto sizeOf_REAL_8 = hdr->GetSizeOf_REAL_8();
                    auto endian_2_byte = hdr->GetEndian_2_byte();
                    auto endian_4_byte = hdr->GetEndian_4_byte();
                    auto endian_8_byte = hdr->GetEndian_8_byte();
                    auto pi_single = hdr->GetPI_SINGLE();
                    auto pi_double = hdr->GetPI_DOUBLE();

                    checksum.pubfilter(reinterpret_cast<const char*>(&sizeOf_INT_2), sizeof(sizeOf_INT_2));
                    checksum.pubfilter(reinterpret_cast<const char*>(&sizeOf_INT_4), sizeof(sizeOf_INT_4));
                    checksum.pubfilter(reinterpret_cast<const char*>(&sizeOf_INT_8), sizeof(sizeOf_INT_8));
                    checksum.pubfilter(reinterpret_cast<const char*>(&sizeOf_REAL_4), sizeof(sizeOf_REAL_4));
                    checksum.pubfilter(reinterpret_cast<const char*>(&sizeOf_REAL_8), sizeof(sizeOf_REAL_8));
                    checksum.pubfilter(reinterpret_cast<const char*>(&endian_2_byte), sizeof(endian_2_byte));
                    checksum.pubfilter(reinterpret_cast<const char*>(&endian_4_byte), sizeof(endian_4_byte));
                    checksum.pubfilter(reinterpret_cast<const char*>(&endian_8_byte), sizeof(endian_8_byte));
                    checksum.pubfilter(reinterpret_cast<const char*>(&pi_single), sizeof(pi_single));
                    checksum.pubfilter(reinterpret_cast<const char*>(&pi_double), sizeof(pi_double));

                    // 3. Version-specific bytes 38-39
                    if (version >= 8)
                    {
                        // v8+: frameLibrary + checksumScheme
                        auto frameLibrary = hdr->GetFrameLibrary();
                        checksum.pubfilter(reinterpret_cast<const char*>(&frameLibrary), sizeof(frameLibrary));
                        checksum.pubfilter(reinterpret_cast<const char*>(&checksumScheme), sizeof(checksumScheme));
                    }
                    else
                    {
                        // v3-v7: legacy 'A''Z' marker
                        const char legacy_marker[2] = {'A', 'Z'};
                        checksum.pubfilter(legacy_marker, 2);
                    }

                    retval = checksum.Value();
                }
            }

            return retval;
        }

        namespace FrEndOfFileImpl
        {
            //-----------------------------------------------------------------
            /// @brief The description of structure
            ///
            /// @return
            ///     A Description object which describes this structure as
            ///     specified by the frame specification.
            //-----------------------------------------------------------------
            template <>
            const Common::Description*
            ClassicIO< FrEndOfFile >::GetDescription(
                unsigned int frame_stream_version ) const
            {
                using Common::Description;

                Description* description = nullptr;


#define ASCII( TYPE ) Description::Ascii< TYPE >( )

                if ( frame_stream_version >= 9 )
                {
                    static Description description_v9;

                    if ( description_v9.size( ) == 0 )
                    {
                        description_v9( FrSH( FrameSpecName( ),
                                              FrameSpecId( ),
                                              "End of File Data Structure" ) );
                        description_v9(
                            FrSE( "nFrames",
                                  ASCII( v9::nFrames_type ),
                                  "Number of frames in this file" ) );
                        description_v9(
                            FrSE( "nBytes",
                                  ASCII( v9::nBytes_type ),
                                  "Total number of bytes in this file"
                                  " ( 0 if NOT computed )" ) );
                        description_v9( FrSE(
                            "seekTOC",
                            ASCII( v9::seekTOC_type ),
                            "Byes to back up to the beginning of the table of"
                            " contents structure. If seekTOC == 0, then there"
                            " is no TOC for this file." ) );
                        description_v9( FrSE( "chkSumTOC",
                                              ASCII( v9::chkSumTOC_type ),
                                              "FrTOC uniqueness checksum" ) );
                        description_v9( FrSE( "chkSumFrHeader",
                                              ASCII( v9::chkSumFrHeader_type ),
                                              "FrHeader checksum." ) );
                        description_v9( FrSE( "chkSum",
                                              ASCII( v9::chkSum_type ),
                                              "Structure checksum." ) );
                        description_v9( FrSE( "chkSumFile",
                                              ASCII( v9::chkSumFile_type ),
                                              "File checksum." ) );
                    }
                    description = &description_v9;
                }
                else if ( frame_stream_version >= 8 )
                {
                    static Description description_v8;

                    if ( description_v8.size( ) == 0 )
                    {
                        description_v8( FrSH( FrameSpecName( ),
                                              FrameSpecId( ),
                                              "End of File Data Structure" ) );
                        description_v8(
                            FrSE( "nFrames",
                                  ASCII( v8::nFrames_type ),
                                  "Number of frames in this file" ) );
                        description_v8(
                            FrSE( "nBytes",
                                  ASCII( v8::nBytes_type ),
                                  "Total number of bytes in this file"
                                  " ( 0 if NOT computed )" ) );
                        description_v8( FrSE(
                            "seekTOC",
                            ASCII( v8::seekTOC_type ),
                            "Byes to back up to the beginning of the table of"
                            " contents structure. If seekTOC == 0, then there"
                            " is no TOC for this file." ) );
                        description_v8( FrSE( "chkSumFrHeader",
                                              ASCII( v8::chkSumFrHeader_type ),
                                              "FrHeader checksum." ) );
                        description_v8( FrSE( "chkSum",
                                              ASCII( v8::chkSum_type ),
                                              "Structure checksum." ) );
                        description_v8( FrSE( "chkSumFile",
                                              ASCII( v8::chkSumFile_type ),
                                              "File checksum." ) );
                    }
                    description = &description_v8;
                }
                else if ( frame_stream_version >= 5 )
                {
                    static Description description_v5;

                    if ( description_v5.size( ) == 0 )
                    {
                        description_v5( FrSH( FrameSpecName( ),
                                              FrameSpecId( ),
                                              "End of File Data Structure" ) );
                        description_v5(
                            FrSE( "nFrames",
                                  ASCII( v5::nFrames_type ),
                                  "Number of frames in this file" ) );
                        description_v5(
                            FrSE( "nBytes",
                                  ASCII( v5::nBytes_type ),
                                  "Total number of bytes in this file"
                                  " ( 0 if NOT computed )" ) );
                        description_v5( FrSE( "chkType",
                                              ASCII( v5::chkType_type ),
                                              "File checksum." ) );
                        description_v5( FrSE( "chkSum",
                                              ASCII( v5::chkSum_type ),
                                              "File checksum." ) );
                        description_v5( FrSE(
                            "seekTOC",
                            ASCII( v5::seekTOC_type ),
                            "Byes to back up to the beginning of the table of"
                            " contents structure. If seekTOC == 0, then there"
                            " is no TOC for this file." ) );
                    }
                    description = &description_v5;
                }
                else if ( frame_stream_version >= 4 )
                {
                    static Description description_v4;

                    if ( description_v4.size( ) == 0 )
                    {
                        description_v4( FrSH( FrameSpecName( ),
                                              FrameSpecId( ),
                                              "End of File Data Structure" ) );
                        description_v4(
                            FrSE( "nFrames",
                                  ASCII( v4::nFrames_type ),
                                  "Number of frames in this file" ) );
                        description_v4(
                            FrSE( "nBytes",
                                  ASCII( v4::nBytes_type ),
                                  "Total number of bytes in this file"
                                  " ( 0 if NOT computed )" ) );
                        description_v4( FrSE( "chkFlag",
                                              ASCII( v4::chkFlag_type ),
                                              "File checksum." ) );
                        description_v4( FrSE( "chkSum",
                                              ASCII( v4::chkSum_type ),
                                              "File checksum." ) );
                        description_v4( FrSE(
                            "seekTOC",
                            ASCII( v4::seekTOC_type ),
                            "Byes to back up to the beginning of the table of"
                            " contents structure. If seekTOC == 0, then there"
                            " is no TOC for this file." ) );
                    }
                    description = &description_v4;
                }
                else if ( frame_stream_version >= 3 )
                {
                    static Description description_v3;

                    if ( description_v3.size( ) == 0 )
                    {
                        description_v3( FrSH( FrameSpecName( ),
                                              FrameSpecId( ),
                                              "End of File Data Structure" ) );
                        description_v3(
                            FrSE( "nFrames",
                                  ASCII( v3::nFrames_type ),
                                  "Number of frames in this file" ) );
                        description_v3(
                            FrSE( "nBytes",
                                  ASCII( v3::nBytes_type ),
                                  "Total number of bytes in this file"
                                  " ( 0 if NOT computed )" ) );
                        description_v3(
                            FrSE( "chkFlag",
                                  ASCII( v3::chkFlag_type ),
                                  "Flag for checksum (1 = calculated; 0 = not "
                                  "calculated)" ) );
                        description_v3(
                            FrSE( "chkSum",
                                  ASCII( v3::chkSum_type ),
                                  "File checksum value, calculated exclusive "
                                  "of chkFlag and chkSum." ) );
                    }
                    description = &description_v3;
                }

                return description;
            }

            template <>
            void
            ClassicIO< FrEndOfFile >::assign( INT_4U FrameSpecVersion,
                                              assign_stream_type& Stream )
            {
                logger_log_debug(logger(), 10,
                    "FrEndOfFile::assign() entry, version ", FrameSpecVersion,
                    ", stream position: ", Stream.tellg());

                // Delegate to unified field reader with no-validation policy
                readFieldsWithPolicy<FrEndOfFileImpl::NoValidationPolicy>(
                    FrameSpecVersion, Stream);

                logger_log_debug(logger(), 10,
                    "FrEndOfFile::assign() exit, stream position: ", Stream.tellg());
            }

            template <>
            template < typename STREAM_TYPE >
            void
            ClassicIO< FrEndOfFile >::readFromStream( STREAM_TYPE& Stream )
            {
                auto frame_stream_version = StreamVersion( Stream );

                logger_log_debug(logger(), 10,
                    "FrEndOfFile::readFromStream() entry, version ", frame_stream_version,
                    ", stream position: ", Stream.tellg());

                // Delegate to unified field reader with checksum validation policy
                // The freezeFileChecksum() call within readFieldsWithPolicy will freeze
                // the file checksum before reading chkSumFile field
                readFieldsWithPolicy<FrEndOfFileImpl::ChecksumValidationPolicy>(
                    frame_stream_version, Stream);

                logger_log_debug(logger(), 10,
                    "FrEndOfFile::readFromStream() exit, stream position: ", Stream.tellg());
            }


            //===================================================================
            // Unified Field Reading Templates with Policy-Based Validation
            //===================================================================

            //-----------------------------------------------------------------
            /// \brief Unified field reading implementation
            ///
            /// This is the single source of truth for reading FrEndOfFile
            /// fields. Both assign() and readFromStream() delegate to this
            /// method with different validation policies.
            //-----------------------------------------------------------------
            template <>
            template<typename ValidationPolicy, typename StreamType>
            void
            ClassicIO< FrEndOfFile >::readFieldsWithPolicy(
                INT_4U version,
                StreamType& stream)
            {
                logger_log_debug(logger(), 10,
                    "FrEndOfFile::readFieldsWithPolicy() entry, frame spec version ", version);

                //-----------------------------------------------------------
                // Common fields across all versions
                //-----------------------------------------------------------
                stream >> nFrames;
                logger_log_debug(logger(), 20,
                    "FrEndOfFile::readFieldsWithPolicy() read nFrames: ", nFrames);

                // nBytes field (type varies by version)
                if (version >= 5)
                {
                    stream >> nBytes;
                    logger_log_debug(logger(), 20,
                        "FrEndOfFile::readFieldsWithPolicy() read nBytes (v5+): ", nBytes);
                }
                else if (version >= 3)
                {
                    v3::nBytes_type nBytes_v3;
                    stream >> nBytes_v3;
                    nBytes = nBytes_v3;
                    logger_log_debug(logger(), 20,
                        "FrEndOfFile::readFieldsWithPolicy() read nBytes (v3): ", nBytes_v3, " -> ", nBytes);
                }

                //-----------------------------------------------------------
                // Version-specific field layouts
                //-----------------------------------------------------------
                if (version >= 8)
                {
                    // Modern layout (v8-v9): Simple sequential reads
                    readModernFormat<ValidationPolicy>(version, stream);
                }
                else if (version >= 3)
                {
                    // Legacy layout (v3-v7): Complex filter manipulation
                    readLegacyFormat<ValidationPolicy>(version, stream);
                }

                logger_log_debug(logger(), 30,
                    "FrEndOfFile::readFieldsWithPolicy() completed - nFrames: ", nFrames, ", nBytes: ", nBytes, ", seekTOC: ", seekTOC);
            }

            //-----------------------------------------------------------------
            /// \brief Read modern format fields (v8-v9)
            //-----------------------------------------------------------------
            template <>
            template<typename ValidationPolicy, typename StreamType>
            void
            ClassicIO< FrEndOfFile >::readModernFormat(
                INT_4U version,
                StreamType& stream)
            {
                logger_log_debug(logger(), 20,
                    "FrEndOfFile::readModernFormat() processing v", version, " format, position: ", stream.tellg());

                auto pos_before_seekTOC = stream.tellg();
                stream >> seekTOC;
                logger_log_debug(logger(), 20,
                    "FrEndOfFile::readModernFormat() read seekTOC: ", seekTOC,
                    " (pos before: ", pos_before_seekTOC, ", after: ", stream.tellg(), ")");

                if (version >= 9)
                {
                    auto pos_before_chkSumTOC = stream.tellg();
                    stream >> chkSumTOC;
                    logger_log_debug(logger(), 20,
                        "FrEndOfFile::readModernFormat() read chkSumTOC: ",
                        pretty_print_checksum(chkSumTOC),
                        " (pos before: ", pos_before_chkSumTOC, ", after: ", stream.tellg(), ")");
                }

                auto pos_before_chkSumFrHeader = stream.tellg();
                stream >> chkSumFrHeader;
                logger_log_debug(logger(), 20,
                    "FrEndOfFile::readModernFormat() read chkSumFrHeader: ",
                    pretty_print_checksum(chkSumFrHeader),
                    " (pos before: ", pos_before_chkSumFrHeader, ", after: ", stream.tellg(), ")");

                // Policy freezes structure checksum BEFORE reading chkSum field
                auto pos_before_freeze_struct = stream.tellg();
                ValidationPolicy::freezeStructureChecksum(stream);
                auto pos_after_freeze_struct = stream.tellg();
                logger_log_debug(logger(), 20,
                    "FrEndOfFile::readModernFormat() froze structure checksum (pos before: ",
                    pos_before_freeze_struct, ", after: ", pos_after_freeze_struct, ")");

                auto pos_before_chkSum = stream.tellg();
                stream >> chkSum;
                logger_log_debug(logger(), 20,
                    "FrEndOfFile::readModernFormat() read chkSum: ",
                    pretty_print_checksum(chkSum),
                    " (pos before: ", pos_before_chkSum, ", after: ", stream.tellg(), ")");

                // Policy freezes file checksum BEFORE reading chkSumFile field
                auto pos_before_freeze_file = stream.tellg();
                ValidationPolicy::freezeFileChecksum(stream);
                auto pos_after_freeze_file = stream.tellg();
                logger_log_debug(logger(), 20,
                    "FrEndOfFile::readModernFormat() froze file checksum (pos before: ",
                    pos_before_freeze_file, ", after: ", pos_after_freeze_file, ")");

                auto pos_before_chkSumFile = stream.tellg();
                stream >> chkSumFile;
                logger_log_debug(logger(), 20,
                    "FrEndOfFile::readModernFormat() read chkSumFile: ",
                    pretty_print_checksum(chkSumFile),
                    " (pos before: ", pos_before_chkSumFile, ", after: ", stream.tellg(), ")");

                // Policy controls validation
                ValidationPolicy::validateChecksums(stream, version, *this);

                logger_log_debug(logger(), 30,
                    "FrEndOfFile::readModernFormat() completed");
            }

            //-----------------------------------------------------------------
            /// \brief Read legacy format fields (v3-v7)
            //-----------------------------------------------------------------
            template <>
            template<typename ValidationPolicy, typename StreamType>
            void
            ClassicIO< FrEndOfFile >::readLegacyFormat(
                INT_4U version,
                StreamType& stream)
            {
                logger_log_debug(logger(), 20,
                    "FrEndOfFile::readLegacyFormat() processing v", version, " format");

                //-----------------------------------------------------------
                // Policy controls filter suspension before checksum field
                // Returns ChecksumState for later restoration
                //-----------------------------------------------------------
                //-----------------------------------------------------------
                // For v5+: Read chkType FIRST (accumulated) before suspending filter
                // This matches WRITE path which manually adds chkType before freezing
                //-----------------------------------------------------------
                ChecksumState saved_checksum_state;
                if (version >= 5)
                {
                    v5::chkType_type chkType_v5;
                    stream >> chkType_v5;
                    legacyChecksumType = chkType_v5;  // Store for use in VerifyObject()
                    logger_log_debug(logger(), 30,
                        "FrEndOfFile::readLegacyFormat() read chkType (v5) BEFORE suspension: ", chkType_v5);

                    // Now add zero placeholder and suspend for chkSum field
                    saved_checksum_state = ValidationPolicy::beforeChecksumField(stream, version);
                }
                else
                {
                    // v3-v4: Suspend first, then read chkType/chkFlag
                    saved_checksum_state = ValidationPolicy::beforeChecksumField(stream, version);

                    if (version >= 3)
                    {
                        v3::chkFlag_type chkFlag_v3;
                        stream >> chkFlag_v3;
                        legacyChecksumType = chkFlag_v3;  // Store for use in VerifyObject()
                        logger_log_debug(logger(), 30,
                            "FrEndOfFile::readLegacyFormat() read chkFlag (v3) AFTER suspension: ", chkFlag_v3);
                    }
                }

                // Read checksum value itself (always not accumulated - filter suspended)
                v3::chkSum_type chkSum_v3;
                stream >> chkSum_v3;
                chkSumFile = chkSum_v3;
                logger_log_debug(logger(), 30,
                    "FrEndOfFile::readLegacyFormat() read chkSum: ",
                    pretty_print_checksum(chkSum_v3), " -> chkSumFile: ",
                    pretty_print_checksum(chkSumFile));

                //-----------------------------------------------------------
                // Policy controls filter restoration after checksum field
                //-----------------------------------------------------------
                ValidationPolicy::afterChecksumField(stream, version, std::move(saved_checksum_state));

                //-----------------------------------------------------------
                // Read remaining fields (seekTOC for v4+)
                //-----------------------------------------------------------
                if (version >= 5)
                {
                    stream >> seekTOC;
                    logger_log_debug(logger(), 30,
                        "FrEndOfFile::readLegacyFormat() read seekTOC (v5): ", seekTOC);
                }
                else if (version >= 4)
                {
                    v4::seekTOC_type seekTOC_v4;
                    stream >> seekTOC_v4;
                    seekTOC = seekTOC_v4;
                    logger_log_debug(logger(), 30,
                        "FrEndOfFile::readLegacyFormat() read seekTOC (v4): ",
                        seekTOC_v4, " -> ", seekTOC);
                }

                //-----------------------------------------------------------
                // Policy controls validation
                //-----------------------------------------------------------
                ValidationPolicy::validateChecksums(stream, version, *this);

                logger_log_debug(logger(), 30,
                    "FrEndOfFile::readLegacyFormat() completed");
            }

        } // namespace FrEndOfFileImpl

        //===================================================================
        // Explicit Template Instantiations for IStream
        //===================================================================
        // These instantiations ensure that the template methods defined in
        // FrEndOfFile.cc are compiled for Common::IStream, which is required
        // for IFrameStream to properly read FrEndOfFile objects.

        namespace FrEndOfFileImpl
        {
            // Instantiate readFromStream for Common::IStream
            template void ClassicIO<FrEndOfFile>::readFromStream<Common::IStream>(Common::IStream&);

            // Instantiate readFieldsWithPolicy for ChecksumValidationPolicy with Common::IStream
            template void ClassicIO<FrEndOfFile>::readFieldsWithPolicy<ChecksumValidationPolicy, Common::IStream>(INT_4U, Common::IStream&);

            // Instantiate readModernFormat for ChecksumValidationPolicy with Common::IStream
            template void ClassicIO<FrEndOfFile>::readModernFormat<ChecksumValidationPolicy, Common::IStream>(INT_4U, Common::IStream&);

            // Instantiate readLegacyFormat for ChecksumValidationPolicy with Common::IStream
            template void ClassicIO<FrEndOfFile>::readLegacyFormat<ChecksumValidationPolicy, Common::IStream>(INT_4U, Common::IStream&);
        } // namespace FrEndOfFileImpl

    } // namespace Universal
} // namespace FrameCPP
