//----------------------------------------------------------------------
/// @file Description.hh
/// @brief Frame structure description management for LIGO/Virgo frame
/// 
/// This file defines the Description class and associated utilities
/// for managing frame structure descriptions (FrSH/FrSE) in the
/// LIGO/Virgo gravitational wave frame format. It provides
/// type-to-string conversions and array representation capabilities
/// for frame data types.
///
/// @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
//----------------------------------------------------------------------

#ifndef FRAMECPP__COMMON__Description_HH
#define FRAMECPP__COMMON__Description_HH

#if !defined( SWIGIMPORTED )
#include <list>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

#include <boost/shared_ptr.hpp>
#endif /* ! defined(SWIGIMPORTED) */

#include "ldastoolsal/types.hh"

#include "framecpp/Common/FrameSpec.hh"

namespace FrameCPP
{
    namespace Common
    {
        class OFrameStream;

        //--------------------------------------------------------------
        /// @brief Exception thrown when frame structure description is
        ///        unknown
        /// 
        /// This exception is thrown when attempting to access a frame
        /// structure description that doesn't exist for the specified
        /// frame specification version.
        //--------------------------------------------------------------
        class UnknownDescription : public std::runtime_error
        {
            inline UnknownDescription( const std::string& FrameStructureName,
                                       INT_4U             FrameSpecVersion )
                : std::runtime_error(
                      generate_msg( FrameStructureName, FrameSpecVersion ) )
            {
            }

        private:
            static std::string
            generate_msg( const std::string& FrameStructureName,
                          INT_4U             FrameSpecVersion )
            {
                std::ostringstream msg;
                msg << "There is no Description for " << FrameStructureName
                    << " for version " << FrameSpecVersion
                    << " of the frame specification" << std::endl;
                return ( msg.str( ) );
            }
        };

        //--------------------------------------------------------------
        /// @brief Frame structure description container and utilities
        /// 
        /// The Description class manages frame structure descriptions
        /// composed of FrSH (Frame Structure Header) and FrSE (Frame
        /// Structure Entry) objects. It provides type-to-string conversion
        /// utilities and array representation capabilities for frame data
        /// types used in gravitational wave data analysis.
        /// 
        /// @note Frame structure descriptions define the layout and
        ///       types of data stored in frame files according to the
        ///       LIGO/Virgo frame specification.
        //--------------------------------------------------------------
        class Description
        {
        public:
            using object_type = boost::shared_ptr< FrameSpec::Object >;
            using container_type = std::list< object_type >;

            //----------------------------------------------------------
            /// @brief Virtual destructor
            //----------------------------------------------------------
            virtual ~Description( );

            //----------------------------------------------------------
            /// @brief Get the name of the frame structure description
            /// @return Reference to the structure name string
            //----------------------------------------------------------
            const std::string& GetName( ) const;

            //----------------------------------------------------------
            /// @brief Write the description to a frame output stream
            /// @param Stream Output stream to write to
            //----------------------------------------------------------
            void Write( OFrameStream& Stream ) const;

            //----------------------------------------------------------
            /// @brief Add a frame object to the description
            /// @param Obj Shared pointer to frame object (FrSH or FrSE)
            //----------------------------------------------------------
            void operator( )( object_type Obj );

            //----------------------------------------------------------
            /// @brief Add a frame object to the description by reference
            /// @param Obj Reference to frame object to clone and add
            //----------------------------------------------------------
            void operator( )( const object_type::element_type& Obj );

            //----------------------------------------------------------
            /// @brief Get the number of objects in the description
            /// @return Number of FrSH/FrSE objects in the description
            //----------------------------------------------------------
            INT_4U size( ) const;

            //----------------------------------------------------------
            /// @brief Get const iterator to beginning of description objects
            /// @return Const iterator pointing to first FrSH/FrSE object
            /// 
            /// Enables range-based for loops and STL algorithms on Description
            /// objects for walking through FrSH (structure headers) and FrSE 
            /// (structure entries) contained in the description.
            //----------------------------------------------------------
            using const_iterator = container_type::const_iterator;
            const_iterator begin( ) const;
            const_iterator end( ) const;

