//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2018-2020 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__FrSummaryClassicIO_HH
#define FrameCPP_UNIVERSAL__IMPL__FrSummaryClassicIO_HH

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

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

#include <sstream>

#include "framecpp/Common/Description.hh"
#include "framecpp/Common/FrameSpec.hh"
#include "framecpp/Common/FrameSpec.tcc"
#include "framecpp/Common/TOCInfo.hh"

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

namespace FrameCPP
{
    namespace Universal
    {

        class FrSummary;

        namespace FrSummaryImpl
        {
            template < typename T >
            class ClassicIO
                : public virtual Data,
                  public Common::TOCInfo,
                  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 )
                {
                }

                ClassicIO( const ClassicIO& ) = default;
                ClassicIO( ClassicIO&& ) = default;
                ClassicIO& operator=( const ClassicIO& ) = delete;
                ClassicIO& operator=( ClassicIO&& ) = delete;

                //-----------------------------------------------------------------
                /// \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 StructName( );
                }

                //-----------------------------------------------------------------
                /// @brief The data structure description
                ///
                /// @return
                ///     The description of the data structure used to represent
                ///     this object when written to a GWF stream
                //-----------------------------------------------------------------
                virtual const Common::Description*
                GetDescription( unsigned int version ) const;

#if WORKING_VIRTUAL_TOCQUERY
            public:
                //-----------------------------------------------------------------
                /// \brief Answer requests for information as needed by TOC
                ///
                /// \param[in] InfoClass
                ///     Class of information being requested.
                //-----------------------------------------------------------------
                virtual void
                TOCQuery( int InfoClass, ... ) const

#else
            protected:
                //-----------------------------------------------------------------
                /// \brief Answer requests for information as needed by TOC
                ///
                /// \param[in] InfoClass
                ///     Class of information being requested.
                /// \param[in] vl
                ///     Variable argument list of requested data.
                //-----------------------------------------------------------------
                virtual void
                vTOCQuery( int InfoClass, va_list vl ) const
#endif /* ! WORKING_VIRTUAL_TOCQUERY */
                {
                    using Common::TOCInfo;

#if WORKING_VIRTUAL_TOCQUERY
                    va_list vl;
                    va_start( vl, InfoClass );
#endif /*  WORKING_VIRTUAL_TOCQUERY */

                    while ( InfoClass != TOCInfo::IC_EOQ )
                    {
                        int data_type = va_arg( vl, int );
                        switch ( data_type )
                        {
                        case TOCInfo::DT_STRING_2:
                        {
                            STRING* data = va_arg( vl, STRING* );
                            switch ( InfoClass )
                            {
                            case TOCInfo::IC_NAME:
                                *data = GetName( );
                                break;
                            default:
                                goto cleanup;
                                break;
                            }
                        }
                        break;
                        default:
                            // Stop processing
                            goto cleanup;
                        }
                        InfoClass = va_arg( vl, int );
                    }
                cleanup:
#if WORKING_VIRTUAL_TOCQUERY
                    va_end( vl )
#endif /*  WORKING_VIRTUAL_TOCQUERY */
                        ;
                }

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

                    if ( frame_stream_version >= 3 )
                    {
                        retval = ( io_name_type::Bytes( name ) //
                                   + io_comment_type::Bytes( comment ) //
                                   + io_test_type::Bytes( test ) //
                                   + ( ( frame_stream_version >= 5 )
                                           ? sizeof( io_gtimes_type ) //
                                               + sizeof( io_gtimen_type )
                                           : 0 ) //
                                   + Stream.PtrStructBytes( ) // moments
                                   + ( ( frame_stream_version >= 4 ) // table
                                           ? Stream.PtrStructBytes( )
                                           : 0 ) // table
                                   + Stream.PtrStructBytes( ) // next
                        );
                    }
                    return ( retval );
                }

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

                    T* obj( new T( ) );

                    io_name_type    io_name;
                    io_comment_type io_comment;
                    io_test_type    io_test;

                    if ( frame_stream_version >= 3 )
                    {
                        Stream >> io_name >> io_comment >> io_test;
                    }
                    if ( frame_stream_version >= 5 )
                    {
                        Stream >> obj->gtime;
                    }

                    if ( frame_stream_version >= 3 )
                    {
                        Stream >> obj->moments;
                    }
                    if ( frame_stream_version >= 4 )
                    {
                        Stream >> obj->table;
                    }

                    if ( frame_stream_version >= 3 )
                    {
                        Stream.Next( obj );
                    }

                    obj->name = io_name;
                    obj->comment = io_comment;
                    obj->test = io_test;

                    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 );

                    Stream << io_name_type( name ) << io_comment_type( comment )
                           << io_test_type( test );
                    if ( frame_stream_version >= 5 )
                    {
                        Stream << io_gtimes_type( gtime.GetSeconds( ) )
                               << io_gtimen_type( gtime.GetNanoseconds( ) );
                    }
                    Stream << moments;
                    if ( frame_stream_version >= 4 )
                    {
                        Stream << table;
                    }
                    WriteNext( Stream );
                }

            private:
                typedef STRING io_name_type;
                typedef STRING io_comment_type;
                typedef STRING io_test_type;
                typedef INT_4U io_gtimes_type;
                typedef INT_4U io_gtimen_type;
            }; // class ClassicIO
        } // namespace FrSummaryImpl
    } // namespace Universal
} // namespace FrameCPP

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

#endif /* FrameCPP_UNIVERSAL__IMPL__FrSummaryClassicIO_HH */
