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

#ifndef FrameCPP_UNIVERSAL__IMPL__FrTOCClassicIO_HH
#define FrameCPP_UNIVERSAL__IMPL__FrTOCClassicIO_HH

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

// Forward declaration to avoid circular dependencies
namespace FrameCPP {
namespace Common {
    struct FrameCPPServices;
}
}

#if defined( __cplusplus ) && !defined( SWIG )
#include <iostream>
#include <map>
#include <vector>
#include <typeinfo>

#include "framecpp/Common/CheckSum.hh"
#include "framecpp/Common/Description.hh"
#include "framecpp/Common/FrameSpec.hh"
#include "framecpp/Common/FrTOC.hh"

#include "framecpp/Universal/impl/FrameSpec.hh"
#include "ldastoolsal/Logger.hh"
#endif /* defined( __cplusplus ) && !defined( SWIG ) */

#if defined( __cplusplus )
#include "framecpp/Universal/impl/FrTOCAdcDataClassicIO.hh"
#include "framecpp/Universal/impl/FrTOCDataClassicIO.hh"
#include "framecpp/Universal/impl/FrTOCEventClassicIO.hh"
#include "framecpp/Universal/impl/FrTOCProcDataClassicIO.hh"
#include "framecpp/Universal/impl/FrTOCSerDataClassicIO.hh"
#include "framecpp/Universal/impl/FrTOCSimDataClassicIO.hh"
#include "framecpp/Universal/impl/FrTOCSimEventClassicIO.hh"
#include "framecpp/Universal/impl/FrTOCSummaryClassicIO.hh"
#include "framecpp/Universal/impl/FrTOCStatDataClassicIO.hh"

#endif /* defined( __cplusplus ) */

#if defined( __cplusplus ) && !defined( SWIG )

namespace FrameCPP
{
    namespace Universal
    {

        class FrTOC;

