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

#ifndef FrameCPP__COMMON__StreamRef_HH
#define FrameCPP__COMMON__StreamRef_HH

#include "ldastoolsal/types.hh"
#include "ldastoolsal/reverse.hh"

#include "framecpp/Common/FrameSpec.hh"
#include "framecpp/Common/FrameStream.hh"
#include "framecpp/Common/StringStream.hh"
#include "framecpp/Common/IOStream.hh"
#include "framecpp/Common/StreamRefInterface.hh"

namespace FrameCPP
{
    namespace Common
    {
        template < class LT, class CT, class IT, int SPEC_VERSION_T >
        class StreamRef : public StreamRefInterface
        {
        public:
            typedef StreamRefInterface::length_type   length_type;
            typedef StreamRefInterface::class_type    class_type;
            typedef StreamRefInterface::instance_type instance_type;

            /// \brief Constructor
            StreamRef( );

            /// \brief Destructor
            virtual ~StreamRef( );

            virtual FrameSpec::size_type SizeOf( ) const;

            FrameSpec::size_type Bytes( const StreamBase& Stream ) const;

            virtual class_type GetClass( ) const;

            virtual instance_type GetInstance( ) const;

            virtual length_type GetLength( ) const;

            virtual void SetClass( class_type Class );

            virtual void SetInstance( instance_type Instance );

            virtual void SetLength( length_type Length );

            virtual StreamRef< LT, CT, IT, SPEC_VERSION_T >*
            Create( IStream& Stream ) const;

            virtual StreamRef< LT, CT, IT, SPEC_VERSION_T >* Clone( ) const;

            virtual const char* ObjectStructName( ) const;

            virtual void Write( OStream& Stream ) const;

            /// \brief comparison operator
            virtual bool operator==(
                const StreamRef< LT, CT, IT, SPEC_VERSION_T >& RHS ) const;

            /// \brief comparison operator
            virtual bool
            operator==( const Common::FrameSpec::Object& Obj ) const;

        protected:
            virtual void assign( IStringStream& Stream );

            /// \brief Down grade an object
            virtual FrameSpec::ObjectInterface::demote_ret_type
            demote( INT_2U              Target,
                    demote_arg_type     Obj,
                    demote_stream_type* Stream ) const;

            /// \brief Upgrade an object
            virtual FrameSpec::ObjectInterface::promote_ret_type
            promote( INT_2U                                       Target,
                     FrameSpec::ObjectInterface::promote_arg_type Obj,
                     promote_stream_type* Stream ) const;

        private:
            using Object::Create;

            typedef LT internal_length_type;
            typedef CT internal_class_type;
            typedef IT internal_instance_type;

            internal_length_type   m_length;
            internal_class_type    m_class;
            internal_instance_type m_instance;

