//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2019 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__PTR_STRUCTClassicIO_HH
#define FrameCPP_UNIVERSAL__IMPL__PTR_STRUCTClassicIO_HH

#include <string>

#if !defined( SWIGIMPORTED )
#include "framecpp/Common/FrameSpec.hh"
#include "framecpp/Common/Description.hh"
#include "framecpp/Common/PTR_STRUCT_BASE.hh"
#endif /* !defined( SWIGIMPORTED ) */

#if !defined( SWIGIMPORTED )
#include "framecpp/Universal/impl/STRING.hh"
#include "framecpp/Universal/impl/GPSTime.hh"
#endif /* !defined( SWIGIMPORTED ) */
#include "framecpp/Universal/impl/PTR_STRUCTData.hh"
#if !defined( SWIGIMPORTED )
#include "framecpp/Universal/impl/FrameSpec.hh"
#endif /* !defined( SWIGIMPORTED ) */

#if !defined( SWIGIMPORTED )
namespace FrameCPP
{
    namespace Universal
    {
        namespace PTR_STRUCTImpl
        {
            template < typename PTR_STRUCT_T >
            class ClassicIO : public virtual Data,
                              public Common::PTR_STRUCT_BASE
            {
            public:
                // Bring base class GetDescription methods into scope
                using Common::FrameSpec::Object::GetDescription;

                //-------------------------------------------------------
                //
                //-------------------------------------------------------
                ClassicIO( );

                //-----------------------------------------------------------------
                /// \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
                Bytes( const Common::StreamBase& Stream ) const override;

                Common::PTR_STRUCT_BASE::class_type Class( ) const override;

                void
                Class( Common::PTR_STRUCT_BASE::class_type ClassId ) override;

                virtual Common::FrameSpec::Object*
                Create( istream_type& Stream ) const override;

                //-----------------------------------------------------------------
                /// \brief Get FrSH/FrSE description of object
                ///
                /// FrSH is a meta-description class, so it returns an empty
                /// description
                //-----------------------------------------------------------------
                virtual const Common::Description* GetDescription( ) const override;

                //-----------------------------------------------------------------
                /// \brief Get FrSH/FrSE description of object for a specific
                /// version
                ///
                /// FrSH is a meta-description class, so it returns an empty
                /// description
                //-----------------------------------------------------------------
                virtual const Common::Description*
                GetDescription( unsigned int version ) const override;

                Common::PTR_STRUCT_BASE::instance_type
                Instance( ) const override
                {
                    return ( Data::Instance( ) );
                }

                void
                Instance( Data::instance_type Value ) override
                {
                    Data::Instance( Value );
                }

                //-----------------------------------------------------------------
                /// \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 override
                {
                    return ( FrameSpecName( ) );
                }

                //-----------------------------------------------------------------
                /// \brief The description of structure
                ///
                /// \return
                ///     A Description object which describes this structure as
                ///     specified by the frame specification.
                //-----------------------------------------------------------------
                static const Common::Description*
                StructDescription( )
                {
                    return nullptr;
                }

                PTR_STRUCT_BASE*
                Clone( ) const override
                {
                    return ( new PTR_STRUCT_T( *(
                        reinterpret_cast< const PTR_STRUCT_T* >( this ) ) ) );
                }

                //-----------------------------------------------------------------
                /// \brief Describe the reference object.
                ///
                /// \param[in] Type
                ///     The type of object being referenced.
                ///
                /// \return
                ///     The text that describes the pointer reference.
                //-----------------------------------------------------------------
                static inline std::string
                Desc( const std::string& Type )
                {
                    std::string ret( "PTR_STRUCT(" );

                    ret += Type;
                    ret += " *)";
                    return ret;
                }

                //-----------------------------------------------------------------
                /// \brief Determine if the reference represents a NULL
                /// reference
                ///
                /// \return
                ///     True if the reference represents a NULL reference,
                ///     false otherwise.
                //-----------------------------------------------------------------
                inline bool
                IsNULL( ) const
                {
                    return ( ( Class( ) == 0 ) && ( Instance( ) == 0 ) );
                }

                //-----------------------------------------------------------------
                /// \brief Write the structure to the stream
                ///
                /// \param[in] Stream
                ///     The output stream where the object is to be written.
                //-----------------------------------------------------------------
                virtual void
                Write( ostream_type& Stream ) const override
                {
                    auto frame_stream_version = StreamVersion( Stream );

                    Stream << data_class;
                    if ( frame_stream_version >= 5 )
                    {
                        Stream << data_instance;
                    }
                    else if ( frame_stream_version >= 3 )
                    {
                        Stream << v3::instance_type( data_instance );
                    }
                }

                //-----------------------------------------------------------------
                /// \brief equality operator for abstract data type
                ///
                /// \param[in] RHS
                ///     The object to be compared.
                ///
                /// \return
                ///     The value true is returned if this object is equivelent
                ///     to the RHS instance; false otherwise.
                //-----------------------------------------------------------------
                virtual bool
                operator==( const Common::FrameSpec::Object& RHS ) const override;

            protected:
                /* typedef Previous::FrSH previous_type; */
                /* typedef PTR_STRUCT_T        current_type; */

                typedef STRING io_name_type;
                typedef STRING io_comment_type;

#if 0
                //-------------------------------------------------------
                /// \brief Assign the contents of the instance
                ///
                /// \param[in] Stream
                ///     An input stream from which to read information for
                ///     the instance.
                //-------------------------------------------------------
                virtual void
                assign( assign_stream_type& Stream )
                {
                    io_name_type    io_name;
                    io_comment_type io_comment;

                    Stream >> io_name >> klass >> io_comment;

                    Data::name = io_name;
                    comment = io_comment;
                }
#endif /* 0 */

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

            private:
                //-----------------------------------------------------------------
                //-----------------------------------------------------------------
                typedef Common::FrameSpec::Object object_type;

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

        } // namespace PTR_STRUCTImpl
    } // namespace Universal
} // namespace FrameCPP

#endif /* !defined( SWIGIMPORTED ) */

#endif /* FrameCPP_UNIVERSAL__IMPL__FrSHClassicIO_HH */
