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

#include <boost/shared_ptr.hpp>

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

#include "framecpp/Universal/PTR_STRUCT.hh"
#include "framecpp/Universal/FrSH.hh"
#include "framecpp/Universal/FrSE.hh"


using FrameCPP::Common::Description;
using FrameCPP::Common::FrameSpec;

//=======================================================================
//               C L A S S I C   I O
//=======================================================================
namespace FrameCPP
{
    namespace Universal
    {
        namespace PTR_STRUCTImpl
        {
            template <>
            ClassicIO< PTR_STRUCT >::ClassicIO( ) : Common::PTR_STRUCT_BASE( 0 )
            {
            }

            template <>
            cmn_streamsize_type
            ClassicIO< PTR_STRUCT >::Bytes(
                const Common::StreamBase& Stream ) const
            {
                auto frame_stream_version = StreamVersion( Stream );
                return ( sizeof( Data::class_type ) +
                         ( ( frame_stream_version >= 5 ) //
                               ? sizeof( Data::instance_type ) //
                               : sizeof( v3::instance_type ) ) );
            }

            template <>
            Common::PTR_STRUCT_BASE::class_type
            ClassicIO< PTR_STRUCT >::Class( ) const
            {
                return ( Data::Class( ) );
            }

            template <>
            void
            ClassicIO< PTR_STRUCT >::Class(
                Common::PTR_STRUCT_BASE::class_type ClassId )
            {
                Data::Class( ClassId );
            }

            template <>
            Common::FrameSpec::Object*
            ClassicIO< PTR_STRUCT >::Create( istream_type& Stream ) const
            {
                PTR_STRUCT* obj( new PTR_STRUCT( ) );

                auto frame_stream_version = StreamVersion( Stream );
                Stream >> obj->data_class;
                if ( frame_stream_version >= 5 )
                {
                    Stream >> obj->data_instance;
                }
                else if ( frame_stream_version >= 3 )
                {
                    v3::instance_type instance;
                    Stream >> instance;
                    obj->data_instance = instance;
                }
                return ( obj );
            }
        } // namespace PTR_STRUCTImpl

    } // namespace Universal
} // namespace FrameCPP

//=======================================================================
// PTR_STTRUCT
//=======================================================================

FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( PTR_STRUCTImpl,
                                      FSI_PTR_STRUCT,
                                      "PTR_STRUCT",
                                      "Pointer to structure" )

namespace FrameCPP
{
    namespace Universal
    {
        //=======================================================================
        // PTR_STRUCT
        //=======================================================================
        PTR_STRUCT::PTR_STRUCT( )
        {
            updateDebugTypeName();
        }

        PTR_STRUCT::PTR_STRUCT( const PTR_STRUCT& Source )
        {
            data_instance = Source.Instance( );
            data_class = Source.Class( );
            updateDebugTypeName();
        }

        PTR_STRUCT::PTR_STRUCT( Data::class_type    ClassId,
                                Data::instance_type InstanceId )
        {
            data_class = ClassId;
            data_instance = InstanceId;
            updateDebugTypeName();
        }

        PTR_STRUCT::~PTR_STRUCT( )
        {
        }

        PTR_STRUCT&
        PTR_STRUCT::Merge( const PTR_STRUCT& RHS )
        {
            /// \todo
            ///   Need to implement Merge routine
            std::string msg( "Merge currently not implemented for " );
            msg += FrameSpecName( );

            throw std::domain_error( msg );
            return *this;
        }

        namespace PTR_STRUCTImpl
        {
            template <>
            const Common::Description*
            ClassicIO< PTR_STRUCT >::GetDescription(
                unsigned int frame_stream_version ) const
            {
                // PTR_STRUCT doesn't have a frame structure description
                // Return empty description
                static Common::Description empty_description;
                return &empty_description;
            }

            template <>
            const Common::Description*
            ClassicIO< PTR_STRUCT >::GetDescription( ) const
            {
                return ( GetDescription( FRAME_SPEC_CURRENT ) );
            }

        } // namespace PTR_STRUCTImpl
#if WORKING || 1
        namespace PTR_STRUCTImpl
        {
            template <>
            bool
            ClassicIO< PTR_STRUCT >::
            operator==( const Common::FrameSpec::Object& Obj ) const
            {
                // Cast *this to PTR_STRUCT so the FrameCPP::compare<T>
                // template deduces T = PTR_STRUCT.
                return FrameCPP::compare(
                    static_cast< const PTR_STRUCT& >( *this ), Obj );
            }
        } // namespace PTR_STRUCTImpl
#endif /* WORKING */
    } // namespace Universal
} // namespace FrameCPP
