//
// 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__UNIVERSAL__IMPL__GPSTIME_HH
#define FRAMECPP__UNIVERSAL__IMPL__GPSTIME_HH

#if defined( __cplusplus )
#if !defined( SWIGIMPORTED )

#include <iostream>
#include <cmath> // for std::modf

#include "ldastoolsal/types.hh"
#include "ldastoolsal/gpstime.hh"

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

#include "framecpp/Universal/impl/FrameSpec.hh"

namespace FrameCPP
{
    namespace Universal
    {
        using LDASTools::AL::GPSTime;

        inline REAL_8
        ConvertToRealSeconds( INT_4U sec, INT_4U nsec )
        {
            return static_cast< REAL_8 >( sec ) +
                static_cast< REAL_8 >( nsec ) / 1e9;
        }

        inline void
        ConvertToSecAndNsec( REAL_8 time_in_seconds, INT_4U& sec, INT_4U& nsec )
        {
            // Separate the integral and fractional parts
            REAL_8 fractional_part =
                std::modf( time_in_seconds, &time_in_seconds );

            sec = static_cast< INT_4U >( time_in_seconds );
            nsec = static_cast< INT_4U >( std::round( fractional_part * 1e9 ) );

            // Handle rounding that might push nanoseconds to 1 second
            if ( nsec == 1000000000 )
            {
                ++sec;
                nsec = 0;
            }
        }

        // Input operator adapter inline
        inline istream_type&
        operator>>( istream_type& Stream, GPSTime& gpsTime )
        {
            auto frame_stream_version = StreamVersion( Stream );

            if ( frame_stream_version >= 3 )
            {
                GPSTime::seconds_type     seconds;
                GPSTime::nanoseconds_type nanoseconds;

                Stream >> seconds >> nanoseconds;

                gpsTime = GPSTime( seconds, nanoseconds );
            }
            return ( Stream );
        }

        // Output operator adapter
        inline ostream_type&
        operator<<( ostream_type& Stream, const GPSTime& gpsTime )
        {
            auto frame_stream_version = StreamVersion( Stream );

            if ( frame_stream_version >= 3 )
            {
                Stream << gpsTime.GetSeconds( ) << gpsTime.GetNanoseconds( );
            }
            return ( Stream );
        }
    } // namespace Universal
} // namespace FrameCPP

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

#endif /* FRAMECPP__UNIVERSAL__IMPL__GPSTime_HH */
