//
// LDASTools - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2020 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_UNIVERSAL__IMPL__FrTOCSimEventData_HH
#define FrameCPP_UNIVERSAL__IMPL__FrTOCSimEventData_HH

#include "ldastoolsal/Logger.hh"

// Forward declaration to avoid circular dependencies
namespace FrameCPP {
namespace Common {
    struct FrameCPPServices;
}
}

namespace FrameCPP
{
    namespace Universal
    {
        namespace FrTOCImpl
        {
            class FrTOCSimEventData
            {
            public:
                struct v4
                {
                    using gtime_type = LDASTools::AL::GPSTime;
                    using nSimEvt_type = INT_4U;
                    using name_type = std::string;
                    using nTotalSEvent_type = INT_4U;
                    using gtimesSim_type = INT_4U;
                    using gtimenSim_type = INT_4U;
                    using positionSimEvent_type = INT_8U;
                };
                struct v5
                {
                    using gtime_type = v4::gtime_type;
                    using nSimEvent_type = v4::nSimEvt_type;
                    using name_type = v4::name_type;
                    using nTotalSEvent_type = v4::nTotalSEvent_type;
                    using gtimesSim_type = v4::gtimesSim_type;
                    using gtimenSim_type = v4::gtimenSim_type;
                    using amplitudeSimEvent_type = REAL_4;
                    using positionSimEvent_type = v4::positionSimEvent_type;
                };
                using v6 = v5;
                using v7 = v6;
                struct v8
                {
                    using gtime_type = v7::gtime_type;
                    using nSimEvent_type = v7::nSimEvent_type;
                    using name_type = v7::name_type;
                    using nTotalSEvent_type = INT_4U;
                    using gtimesSim_type = v7::gtimesSim_type;
                    using gtimenSim_type = v7::gtimenSim_type;
                    using amplitudeSimEvent_type = v7::amplitudeSimEvent_type;
                    using positionSimEvent_type = v7::positionSimEvent_type;

                    struct event_type
                    {
                        //---------------------------------------------------------------
                        /// \brief Start time of event.
                        //---------------------------------------------------------------
                        gtime_type GTime;
                        //---------------------------------------------------------------
                        /// \brief Amplitude of event.
                        //---------------------------------------------------------------
                        amplitudeSimEvent_type amplitudeSimEvent;
                        //---------------------------------------------------------------
                        /// \brief FrSimEvent offset positions.
                        ///
                        /// FrSimEvent offset positions, in bytes,
                        /// from the beginning of the file.
                        //---------------------------------------------------------------
                        positionSimEvent_type positionSimEvent;
                    };
                };
                using v9 = v8;
                // ------------------------------------------------------
                using current_version = v9;

                using gtime_type = current_version::gtime_type;
                using nSimEvent_type = current_version::nSimEvent_type;
                using name_type = current_version::name_type;
                using nTotalSEvent_type = current_version::nTotalSEvent_type;
                using gtimesSim_type = current_version::gtimesSim_type;
                using gtimenSim_type = current_version::gtimenSim_type;
                using amplitudeSimEvent_type =
                    current_version::amplitudeSimEvent_type;
                using positionSimEvent_type =
                    current_version::positionSimEvent_type;

                using event_type = current_version::event_type;
                // ------------------------------------------------------
                //  For Backwards compatibility
                // ------------------------------------------------------
                using nevent_type = nSimEvent_type;

                // ------------------------------------------------------

                static constexpr int event_type_size =
                    sizeof( gtimesSim_type ) + sizeof( gtimenSim_type ) +
                    sizeof( amplitudeSimEvent_type ) +
                    sizeof( positionSimEvent_type );

                typedef std::vector< event_type > events_container_type;
                /* typedef std::vector< event_container_type >
                 * events_container_type; */
                typedef std::map< name_type, events_container_type >
                    nameSimEvent_container_type;

                constexpr static INT_4U NO_DATA_AVAILABLE = 0xFFFFFFFF;

                //-----------------------------------------------------------------
                /// \brief Return all events
                ///
                /// \return
                ///     Constant container to the indexed FrSimEvent elements.
                //-----------------------------------------------------------------
                const nameSimEvent_container_type&
                GetSimEvent( ) const
                {
                    return m_info;
                }

            protected:
                /// @brief Get the logger for FrTOCSimEvent debugging (static version)
                /// @return Static logger instance (thread-safe, lazy initialization)
                /// Moved to FrTOCSimEvent.cc to avoid ODR violations from function-local statics
                static SimpleLogger* logger();