            StreamRef( IStream& Stream );
            StreamRef( const StreamRef& Source );

        }; // class - StreamRef

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::StreamRef( )
            : StreamRefInterface( SPEC_VERSION_T ), m_length( 0 ), m_class( 0 ),
              m_instance( 0 )
        {
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::StreamRef( IStream& Stream )
            : StreamRefInterface( SPEC_VERSION_T ), m_length( 0 ), m_class( 0 ),
              m_instance( 0 )
        {
            Stream >> m_length >> m_class >> m_instance;
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::StreamRef(
            const StreamRef& Source )
            : StreamRefInterface( Source ), m_length( Source.m_length ),
              m_class( Source.m_class ), m_instance( Source.m_instance )
        {
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::~StreamRef( )
        {
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline cmn_streamsize_type
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::SizeOf( ) const
        {
            return sizeof( m_length ) + sizeof( m_class ) +
                sizeof( m_instance );
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        cmn_streamsize_type
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::Bytes(
            const StreamBase& Stream ) const
        {
            cmn_streamsize_type retval = 0;
            auto                frame_stream_version = StreamVersion( Stream );

            if ( frame_stream_version >= 5 )
            {
                retval = SizeOf( );
            }
            else if ( frame_stream_version >= 3 )
            {
                retval = //
                    sizeof( INT_4U ) // length
                    + sizeof( INT_2U ) // class
                    + sizeof( INT_2U ) // instance
                    ;
            }
            return ( retval );
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline typename StreamRef< LT, CT, IT, SPEC_VERSION_T >::class_type
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::GetClass( ) const
        {
            return m_class;
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline typename StreamRef< LT, CT, IT, SPEC_VERSION_T >::instance_type
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::GetInstance( ) const
        {
            return m_instance;
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline typename StreamRef< LT, CT, IT, SPEC_VERSION_T >::length_type
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::GetLength( ) const
        {
            return m_length;
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline void
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::SetClass( class_type Class )
        {
            m_class = Class;
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline void
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::SetInstance(
            instance_type Instance )
        {
            m_instance = Instance;
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline void
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::SetLength( length_type Length )
        {
            m_length = Length;
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline StreamRef< LT, CT, IT, SPEC_VERSION_T >*
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::Create( IStream& Stream ) const
        {
            return new StreamRef( Stream );
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline StreamRef< LT, CT, IT, SPEC_VERSION_T >*
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::Clone( ) const
        {
            return new StreamRef( *this );
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline const char*
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::ObjectStructName( ) const
        {
            const char* const name = "StreamRef";
            return name;
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline void
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::Write( OStream& Stream ) const
        {
            auto frame_stream_version = StreamVersion( Stream );

            if ( frame_stream_version >= 5 )
            {
                Stream << m_length << m_class << m_instance;
            }
            else if ( frame_stream_version >= 3 )
            {
              Stream << INT_4U( m_length ) << INT_2U( m_class ) << INT_2U( m_instance );
            }
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline bool
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::
        operator==( const StreamRef< LT, CT, IT, SPEC_VERSION_T >& RHS ) const
        {
            return ( ( m_length == RHS.m_length ) &&
                     ( m_class == RHS.m_class ) &&
                     ( m_instance == RHS.m_instance ) );
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline bool
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::
        operator==( const Common::FrameSpec::Object& Obj ) const
        {
            typedef StreamRef< LT, CT, IT, SPEC_VERSION_T > data_type;

            try
            {
                const data_type& robj =
                    reinterpret_cast< const data_type& >( Obj );
                return ( *this == robj );
            }
            catch ( ... )
            {
            }
            return ( false );
        }

        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        inline void
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::assign( IStringStream& Stream )
        {
            Stream >> m_length >> m_class >> m_instance;
        }

        /// \brief Down grade an object
        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        FrameSpec::ObjectInterface::demote_ret_type
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::demote(
            INT_2U              Target,
            demote_arg_type     Obj,
            demote_stream_type* Stream ) const
        {
            throw Unimplemented(
                "Object* StreamRef::demote( Object* Obj ) const",
                0,
                __FILE__,
                __LINE__ );
        }

        /// \brief Upgrade an object
        template < typename LT, typename CT, typename IT, int SPEC_VERSION_T >
        FrameSpec::ObjectInterface::promote_ret_type
        StreamRef< LT, CT, IT, SPEC_VERSION_T >::promote(
            INT_2U                                       Target,
            FrameSpec::ObjectInterface::promote_arg_type Obj,
            promote_stream_type*                         Stream ) const
        {
            throw Unimplemented(
                "Object* StreamRef::promote( Object* Obj ) const",
                0,
                __FILE__,
                __LINE__ );
        }

        //-------------------------------------------------------------------
        /// \brief Generalized common elements of frame structure.
        ///
        /// This templated implementation simplifies the actual
        /// implementation of the common elements of all frame structures
        /// by having each of the storage elements have a separate
        /// template parameter.
        ///
        /// This implementation supports versions of the frame
        /// specification which require the chkType field.
        ///
        /// \tparam LT
        ///     Data type for the length field.
        /// \tparam CKT
        ///     Data type for the chkType field.
        /// \tparam CT
        ///     Data type for the class field.
        /// \tparam IT
        ///     Data type for the instance field.
        //-------------------------------------------------------------------
        template < class LT, class CKT, class CT, class IT, int SPEC_VERSION_T >
        class StreamRef2 : public StreamRefInterface
        {
        public:
            typedef StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T > self_type;
            //-----------------------------------------------------------------
            /// \brief Constructor
            //-----------------------------------------------------------------
            StreamRef2( );

            //-----------------------------------------------------------------
            /// \brief Destructor
            //-----------------------------------------------------------------
            virtual ~StreamRef2( );

            static FrameSpec::size_type Bytes( );

            FrameSpec::size_type Bytes( const StreamBase& Stream ) const;

            virtual FrameSpec::size_type SizeOf( ) const;

            virtual INT_2U GetChkType( ) const;

            virtual class_type GetClass( ) const;

            virtual instance_type GetInstance( ) const;

            virtual length_type GetLength( ) const;

            virtual void SetChkType( INT_2U ChkType );

            virtual void SetClass( class_type Class );

            virtual void SetInstance( instance_type Instance );

            virtual void SetLength( length_type Length );

            virtual self_type* Create( IStream& Stream ) const;

            virtual self_type* Clone( ) const;

            virtual const char* ObjectStructName( ) const;

            virtual void Write( OStream& Stream ) const;

            /// \brief comparison operator
            virtual bool operator==(
                const StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >& RHS )
                const;

            /// \brief comparison operator
            virtual bool
            operator==( const Common::FrameSpec::Object& Obj ) const;

        protected:
            virtual void assign( IStringStream& Stream );

            /// \brief Down grade an object
            virtual demote_ret_type demote( INT_2U              Target,
                                            demote_arg_type     Obj,
                                            demote_stream_type* Stream ) const;

            /// \brief Upgrade an object
            virtual promote_ret_type
            promote( INT_2U               Target,
                     promote_arg_type     Obj,
                     promote_stream_type* Stream ) const;

        private:
            using Object::Create;

            typedef LT  internal_length_type;
            typedef CKT internal_chkType_type;
            typedef CT  internal_class_type;
            typedef IT  internal_instance_type;

            internal_length_type   m_length;
            internal_chkType_type  m_chkType;
            internal_class_type    m_class;
            internal_instance_type m_instance;

            StreamRef2( IStream& Stream );
            StreamRef2( const StreamRef2& Source );

        }; // class - StreamRef2

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::StreamRef2( )
            : StreamRefInterface( SPEC_VERSION_T ), m_length( 0 ),
              m_chkType( 0 ), m_class( 0 ), m_instance( 0 )
        {
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::StreamRef2(
            IStream& Stream )
            : StreamRefInterface( SPEC_VERSION_T ), m_length( 0 ),
              m_chkType( 0 ), m_class( 0 ), m_instance( 0 )
        {
            Stream >> m_length >> m_chkType >> m_class >> m_instance;

            if ( m_chkType )
            {
                char buffer[ sizeof( LT ) + sizeof( CKT ) + sizeof( CT ) +
                             sizeof( IT ) ];

                {
                    char* pos = buffer;

#define COPY( var )                                                            \
    {                                                                          \
        char* p( reinterpret_cast< char* >( &var ) );                          \
                                                                               \
        std::copy( p, p + sizeof( var ), pos );                                \
        pos += sizeof( var );                                                  \
    }

                    COPY( m_length );
                    COPY( m_chkType );
                    COPY( m_class );
                    COPY( m_instance );
#undef COPY
                }

                if ( Stream.ByteSwapping( ) )
                {
#if LM_DEBUG_INPUT
                    std::cerr << "DEBUG: Read StreamRef2:"
                              << " swapping bytes"
                              << " [ " << __LINE__ << ": " << __FILE__ << " ]"
                              << std::endl;
#endif /* LM_DEBUG_INPUT */
                    char* pos = buffer;

                    reverse< sizeof( m_length ) >( pos, 1 );
                    pos += sizeof( m_length );

                    reverse< sizeof( m_chkType ) >( pos, 1 );
                    pos += sizeof( m_chkType );

                    reverse< sizeof( m_class ) >( pos, 1 );
                    pos += sizeof( m_class );

                    reverse< sizeof( m_instance ) >( pos, 1 );
                }

                Stream.SetCheckSumObject(
                    (const CheckSum::kind_type)m_chkType );
                Stream.GetCheckSumObject( )->Filter( &( buffer[ 0 ] ),
                                                     sizeof( buffer ) );

#if LM_DEBUG_INPUT
                std::cerr << "DEBUG: Read StreamRef2:"
                          << " size: " << sizeof( buffer ) << std::endl
#endif /* LM_DEBUG_INPUT */
                    ;
            }
#if LM_DEBUG_INPUT
            std::cerr << "DEBUG: Read StreamRef2:"
                      << " m_length:" << m_length
                      << " m_chkType: " << (int)m_chkType
                      << " m_class: " << (int)m_class
                      << " m_instance: " << m_instance << " [ " << __LINE__
                      << ": " << __FILE__ " ]" << std::endl;
#endif /* LM_DEBUG_INPUT */
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::StreamRef2(
            const StreamRef2& Source )
            : Common::StreamRefInterface( Source ), m_length( Source.m_length ),
              m_chkType( Source.m_chkType ), m_class( Source.m_class ),
              m_instance( Source.m_instance )
        {
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::~StreamRef2( )
        {
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline cmn_streamsize_type
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::Bytes( )
        {
            return sizeof( LT ) + sizeof( CKT ) + sizeof( CT ) + sizeof( IT );
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        cmn_streamsize_type
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::Bytes(
            const StreamBase& Stream ) const
        {
            cmn_streamsize_type retval = 0;

            auto frame_stream_version = StreamVersion( Stream );

            if ( frame_stream_version >= 8 )
            {
                retval = Bytes( );
            }
            else if ( frame_stream_version >= 5 )
            {
                retval = //
                    sizeof( INT_8U ) // length
                    + sizeof( INT_2U ) // class
                    + sizeof( INT_4U ) // instance
                    ;
            }
            else if ( frame_stream_version >= 3 )
            {
                retval = //
                    sizeof( INT_4U ) // length
                    + sizeof( INT_2U ) // class
                    + sizeof( INT_2U ) // instance
                    ;
            }
            return ( retval );
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        FrameSpec::size_type
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::SizeOf( ) const
        {
            return Bytes( );
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline
            typename StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::class_type
            StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::GetChkType( ) const
        {
            return m_chkType;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline
            typename StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::class_type
            StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::GetClass( ) const
        {
            return m_class;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline typename StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::
            instance_type
            StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::GetInstance( ) const
        {
            return m_instance;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline
            typename StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::length_type
            StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::GetLength( ) const
        {
            return m_length;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline void
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::assign(
            IStringStream& Stream )
        {
      Stream
	>> m_length
	>> m_chkType
	>> m_class
	>> m_instance
	;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline void
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::SetChkType(
            INT_2U ChkType )
        {
            m_chkType = ChkType;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline void
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::SetClass(
            class_type Class )
        {
            m_class = Class;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline void
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::SetInstance(
            instance_type Instance )
        {
            m_instance = Instance;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline void
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::SetLength(
            length_type Length )
        {
            m_length = Length;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >*
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::Create(
            IStream& Stream ) const
        {
            return new StreamRef2( Stream );
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >*
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::Clone( ) const
        {
            return new StreamRef2( *this );
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline const char*
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::ObjectStructName( ) const
        {
            const char* const name = "StreamRef2";
            return name;
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline void
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::Write(
            OStream& Stream ) const
        {
            auto frame_stream_version = StreamVersion( Stream );

            if ( frame_stream_version >= 8 )
            {
                // CRITICAL: Initialize chkType from stream's checksum object if available
                // This is the authoritative source for v8+ files
                CKT chkType = CheckSum::NONE;

                if ( Stream.GetCheckSumObject( ) )
                {
                    chkType = Stream.GetCheckSumObject( )->Type( );
                }
                else if ( m_chkType == CheckSum::NONE || m_chkType == CheckSum::CRC )
                {
                    // Only use m_chkType if it's valid and stream doesn't have a checksum object
                    chkType = m_chkType;
                }

                // Final validation: Ensure chkType is valid before writing
                if ( chkType != CheckSum::NONE && chkType != CheckSum::CRC )
                {
                    // Invalid value - use NONE to prevent stream corruption
                    chkType = CheckSum::NONE;
                }

                Stream << m_length << chkType << m_class << m_instance;
            }
            else if ( frame_stream_version >= 5 )
            {
              Stream << INT_8U( m_length ) << INT_2U( m_class ) << INT_4U( m_instance );
            }
            else if ( frame_stream_version >= 3 )
            {
                Stream << INT_4U( m_length ) << INT_2U( m_class )
                       << INT_2U( m_instance );
            }
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline bool
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::operator==(
            const StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >& RHS ) const
        {
            return ( ( m_length == RHS.m_length ) &&
                     ( m_class == RHS.m_class ) &&
                     ( m_instance == RHS.m_instance ) );
        }

        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        inline bool
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::
        operator==( const Common::FrameSpec::Object& Obj ) const
        {
            typedef StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T > data_type;

            try
            {
                const data_type& robj =
                    reinterpret_cast< const data_type& >( Obj );
                return ( *this == robj );
            }
            catch ( ... )
            {
            }
            return ( false );
        }

        /// \brief Down grade an object
        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        typename StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::demote_ret_type
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::demote(
            INT_2U              Target,
            demote_arg_type     Obj,
            demote_stream_type* Stream ) const
        {
            throw Unimplemented(
                "Object* StreamRef2::demote( Object* Obj ) const",
                0,
                __FILE__,
                __LINE__ );
        }

        /// \brief Upgrade an object
        template < typename LT,
                   typename CKT,
                   typename CT,
                   typename IT,
                   int SPEC_VERSION_T >
        typename StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::promote_ret_type
        StreamRef2< LT, CKT, CT, IT, SPEC_VERSION_T >::promote(
            INT_2U               Target,
            promote_arg_type     Obj,
            promote_stream_type* Stream ) const
        {
            throw Unimplemented(
                "Object* StreamRef2::promote( Object* Obj ) const",
                0,
                __FILE__,
                __LINE__ );
        }

    } // namespace Common
} // namespace FrameCPP
#endif /* FrameCPP__COMMON__StreamRef_HH */