            //----------------------------------------------------------
            /// @brief Get ASCII string representation of a data type
            /// @tparam T Data type to convert
            /// @return C-string representation of the type
            /// 
            /// Template specializations provide string representations
            /// for frame data types (INT_2S, INT_4U, REAL_8, etc.) and
            /// their pointer variants with left-side asterisk notation.
            /// 
            /// @note Specialized for all LDAS data types and pointers
            //----------------------------------------------------------
            template < typename T >
            static const char* Ascii( );

            //----------------------------------------------------------
            /// @brief Create ASCII array representation with 1 dimension
            /// @tparam T Element data type
            /// @param dim1 Size of first dimension as string
            /// @return String representation like "INT_4S[10]"
            //----------------------------------------------------------
            template < typename T >
            static std::string AsciiArray( const std::string& dim1 );

            //----------------------------------------------------------
            /// @brief Create ASCII array representation with 2 dimensions
            /// @tparam T Element data type
            /// @param dim1 Size of first dimension as string
            /// @param dim2 Size of second dimension as string
            /// @return String representation like "REAL_8[10][20]"
            //----------------------------------------------------------
            template < typename T >
            static std::string AsciiArray( const std::string& dim1, const std::string& dim2 );

            //----------------------------------------------------------
            /// @brief Create ASCII array representation with 3 dimensions
            /// @tparam T Element data type
            /// @param dim1 Size of first dimension as string
            /// @param dim2 Size of second dimension as string
            /// @param dim3 Size of third dimension as string
            /// @return String representation like "INT_2U[5][10][15]"
            //----------------------------------------------------------
            template < typename T >
            static std::string AsciiArray( const std::string& dim1, const std::string& dim2, const std::string& dim3 );

            //----------------------------------------------------------
            /// @brief Create ASCII array representation with N dimensions
            /// @tparam T Element data type
            /// @param dimensions Vector of dimension sizes as strings
            /// @return String representation with arbitrary dimensions
            /// 
            /// @example
            /// std::vector<std::string> dims = {"10", "20", "30", "40"};
            /// auto result = AsciiArray<INT_4S>(dims); // "INT_4S[10][20][30][40]"
            //----------------------------------------------------------
            template < typename T >
            static std::string AsciiArray( const std::vector<std::string>& dimensions );

        private:
            container_type m_desc;
        };

        //--------------------------------------------------------------
        /// @name Basic Data Type Specializations
        /// @brief Ascii template specializations for fundamental types
        /// 
        /// These specializations provide string representations for the
        /// standard LDAS data types used in gravitational wave frames.
        //--------------------------------------------------------------
        ///@{
        
        /// @brief Specialization for signed 16-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_2S >( )
        {
            return ( "INT_2S" );
        }

        /// @brief Specialization for unsigned 16-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_2U >( )
        {
            return ( "INT_2U" );
        }

        /// @brief Specialization for signed 32-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_4S >( )
        {
            return ( "INT_4S" );
        }

        /// @brief Specialization for unsigned 32-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_4U >( )
        {
            return ( "INT_4U" );
        }

        /// @brief Specialization for signed 64-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_8S >( )
        {
            return ( "INT_8S" );
        }

        /// @brief Specialization for unsigned 64-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_8U >( )
        {
            return ( "INT_8U" );
        }

        /// @brief Specialization for 32-bit floating point
        template <>
        inline const char*
        Common::Description::Ascii< REAL_4 >( )
        {
            return ( "REAL_4" );
        }

        /// @brief Specialization for 64-bit floating point
        template <>
        inline const char*
        Common::Description::Ascii< REAL_8 >( )
        {
            return ( "REAL_8" );
        }

        /// @brief Specialization for unsigned character
        template <>
        inline const char*
        Common::Description::Ascii< CHAR_U >( )
        {
            return ( "CHAR" );
        }

        /// @brief Specialization for C-style string
        template <>
        inline const char*
        Common::Description::Ascii< const char* >( )
        {
            return ( "STRING" );
        }

