//
// 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 <boost/pointer_cast.hpp>

#include "framecpp/Common/FrameStream.hh"
#include "framecpp/Common/FrStatData.hh"

namespace FrameCPP
{
    namespace Common
    {
        //===================================================================
        // FrStatData::Query
        //===================================================================
        FrStatData::Query::~Query( )
        {
        }

        void
        FrStatData::Query::Add( const std::string&            NamePattern,
                                const LDASTools::AL::GPSTime& StartTime,
                                const LDASTools::AL::GPSTime& EndTime,
                                const INT_4U                  Version )
        {
            //---------------------------------------------------------------------
            /// \todo
            ///   Verify mode to be table of contents
            //---------------------------------------------------------------------
            const FrTOC* toc(
                dynamic_cast< const FrTOC* >( m_stream->GetTOC( ) ) );
            if ( toc )
            {
                toc->FrStatDataQuery(
                    NamePattern, StartTime, EndTime, Version, *this );
            }
            //---------------------------------------------------------------------
            // Read into memory anything that has been requested
            //---------------------------------------------------------------------
            load( );
        }

        void
        FrStatData::Query::Reset( stream_type& Stream )
        {
            m_fr_stat_data.resize( 0 );
            m_stream = &Stream;
            m_dirty = false;
        }

        FrStatData::Query::fr_stat_data_type FrStatData::Query::
                                             operator[]( INT_4U Index )
        {
            if ( Index >= size( ) )
            {
                throw std::range_error(
                    "Index out of range for FrStatData::Query" );
            }
            return m_fr_stat_data[ Index ].s_fr_stat_data;
        }

        void
        FrStatData::Query::Add( const INT_4U       QueryStartTime,
                                const INT_4U       QueryEndTime,
                                const INT_4U       QueryVersion,
                                const std::string& Name,
                                const INT_4U       Start,
                                const INT_4U       End,
                                const INT_4U       Version,
                                const INT_8U       Position,
                                const std::string& Detector )
        {
            if ( // Verify Time range match
                ( ( ( QueryStartTime == 0 ) && ( QueryEndTime == 0 ) ) ||
                  ( ( QueryStartTime >= Start ) && ( QueryStartTime < End ) ) ||
                  ( ( QueryEndTime >= Start ) && ( QueryEndTime < End ) ) ||
                  ( ( QueryStartTime < Start ) &&
                    ( QueryEndTime >= Start ) ) ) &&
                // Verify Version
                ( ( QueryVersion == FrStatData::Query::ALL_VERSIONS ) ||
                  ( QueryVersion == FrStatData::Query::LATEST_VERSION ) ||
                  ( QueryVersion == Version ) ) )
            {
                if ( QueryVersion == FrStatData::Query::LATEST_VERSION )
                {
                    bool found = false;
                    for ( data_type::iterator
                              cur_data = m_fr_stat_data.begin( ),
                              last_data = m_fr_stat_data.end( );
                          cur_data != last_data;
                          ++cur_data )
                    {
                        if ( ( ( *cur_data ).s_nameStat == Name ) &&
                             ( ( *cur_data ).s_tStart == Start ) &&
                             ( ( *cur_data ).s_tEnd == End ) )
                        {
                            if ( ( *cur_data ).s_version < Version )
                            {
                                ( *cur_data ).s_fr_stat_pos = Position;
                                ( *cur_data ).s_fr_stat_data.reset( );
                                ( *cur_data ).s_version = Version;
                                m_dirty = true;
                            }
                            found = true;
                            break;
                        }
                    }
                    if ( found == false )
                    {
                        m_dirty = true;
                        m_fr_stat_data.push_back( query_info_type( ) );
                        m_fr_stat_data.back( ).s_fr_stat_pos = Position;
                        m_fr_stat_data.back( ).s_fr_stat_data.reset( );
                        m_fr_stat_data.back( ).s_nameStat = Name;
                        m_fr_stat_data.back( ).s_detector = Detector;
                        m_fr_stat_data.back( ).s_tStart = Start;
                        m_fr_stat_data.back( ).s_tEnd = End;
                        m_fr_stat_data.back( ).s_version = Version;
                    }
                }
                else
                {
                    m_dirty = true;
                    m_fr_stat_data.push_back( query_info_type( ) );
                    m_fr_stat_data.back( ).s_fr_stat_pos = Position;
                    m_fr_stat_data.back( ).s_fr_stat_data.reset( );
                    m_fr_stat_data.back( ).s_nameStat = Name;
                    m_fr_stat_data.back( ).s_detector = Detector;
                    m_fr_stat_data.back( ).s_tStart = Start;
                    m_fr_stat_data.back( ).s_tEnd = End;
                    m_fr_stat_data.back( ).s_version = Version;
                }
            }
        }

        void
        FrStatData::Query::load( )
        {
            if ( m_dirty )
            {
                // Sort the list by file position in ascending order
                std::sort( m_fr_stat_data.begin( ),
                           m_fr_stat_data.end( ),
                           CompareFilePosition( ) );

                // Loop over list loading in those that have not yet been read.
                for ( data_type::iterator cur = m_fr_stat_data.begin( ),
                                          last = m_fr_stat_data.end( );
                      cur != last;
                      ++cur )
                {
                    if ( !cur->s_fr_stat_data )
                    {
                        IFrameStream::object_type obj =
                            m_stream->ReadFrStatData( ( *cur ).s_fr_stat_pos );
                        fr_stat_data_type stat_data(
                            boost::dynamic_pointer_cast<
                                fr_stat_data_type::element_type >( obj ) );

                        ( *cur ).s_fr_stat_data = stat_data;
                    }
                }
                m_dirty = false;
            }
        }
    } // namespace Common
} // namespace FrameCPP
