//
// 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_UNIVERSAL__IMPL__FrTableClassicIO_HH
#define FrameCPP_UNIVERSAL__IMPL__FrTableClassicIO_HH

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

#include "framecpp/Common/Description.hh"
#include "framecpp/Common/FrameStream.hh"
#include "framecpp/Common/TOCInfo.hh"

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

namespace FrameCPP
{
    namespace Universal
    {
        class FrTable;

        namespace FrTableImpl
        {
            template < typename T >
            class ClassicIO
                : public virtual Data,
                  public ObjectWithChecksum< Common::FrameSpec::Object >
            {
            public:
                // Bring base class GetDescription methods into scope
                using FrameCPP::Common::FrameSpec::Object::GetDescription;
                
                //-------------------------------------------------------
                //
                //-------------------------------------------------------
                ClassicIO( )
                    : ObjectWithChecksum< Common::FrameSpec::Object >(
                          FrameSpecId( ), nullptr )
                {
                }

                // Explicitly delete move assignment operator to avoid 
                // virtual inheritance issues
                ClassicIO& operator=(ClassicIO&&) = delete;

                virtual const Common::Description*
                GetDescription( unsigned int version ) const;

            protected:
                typedef STRING io_name_type;
                typedef STRING io_comment_type;

                //-----------------------------------------------------------------
                /// \brief The name structure name of this object.
                ///
                /// \return
                ///     The name of the structure as specified by the frame
                ///     specification.
                //-----------------------------------------------------------------
                virtual const char*
                ObjectStructName( ) const
                {
                    return ( FrameSpecName( ) );
                }

                //-----------------------------------------------------------------
                /// \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 cmn_streamsize_type
                pBytes( const Common::StreamBase& Stream ) const
                {
                    auto frame_stream_version = StreamVersion( Stream );

                    cmn_streamsize_type columnNameSize = 0;
                    cmn_streamsize_type bytes = 0;

                    if ( frame_stream_version >= 4 )
                    {
                        for ( auto& column_element : column )
                        {
                            columnNameSize +=
                                STRING::Bytes( column_element->GetName( ) );
                        }
                        bytes = //
                            io_name_type::Bytes( name ) + // name
                            io_comment_type::Bytes( comment ) + // comment
                            sizeof( nColumn_type ) + // nColumn
                            sizeof( nRow_type ) + // nRow
                            columnNameSize + // columnName
                            Stream.PtrStructBytes( ) + // column
                            Stream.PtrStructBytes( ) // next
                            ;
                    }
                    return bytes;
                }

                //-----------------------------------------------------------------
                /// \brief Virtual constructor
                ///
                /// \param[in] Stream
                ///     The input stream from where the object is being read.
                //-----------------------------------------------------------------
                virtual self_type*
                pCreate( Common::IStream& Stream ) const
                {
                    T* obj( new T );

                    typedef std::vector< STRING > io_column_name_container_type;
                    typedef Common::IStream::
                        Resizer< io_column_name_container_type, nColumn_type >
                                                  resizer;
                    io_name_type                  io_name;
                    io_comment_type               io_comment;
                    nColumn_type                  nColumn;
                    nRow_type                     nRow;
                    io_column_name_container_type columnName;

                    // Read
                    Stream //
                        >> io_name //
                        >> io_comment //
                        >> nColumn //
                        >> nRow //
                        >> resizer( columnName, nColumn ) >> columnName //
                        >> obj->column //
                        ;
                    Stream.Next( obj );

                    // Post transformations
                    obj->name = io_name;
                    obj->comment = io_comment;

                    return ( obj );
                }

                //-----------------------------------------------------------------
                /// \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
                {
                    auto frame_stream_version = StreamVersion( Stream );

                    if ( frame_stream_version < 4 )
                    {
                        return;
                    }

                    std::vector< STRING > io_columnName;

                    for ( auto& column_element : column )
                    {
                        io_columnName.push_back(
                            STRING( column_element->GetName( ) ) );
                    }
                    Stream << io_name_type( name ) //
                           << io_comment_type( comment ) //
                           << nColumn_type( column.size( ) ) // nColumn
                           << nRow_type( GetNRow( ) ) //
                           << io_columnName //
                           << column //
                        ;

                    WriteNext( Stream );
                }

            private:
                //-----------------------------------------------------------------
                /// \brief Object with checksum data.
                //-----------------------------------------------------------------
                typedef ObjectWithChecksum< Common::FrameSpec::Object >
                    object_type;

                using Common::FrameSpec::Object::Create;
            };

        } // namespace FrTableImpl
    } // namespace Universal
} // namespace FrameCPP

#endif /* FrameCPP_UNIVERSAL__IMPL__FrTableClassicIO_HH */
