//----------------------------------------------------------------------
/// @file Description.cc
/// @brief Implementation of Description class for frame structure
///        management
/// 
/// This file implements the Description class methods for managing
/// frame structure descriptions (FrSH/FrSE) in the LIGO/Virgo
/// gravitational wave frame format. It handles object storage,
/// validation, and output operations for frame descriptions.
///
/// @author LDASTools frameCPP Team
/// @date 2018
//----------------------------------------------------------------------
/// 
/// 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 <sstream>
#include <stdexcept>

#include <boost/pointer_cast.hpp>

#include "framecpp/Common/Description.hh"
#include "framecpp/Common/FrameStream.hh"
#include "framecpp/Common/FrSH.hh"

typedef boost::shared_ptr< FrameCPP::Common::FrSH > fr_sh_type;

namespace FrameCPP
{
    namespace Common
    {
        //----------------------------------------------------------
        /// @brief Destructor - clean up description resources
        //----------------------------------------------------------
        Description::~Description( )
        {
        }

        //----------------------------------------------------------
        /// @brief Get the name of the frame structure from the FrSH
        /// @return Reference to structure name, or "UNKNOWN" if no FrSH
        //----------------------------------------------------------
        const std::string&
        Description::GetName( ) const
        {
            static const std::string unknown( "UNKNOWN" );

            if ( m_desc.empty( ) )
            {
                return unknown;
            }

            fr_sh_type frsh =
                boost::dynamic_pointer_cast< fr_sh_type::element_type >(
                    m_desc.front( ) );
            if ( frsh )
            {
                return frsh->name( );
            }

            return unknown;
        }

        //----------------------------------------------------------
        /// @brief Write all description objects to output stream
        /// @param Stream Output frame stream to write to
        /// 
        /// Iterates through all FrSH/FrSE objects in the description
        /// and writes them to the specified output stream.
        //----------------------------------------------------------
        void
        Description::Write( OFrameStream& Stream ) const
        {
            for ( const auto& element : m_desc )
            {
                if ( element )
                {
                    Stream.Write( element );
                }
            }
        }

        //----------------------------------------------------------
        /// @brief Add frame object to description with validation
        /// @param Obj Shared pointer to frame object to add
        /// @throws std::logic_error If object type is invalid
        /// 
        /// Validates that the first object is FrSH and subsequent
        /// objects are FrSE, maintaining proper description structure.
        //----------------------------------------------------------
        void
        Description::operator( )( object_type Obj )
        {
            if ( ( m_desc.size( ) == 0 ) &&
                 ( Obj->GetClass( ) != FrameSpec::Info::FSI_FR_SH ) )
            {
                std::ostringstream msg;

                msg << "Expected object of class FrSH instead of: "
                    << Obj->GetClass( );
                throw( std::logic_error( msg.str( ) ) );
            }
            else if ( ( m_desc.size( ) > 0 ) &&
                      ( Obj->GetClass( ) != FrameSpec::Info::FSI_FR_SE ) )
            {
                std::ostringstream msg;

                msg << "Expected object of class FrSE instead of: "
                    << Obj->GetClass( );
                throw( std::logic_error( msg.str( ) ) );
            }

            m_desc.push_back( Obj );
        }

        //----------------------------------------------------------
        /// @brief Add frame object by reference with cloning
        /// @param Obj Reference to frame object to clone and add
        /// 
        /// Creates a cloned copy of the provided object and adds it
        /// to the description using the shared pointer version.
        //----------------------------------------------------------
        void
        Description::operator( )( const object_type::element_type& Obj )
        {
            object_type o( Obj.Clone( ) );

            this->operator( )( o );
        }
    } // namespace Common
} // namespace FrameCPP