        namespace FrTOCImpl
        {
            template < typename T >
            class ClassicIO : public virtual Data,
                              public ObjectWithChecksum< Common::FrTOC >,
                              public FrTOCDataClassicIO,
                              public FrTOCAdcDataClassicIO,
                              public FrTOCProcDataClassicIO,
                              public FrTOCSimDataClassicIO,
                              public FrTOCSerDataClassicIO,
                              public FrTOCSummaryClassicIO,
                              public FrTOCEventClassicIO,
                              public FrTOCSimEventClassicIO,
                              public FrTOCStatDataClassicIO
            {
            public:
                // Bring base class GetDescription methods into scope
                using FrameCPP::Common::FrameSpec::Object::GetDescription;

                //-----------------------------------------------------------------
                /// @brief Default constructor with debugging
                //-----------------------------------------------------------------
                ClassicIO( ) : ObjectWithChecksum< Common::FrTOC >( nullptr )
                {
                    if (logger()) {
                        logger_log_debug(logger(), 30, "FrTOC::ClassicIO::ClassicIO() DEFAULT constructor, this=", static_cast<const void*>(this));
                        logger_log_debug(logger(), 30, "FrTOC::ClassicIO::ClassicIO() FrTOCDataData base address=", static_cast<const void*>(static_cast<const FrTOCDataData*>(this)));
                    }
                }

                /// @brief Copy constructor with debugging
                ClassicIO( const ClassicIO& other ) : ObjectWithChecksum< Common::FrTOC >( nullptr )
                {
                    if (logger()) {
                        logger_log_debug(logger(), 20, "FrTOC::ClassicIO::ClassicIO() COPY constructor, this=", static_cast<const void*>(this), " from=", static_cast<const void*>(&other));
                        logger_log_debug(logger(), 20, "FrTOC::ClassicIO::ClassicIO() COPY: new FrTOCDataData base address=", static_cast<const void*>(static_cast<const FrTOCDataData*>(this)));
                        logger_log_debug(logger(), 20, "FrTOC::ClassicIO::ClassicIO() COPY: source FrTOCDataData base address=", static_cast<const void*>(static_cast<const FrTOCDataData*>(&other)));
                    }
                }
                /// @brief Move constructor with debugging
                ClassicIO( ClassicIO&& other ) noexcept : ObjectWithChecksum< Common::FrTOC >( nullptr )
                {
                    if (logger()) {
                        logger_log_debug(logger(), 20, "FrTOC::ClassicIO::ClassicIO() MOVE constructor, this=", static_cast<const void*>(this), " from=", static_cast<const void*>(&other));
                        logger_log_debug(logger(), 20, "FrTOC::ClassicIO::ClassicIO() MOVE: new FrTOCDataData base address=", static_cast<const void*>(static_cast<const FrTOCDataData*>(this)));
                    }
                }
                ClassicIO& operator=( const ClassicIO& ) = delete;
                ClassicIO& operator=( ClassicIO&& ) = delete;

                //---------------------------------------------------------------
                /// @brief Return TOC identifying checksum
                ///
                /// The CRC checksum that characterizes this Table of Contents.
                ///
                /// @return
                ///   The checksum that characterizes this Table of Contents
                ///   structure
                //---------------------------------------------------------------
                virtual INT_4U GetTOCChecksum( ) override;

                //-----------------------------------------------------------------
                /// \brief The description of structure
                ///
                /// \return
                ///     A Description object which describes this structure as
                ///     specified by the frame specification.
                //-----------------------------------------------------------------
                const Common::Description*
                GetDescription( unsigned int version ) const override;

            protected:
                //-----------------------------------------------------------------
                /// \brief Number of bytes needed to write this structure
                ///
                /// \param[in] Stream
                ///     The stream from which to the object is being read or
                ///     written.
                ///
                /// \return
                ///     The number of bytes need to read or write this object.
                //-----------------------------------------------------------------
                virtual Common::FrameSpec::size_type
                pBytes( const Common::StreamBase& Stream ) const override;

                //-----------------------------------------------------------------
                /// \brief Virtual constructor
                ///
                /// \param[in] Stream
                ///     The input stream from where the object is being read.
                //-----------------------------------------------------------------
                virtual self_type* pCreate( istream_type& Stream ) const override;

                //-----------------------------------------------------------------
                /// \brief Write the structure to the stream
                ///
                /// \param[in] Stream
                ///     The output stream where the object is to be written.
                //-----------------------------------------------------------------
                virtual void pWrite( Common::OStream& Stream ) const override;

                //=================================================================
                // TRULY DRY Helper Member Functions using Variadic Templates
                // These can access protected methods since they're part of the
                // class hierarchy
                //=================================================================

                // Single source of truth for FrTOC base class ordering
                using FrTOCBaseClasses = std::tuple< FrTOCDataClassicIO,
                                                     FrTOCStatDataClassicIO,
                                                     FrTOCAdcDataClassicIO,
                                                     FrTOCProcDataClassicIO,
                                                     FrTOCSimDataClassicIO,
                                                     FrTOCSerDataClassicIO,
                                                     FrTOCSummaryClassicIO,
                                                     FrTOCEventClassicIO,
                                                     FrTOCSimEventClassicIO >;

                // Helper to call Bytes() on all base classes using fold
                // expression
                template < std::size_t... Is >
                Common::FrameSpec::size_type
                call_all_bytes_impl( const Common::StreamBase& stream,
                                     std::index_sequence< Is... > ) const
                {
                    return (
                        std::tuple_element_t< Is, FrTOCBaseClasses >::Bytes(
                            stream ) +
                        ... );
                }

                // Helper to call write() on all base classes using fold
                // expression
                template < std::size_t... Is >
                void
                call_all_write_impl( Common::OStream& stream,
                                     std::index_sequence< Is... > ) const
                {
                    ( std::tuple_element_t< Is, FrTOCBaseClasses >::write(
                          stream ),
                      ... );
                }

                // Helper to call GetDescription() on all base classes using
                // fold expression
                template < std::size_t... Is >
                void
                call_all_get_description_impl(
                    unsigned int         frame_stream_version,
                    Common::Description& desc,
                    std::index_sequence< Is... > ) const
                {
                    ( std::tuple_element_t< Is, FrTOCBaseClasses >::
                          GetDescription( frame_stream_version, desc ),
                      ... );
                }

                /// \brief Call Bytes() method on all base classes using truly
                /// DRY approach
                Common::FrameSpec::size_type
                call_all_bytes_dry( const Common::StreamBase& stream ) const
                {
                    return call_all_bytes_impl(
                        stream,
                        std::make_index_sequence<
                            std::tuple_size_v< FrTOCBaseClasses > >{} );
                }

                /// \brief Call write() method on all base classes using truly
                /// DRY approach
                void
                call_all_write_dry( Common::OStream& stream ) const
                {
                    call_all_write_impl(
                        stream,
                        std::make_index_sequence<
                            std::tuple_size_v< FrTOCBaseClasses > >{} );
                }

                /// \brief Call GetDescription() method on all base classes
                /// using truly DRY approach
                void
                call_all_get_description_dry( unsigned int frame_stream_version,
                                              Common::Description& desc ) const
                {
                    call_all_get_description_impl(
                        frame_stream_version,
                        desc,
                        std::make_index_sequence<
                            std::tuple_size_v< FrTOCBaseClasses > >{} );
                }

                // Helper to call Load() on all base classes using fold
                // expression
                template < std::size_t... Is >
                void
                call_all_load_impl( ClassicIO*    target,
                                    istream_type& stream,
                                    INT_4U&       nframe,
                                    std::index_sequence< Is... > ) const
                {
                    // Debug: Log which Load methods are being called
                    if (logger()) {
                        logger_log_debug(logger(), 30, "FrTOC::call_all_load_impl() entry, nframe=", nframe);
                        logger_log_debug(logger(), 30, "FrTOC::call_all_load_impl() about to call Load() on ", std::tuple_size_v<FrTOCBaseClasses>, " components");
                        logger_log_debug(logger(), 30, "FrTOC::call_all_load_impl() target object address=", static_cast<const void*>(target));
                    }

                    // Apply fold expression to call all Load methods on target
                    ( target->std::template tuple_element< Is, FrTOCBaseClasses >::type::Load(stream, nframe),
                      ... );

                    if (logger()) {
                        logger_log_debug(logger(), 30, "FrTOC::call_all_load_impl() completed, final nframe=", nframe);
                    }
                }

                /// \brief Call Load() method on all base classes using truly
                /// DRY approach
                void
                call_all_load_dry( Universal::FrTOC* obj,
                                   istream_type&     stream ) const
                {
                    // CRITICAL: Load directly into obj, not into this!
                    // Cast obj to ClassicIO* so we can call Load() methods on it
                    ClassicIO* mutable_obj = static_cast<ClassicIO*>(obj);

                    if (logger()) {
                        logger_log_debug(logger(), 5, "call_all_load_dry() ENTRY - About to call Load() directly on obj");
                        logger_log_debug(logger(), 5, "call_all_load_dry() Universal::FrTOC* obj address=", static_cast<const void*>(obj));
                        logger_log_debug(logger(), 5, "call_all_load_dry() ClassicIO* mutable_obj address=", static_cast<const void*>(mutable_obj));
                    }

                    INT_4U nframe = 0; // Initialize to 0, FrTOCDataClassicIO will update it

                    // Load directly into mutable_obj (not this!) - no copy needed
                    call_all_load_impl(
                        mutable_obj,
                        stream,
                        nframe,
                        std::make_index_sequence<
                            std::tuple_size_v< FrTOCBaseClasses > >{} );

                    if (logger()) {
                        logger_log_debug(logger(), 5, "call_all_load_dry() completed, final nframe=", nframe);
                    }
                }

            protected:
                /// @brief Stub for old static logger - always returns nullptr
                /// @deprecated This is only here to prevent compilation errors. Use logger(services) instead.
                /// @return Always returns nullptr
                static SimpleLogger* logger() {
                    return nullptr;
                }
                
                /// @brief Get the logger for FrTOC ClassicIO debugging (dependency injection version)
                /// @param services FrameCPP services context containing logger manager
                /// @return Logger instance from services context
                static SimpleLogger* logger(const FrameCPP::Common::FrameCPPServices& services);

            private:
                void fill_description( unsigned int frame_stream_version,
                                       Common::Description& Desc ) const;

            }; // End class ClassicIO
        } // namespace FrTOCImpl
    } // namespace Universal
} // namespace FrameCPP

#endif /* defined( __cplusplus ) && !defined( SWIG ) */

#endif /* FrameCPP_UNIVERSAL__IMPL__FrTOCClassicIO_HH */
