//
// 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 "framecpp/Common/IOStream.hh"
#include "framecpp/Common/STRING.hh"
#include "framecpp/Common/FrameSpec.tcc"
#include "framecpp/Common/Description.hh"

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

#include "framecpp/Universal/FrSH.hh"


using FrameCPP::Common::Description;
using FrameCPP::Common::FrameSpec;
using FrameCPP::Common::IStream;
using FrameCPP::Common::OStream;

FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( FrSHImpl,
                                      FSI_FR_SH,
                                      "FrSH",
                                      "Frame Structure Header" )

namespace FrameCPP
{
    namespace Universal
    {
        FrSH::FrSH( )
        {
            updateDebugTypeName();
        }

        FrSH::FrSH( const name_type& Name,
                    class_type       ClassId,
                    const name_type& Comment )
        {
            Data::name = Name;
            klass = ClassId;
            comment = Comment;
            updateDebugTypeName();
        }

        FrSH::~FrSH( )
        {
        }

#if 0
        //---------------------------------------------------------------
        /// @note
        ///   This is only used by the FrameStream class and as such,
        ///   should be part of FrSHClassicIO
        //---------------------------------------------------------------
        FrSH*
        FrSH::Clone( ) const
        {
            return new FrSH( *this );
        }
#endif /* 0 */

#if NEEDED
        FrameSpec::Object*
        FrSH::Demote( Object* Obj, IStream* Stream ) const
        {
            if ( dynamic_cast< FrSH* >( Obj ) != (FrSH*)NULL )
            {
                return Obj;
            }
            throw Unimplemented( "Object* FrSH::Demote( Object* Obj ) const",
                                 DATA_FORMAT_VERSION,
                                 __FILE__,
                                 __LINE__ );
        }

        FrameSpec::Object*
        FrSH::Promote( Object* Obj, IStream* Stream ) const
        {
            if ( dynamic_cast< FrSH* >( Obj ) != (FrSH*)NULL )
            {
                return Obj;
            }
            throw Unimplemented( "Object* FrSH::Promote( Object* Obj ) const",
                                 DATA_FORMAT_VERSION,
                                 __FILE__,
                                 __LINE__ );
        }
#endif /* NEEDED */

#if 0
        const char*
        FrSH::ObjectStructName( ) const
        {
            return StructName( );
        }
#endif /* 0 */

        bool
        FrSH::operator==( const Common::FrameSpec::Object& Obj ) const
        {
            // FrameCPP::compare() is not usable here because FrSH inherits
            // two distinct GetClass() members — one from FrameSpec::Object
            // (runtime class id) and one from FrSHImpl::Data (frame-spec
            // class number) — and the generic helper template calls
            // LHS.GetClass() unqualified.
            if ( this == &Obj )
            {
                return true;
            }
            const FrSH* rhs = dynamic_cast< const FrSH* >( &Obj );
            return ( rhs != nullptr &&
                     static_cast< const FrSHImpl::Data& >( *this ) ==
                         static_cast< const FrSHImpl::Data& >( *rhs ) );
        }

        void
        FrSH::
#if WORKING_VIRTUAL_TOCQUERY
            TOCQuery( int InfoClass, ... ) const
#else /*  WORKING_VIRTUAL_TOCQUERY */
            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:
                {
                    Common::STRING< INT_2U >* data =
                        va_arg( vl, Common::STRING< INT_2U >* );
                    *data = GetName( );
                }
                break;
                case TOCInfo::DT_INT_2U:
                {
                    INT_2U* data = va_arg( vl, INT_2U* );
                    *data = Data::GetClass( );
                }
                break;
                default:
                    // Stop processing
                    InfoClass = TOCInfo::IC_EOQ;
                    continue;
                }
                InfoClass = va_arg( vl, int );
            }
#if WORKING_VIRTUAL_TOCQUERY
            va_end( vl )
#endif /*  WORKING_VIRTUAL_TOCQUERY */
                ;
        }

#if 0
        void
        FrSH::assign( assign_stream_type& Stream )
        {
            Stream >> m_data.name >> m_data.classId >> m_data.comment;
        }

        INT_8U
        FrSH::pBytes( const Common::StreamBase& Stream ) const
        {
            return m_data.name.Bytes( ) + sizeof( m_data.classId ) +
                m_data.comment.Bytes( );
        }

        FrSH*
        FrSH::pCreate( Common::IStream& Stream ) const
        {
            return new FrSH( Stream );
        }

        void
        FrSH::pWrite( Common::OStream& Stream ) const
        {
#ifdef FRAMECPP_DEBUG
            std::cerr << "FrSH::pWrite: name" << m_data.name << " classId: " << m_data.classId << std::endl;
#endif /* FRAMECPP_DEBUG */
            Stream << m_data.name << m_data.classId << m_data.comment;
        }

        const std::string&
        FrSH::name( ) const
        {
            return GetName( );
        }

        INT_2U
        FrSH::classId( ) const
        {
            return GetClass( );
        }

        FrSH::demote_ret_type
        FrSH::demote( INT_2U           Target,
                      demote_arg_type  Obj,
                      Common::IStream* Stream ) const
        {
            throw Unimplemented( "Object* FrSH::demote( Object* Obj ) const",
                                 DATA_FORMAT_VERSION,
                                 __FILE__,
                                 __LINE__ );
        }

        FrSH::promote_ret_type
        FrSH::promote( INT_2U           Target,
                       promote_arg_type Obj,
                       Common::IStream* Stream ) const
        {
            throw Unimplemented( "Object* FrSH::promote( Object* Obj ) const",
                                 DATA_FORMAT_VERSION,
                                 __FILE__,
                                 __LINE__ );
        }
#endif /* 0 */

        const Description*
        FrSH::GetDescription( ) const
        {
            // FrSH is a meta-description class that doesn't have its own
            // description Return empty description
            static Description empty_description;
            return &empty_description;
        }

        const Description*
        FrSH::GetDescription( unsigned int version ) const
        {
            // FrSH is a meta-description class that doesn't have its own
            // description Return empty description
            static Description empty_description;
            return &empty_description;
        }

    } // namespace Universal
} // namespace FrameCPP