        /// @brief Specialization for C++ string
        template <>
        inline const char*
        Common::Description::Ascii< std::string >( )
        {
            return ( "STRING" );
        }
        ///@}

        //--------------------------------------------------------------
        /// @name Pointer Type Specializations
        /// @brief Ascii template specializations for pointer types
        /// 
        /// These specializations provide string representations for
        /// pointer types with left-side asterisk notation (*TYPE).
        /// This notation is used in frame structure descriptions to
        /// indicate pointer data members.
        //--------------------------------------------------------------
        ///@{
        
        /// @brief Specialization for pointer to signed 16-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_2S* >( )
        {
            return ( "*INT_2S" );
        }

        /// @brief Specialization for pointer to unsigned 16-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_2U* >( )
        {
            return ( "*INT_2U" );
        }

        /// @brief Specialization for pointer to signed 32-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_4S* >( )
        {
            return ( "*INT_4S" );
        }

        /// @brief Specialization for pointer to unsigned 32-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_4U* >( )
        {
            return ( "*INT_4U" );
        }

        /// @brief Specialization for pointer to signed 64-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_8S* >( )
        {
            return ( "*INT_8S" );
        }

        /// @brief Specialization for pointer to unsigned 64-bit integer
        template <>
        inline const char*
        Common::Description::Ascii< INT_8U* >( )
        {
            return ( "*INT_8U" );
        }

        /// @brief Specialization for pointer to 32-bit floating point
        template <>
        inline const char*
        Common::Description::Ascii< REAL_4* >( )
        {
            return ( "*REAL_4" );
        }

        /// @brief Specialization for pointer to 64-bit floating point
        template <>
        inline const char*
        Common::Description::Ascii< REAL_8* >( )
        {
            return ( "*REAL_8" );
        }

        /// @brief Specialization for pointer to unsigned character
        template <>
        inline const char*
        Common::Description::Ascii< CHAR_U* >( )
        {
            return ( "*CHAR" );
        }

        /// @brief Specialization for pointer to C-style string (char**)
        template <>
        inline const char*
        Common::Description::Ascii< const char** >( )
        {
            return ( "*STRING" );
        }

        /// @brief Specialization for pointer to C++ string
        template <>
        inline const char*
        Common::Description::Ascii< std::string* >( )
        {
            return ( "*STRING" );
        }
        ///@}

        inline INT_4U
        Common::Description::size( ) const
        {
            return m_desc.size( );
        }

        inline Common::Description::const_iterator
        Common::Description::begin( ) const
        {
            return m_desc.begin( );
        }

        inline Common::Description::const_iterator
        Common::Description::end( ) const
        {
            return m_desc.end( );
        }

        //--------------------------------------------------------------
        /// @name Array Template Implementations
        /// @brief Template implementations for array type representations
        /// 
        /// These template implementations generate string representations
        /// for multidimensional arrays with symbolic dimension sizes.
        /// Supports both value and pointer types with flexible
        /// dimension specification.
        //--------------------------------------------------------------
        ///@{
        
        template < typename T >
        inline std::string
        Common::Description::AsciiArray( const std::string& dim1 )
        {
            std::string result = Ascii< T >( );
            result += "[" + dim1 + "]";
            return result;
        }

        template < typename T >
        inline std::string
        Common::Description::AsciiArray( const std::string& dim1, const std::string& dim2 )
        {
            std::string result = Ascii< T >( );
            result += "[" + dim1 + "][" + dim2 + "]";
            return result;
        }

        template < typename T >
        inline std::string
        Common::Description::AsciiArray( const std::string& dim1, const std::string& dim2, const std::string& dim3 )
        {
            std::string result = Ascii< T >( );
            result += "[" + dim1 + "][" + dim2 + "][" + dim3 + "]";
            return result;
        }

        template < typename T >
        inline std::string
        Common::Description::AsciiArray( const std::vector<std::string>& dimensions )
        {
            std::string result = Ascii< T >( );
            for ( const auto& dim : dimensions )
            {
                result += "[" + dim + "]";
            }
            return result;
        }
        ///@}


    } // namespace Common
} // namespace FrameCPP

#endif /* FRAMECPP__COMMON__Description_HH */