                /// @brief Get the logger for FrTOCSimEvent debugging (dependency injection version)
                /// @param services Services context containing logger manager
                /// @return Logger instance from services context
                static SimpleLogger* logger(const FrameCPP::Common::FrameCPPServices& services);

                nameSimEvent_container_type m_info;

                //-----------------------------------------------------------------
                /// \brief Locate the position of the event
                ///
                /// \param[in] Frame
                ///     Frame offset of the event.
                /// \param[in] SimEvent
                ///     The name of the event.
                ///
                /// \return
                ///     The file position of the requested event.
                //-----------------------------------------------------------------
                inline positionSimEvent_type
                positionSimEvent( INT_4U             Frame,
                                  const std::string& SimEvent ) const
                {
#if DEBUG
                    std::cerr << "DEBUG: positionSimEvent(" << Frame << ", "
                              << SimEvent << ")" << std::endl;
#endif /* 0 */
                    const nameSimEvent_container_type::const_iterator event(
                        m_info.find( SimEvent ) );
                    //-----------------------------------------------------------------
                    // Locate the event by name
                    //-----------------------------------------------------------------
                    if ( event == m_info.end( ) )
                    {
                        //---------------------------------------------------------------
                        // SimEvent name does not exist.
                        //---------------------------------------------------------------
                        std::ostringstream msg;

                        msg << "No FrSimEvent structures with the name '"
                            << SimEvent << "'";
#if DEBUG
                        std::cerr << "DEBUG: " << msg.str( ) << std::endl;
#endif /* 0 */
                        throw std::out_of_range( msg.str( ) );
                    }
                    //-----------------------------------------------------------------
                    // Verify that the index exists
                    //-----------------------------------------------------------------
                    if ( Frame >= event->second.size( ) )
                    {
                        std::ostringstream msg;

                        msg << "Request for frame " << Frame
                            << " exceeds the range of 0 through "
                            << ( event->second.size( ) - 1 );
#if DEBUG
                        std::cerr << "DEBUG: " << msg.str( ) << std::endl;
#endif /* 0 */
                        throw std::out_of_range( msg.str( ) );
                    }
                    //-----------------------------------------------------------------
                    // Return position information
                    //-----------------------------------------------------------------
#if 0
                    std::cerr << "DEBUG: FrSimEvent file position: "
                              << event->second[ Frame ].positionSimEvent
                              << std::endl;
#endif /* 0 */
                    return event->second[ Frame ].positionSimEvent;
                }

                inline positionSimEvent_type
                positionSimEvent( const std::string& SimEvent,
                                  INT_4U             Index ) const
                {
#if DEBUG
                    std::cerr << "DEBUG: positionSimEvent(" << SimEvent << ", "
                              << Index << ")" << std::endl;
#endif /* 0 */

                    const nameSimEvent_container_type::const_iterator event(
                        m_info.find( SimEvent ) );
                    //-----------------------------------------------------------------
                    // Locate the event by name
                    //-----------------------------------------------------------------
                    if ( event == m_info.end( ) )
                    {
                        //---------------------------------------------------------------
                        // SimEvent name does not exist.
                        //---------------------------------------------------------------
                        std::ostringstream msg;

                        msg << "No FrSimEvent structures with the name '"
                            << SimEvent << "'";
#if DEBUG
                        std::cerr << "DEBUG: " << msg.str( ) << std::endl;
#endif /* 0 */
                        throw std::out_of_range( msg.str( ) );
                    }
                    //-----------------------------------------------------------------
                    // Verify that the index exists
                    //-----------------------------------------------------------------
                    if ( Index >= event->second.size( ) )
                    {
                        std::ostringstream msg;

                        msg << "Request for index " << Index
                            << " exceeds the range of 0 through "
                            << ( event->second.size( ) - 1 );
                        throw std::out_of_range( msg.str( ) );
                    }
                    //-----------------------------------------------------------------
                    // Return position information
                    //-----------------------------------------------------------------
#if DEBUG
                    std::cerr << "DEBUG: FrSimEvent file position: "
                              << event->second[ Index ].positionSimEvent
                              << std::endl;
#endif /* DEBUG */
                    return event->second[ Index ].positionSimEvent;
                }
            };

        } // namespace FrTOCImpl
    } // namespace Universal
} // namespace FrameCPP

#endif /* FrameCPP_UNIVERSAL__IMPL__FrTOCSimEventData_HH */
