//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2019 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__FrTOCProcDataClassicIO_HH
#define FrameCPP_UNIVERSAL__IMPL__FrTOCProcDataClassicIO_HH

#if defined( __cplusplus )
#if !defined( SWIG )

#include "framecpp/Common/Description.hh"
#include "framecpp/Common/FrameSpec.hh"
#include "framecpp/Common/FrameSpec.tcc"
#include "framecpp/Common/TOCInfo.hh"

#include "framecpp/Universal/impl/FrTOCConstants.hh"
#include "framecpp/Universal/impl/FrTOCProcDataData.hh"

namespace FrameCPP
{
    namespace Universal
    {
        namespace FrTOCImpl
        {
            class FrTOCProcDataClassicIO : public virtual FrTOCProcDataData
            {
            public:
                //-----------------------------------------------------------------
                /// \brief Default constructor.
                //-----------------------------------------------------------------
                FrTOCProcDataClassicIO( ) = default;

                //-----------------------------------------------------------------
                /// \brief Number of bytes needed to write this structure
                ///
                /// \param[in] frame_stream_version
                ///     The frame specification version
                ///     written.
                ///
                /// \return
                ///     The number of bytes need to read or write this object.
                //-----------------------------------------------------------------
                Common::FrameSpec::size_type
                Bytes( const Common::StreamBase& Stream ) const;

                //-----------------------------------------------------------------
                /// \brief Gather TOC info for FrProcData being written.
                ///
                /// \param[in] Info
                ///     Information
                ///
                /// \param[in] FrameOffset
                ///     The frame offset of the frame being written.
                ///     The frame offsets start at 0 (zero).
                ///
                /// \param[in] Position
                ///     The byte offset from the start of the file
                ///     where the structure is written.
                //-----------------------------------------------------------------
                void QueryProc( const Common::TOCInfo& Info,
                                INT_4U                 FrameOffset,
                                INT_8U                 Position );

                inline FrTOCProcDataClassicIO&
                operator=( FrTOCProcDataClassicIO&& TOC );

                //-----------------------------------------------------------------
                /// \brief equality operator
                ///
                /// \param[in] RHS
                ///     The FrTOCProcData object to be compared.
                ///
                /// \return
                ///     The value true is returned if this object is equivelent
                ///     to the RHS instance; false otherwise.
                //-----------------------------------------------------------------
                bool operator==( const FrTOCProcDataClassicIO& RHS ) const;

                //-----------------------------------------------------------------
                /// \brief The description of structure
                ///
                /// \param[in,out] Stream
                ///   Stream to which  the description will be written
                /// \param[out] Desc
                ///     Storage for the description of the structure.
                ///
                /// \return
                ///     A Description object which describes this structure as
                ///     specified by the frame specification.
                //-----------------------------------------------------------------
                void GetDescription( unsigned int         frame_stream_version,
                                     Common::Description& Desc ) const;

                //-----------------------------------------------------------------
                /// \brief Read contents from stream
                ///
                /// \param[in] Stream
                ///     The stream from which the object is being read.
                /// \param[in,out] nframe
                ///     The number of frames. Used by this class.
                ///
                /// \return
                ///    A new instance of this object.
                //-----------------------------------------------------------------
                void Load( istream_type& Stream, INT_4U& nframe );

            protected:
                //---------------------------------------------------------------
                /// \brief TOC optimization
                ///
                /// This method optimizes the TOC reading by only reading
                /// the structure offsets.
                /// Once read, it is moved into the structure.
                //---------------------------------------------------------------
                void cachePositions( istream_type& Stream );

                //-----------------------------------------------------------------
                /// \brief Iterate over contents.
                ///
                /// \param[in] Info
                ///     Specifies the type of information to be searched.
                ///
                /// \param[in] Action
                ///     Action to be taken for each piece of information found.
                //-----------------------------------------------------------------
                void forEach( Common::FrTOC::query_info_type Info,
                              Common::FrTOC::FunctionBase&   Action ) const;

                void setPositionsOffset( istream_type& Stream );

                //-----------------------------------------------------------------
                /// \brief Write the structure to the stream
                ///
                /// \param[in] Stream
                ///     The output stream where the object is to be written.
                //-----------------------------------------------------------------
                void write( Common::OStream& Stream ) const;

            private:
                typedef std::vector< position_type >              info_type;
                typedef typename std::map< name_type, info_type > Map_type;
                typedef typename std::vector< name_type > key_container_type;
                typedef std::vector< position_type >      positions_cache_type;
                typedef INT_4U                            frame_count_type;
                typedef INT_4U                            n_type;

                class input_iterator
                {
                public:
                    input_iterator( Map_type& Destination,
                                    typename info_type::const_iterator Position,
                                    frame_count_type FrameCount );

                    void operator( )( const typename Map_type::key_type& Name );

                private:
                    Map_type&                          destination;
                    typename info_type::const_iterator cur_position;
                    frame_count_type                   frame_count;
                    typename Map_type::iterator        it;
                }; // class input_iterator

                frame_count_type frame_count{ 0 };
                //---------------------------------------------------------------
                /// A cache of the positions of the FrAdcData structures within
                /// the stream.
                //---------------------------------------------------------------
                positions_cache_type positions_cache;
                //---------------------------------------------------------------
                /// The number of bytes from the end of the stream where the
                ///   array containing the FrAdcData offsets.
                //---------------------------------------------------------------
                position_type positions_cache_offset{ 0 };
            }; // class ClassicIO

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

#endif /* ! defined(SWIG) */
#endif /* defined( __cplusplus ) */

#endif /* FrameCPP_UNIVERSAL__IMPL__FrTOCProcDataClassicIO_HH */
