# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/01_leapseconds.ipynb (unless otherwise specified).

__all__ = ['logger', 'LeapSeconds', 'gps2utc', 'utc2gps']

# Cell
"""Copyright 2020 The Aerospace Corporation"""

# Cell
import datetime

from .core import GPSTime

from typing import List, Union, Optional, Tuple
from logging import getLogger

logger = getLogger(__name__)

# Cell
class LeapSeconds:
    """Determine the number of leap seconds.

    The purpose of this class is to provide a place to store leap second
    information. It has two methods: one to get the number of leap seconds at
    a given time and one to get the next leap second.

    .. note:: Leap Second Information
        The number of leap seconds is accurate through 31 Dec 2020, which is
        all of the planned leap seconds at the time of writing (30 March 2020).
        There is no leap second planned for 30 Jun 2020, so the next possible
        leap second is 31 Dec 2020 23:59:60. This class will need to be
        updated as future leap seconds are planned.

    """

    _leap_seconds: List[List[Union[GPSTime, int]]] = [
        [GPSTime.from_datetime(datetime.datetime(1981, 6, 30, 23, 59, 59)) + 1, 1],
        [GPSTime.from_datetime(datetime.datetime(1982, 6, 30, 23, 59, 59)) + 1, 2],
        [GPSTime.from_datetime(datetime.datetime(1983, 6, 30, 23, 59, 59)) + 1, 3],
        [GPSTime.from_datetime(datetime.datetime(1985, 6, 30, 23, 59, 59)) + 1, 4],
        [GPSTime.from_datetime(datetime.datetime(1987, 12, 31, 23, 59, 59)) + 1, 5],
        [GPSTime.from_datetime(datetime.datetime(1989, 12, 31, 23, 59, 59)) + 1, 6],
        [GPSTime.from_datetime(datetime.datetime(1990, 12, 31, 23, 59, 59)) + 1, 7],
        [GPSTime.from_datetime(datetime.datetime(1992, 6, 30, 23, 59, 59)) + 1, 8],
        [GPSTime.from_datetime(datetime.datetime(1993, 6, 30, 23, 59, 59)) + 1, 9],
        [GPSTime.from_datetime(datetime.datetime(1994, 6, 30, 23, 59, 59)) + 1, 10],
        [GPSTime.from_datetime(datetime.datetime(1995, 12, 31, 23, 59, 59)) + 1, 11],
        [GPSTime.from_datetime(datetime.datetime(1997, 6, 30, 23, 59, 59)) + 1, 12],
        [GPSTime.from_datetime(datetime.datetime(1998, 12, 31, 23, 59, 59)) + 1, 13],
        [GPSTime.from_datetime(datetime.datetime(2005, 12, 31, 23, 59, 59)) + 1, 14],
        [GPSTime.from_datetime(datetime.datetime(2008, 12, 31, 23, 59, 59)) + 1, 15],
        [GPSTime.from_datetime(datetime.datetime(2012, 6, 30, 23, 59, 59)) + 1, 16],
        [GPSTime.from_datetime(datetime.datetime(2015, 6, 30, 23, 59, 59)) + 1, 17],
        [GPSTime.from_datetime(datetime.datetime(2016, 12, 31, 23, 59, 59)) + 1, 18],
    ]
    """Table of Leap Seconds"""

    @classmethod
    def get_leap_seconds(cls, time: GPSTime) -> int:
        """Get the current number of leap seconds.

        Parameters
        ----------
        time : GPSTime
            The time at which to find the number of leap seconds

        Returns
        -------
        int
            The number of leap seconds at time

        """
        if time > datetime.datetime(2020, 12, 31, 23, 59, 59):
            logger.warning(
                "Leap seconds only current through 31 Dec 2020. Any future "
                "leap seconds not included. Update when available."
            )

        if time < cls._leap_seconds[0][0]:
            return 0
        else:
            for _ls in cls._leap_seconds[::-1]:
                if time > _ls[0]:
                    return _ls[1]

    @classmethod
    def get_next_leap_second(cls, time: GPSTime) -> Optional[Tuple[GPSTime, int]]:
        """Get the next leap second.

        This method is used to compute the next leap second from the given
        time. If the time is after the most recent leap second update and
        there is no planned future leap second, this will return a None
        indicating no planned update.


        Parameters
        ----------
        time : GPSTime
            The time for which the next leap second is desired

        Returns
        -------
        Optional[Tuple[GPSTime, int]]
            A tuple containing the GPSTime of the next leap second and the
            number of leap seconds at that time. If the next leap second from
            the desired time is not known, than None is returned.

        """
        if time > datetime.datetime(2020, 12, 31, 23, 59, 59):
            logger.warning(
                "Leap seconds only current through 31 Dec 2020. Any future "
                "leap seconds not included. Update when available."
            )

        if time < cls._leap_seconds[0][0]:
            return cls._leap_seconds[0]
        elif time > cls._leap_seconds[-1][0]:
            return None
        else:
            for _ls in cls._leap_seconds:
                if time < _ls[0]:
                    return _ls

# Cell
def gps2utc(gps_time: Union[GPSTime, datetime.datetime]) -> datetime.datetime:
    """Convert GPS Time to UTC Time

    This function adjust the GPS Time using the number of leap seconds to get
    the UTC time.

    Parameters
    ----------
    gps_time : Union[GPSTime, datetime.datetime]
        The current GPS Time

    Returns
    -------
    datetime.datetime
        The UTC Time

    """
    if isinstance(gps_time, datetime.datetime):
        gps_time = GPSTime.from_datetime(gps_time)

    assert isinstance(gps_time, GPSTime), "gps_time must be a GPSTime or datetime"

    leap_seconds = LeapSeconds.get_leap_seconds(gps_time)
    utc_time = gps_time - leap_seconds

    return utc_time.to_datetime()

# Cell
def utc2gps(utc_time: datetime.datetime) -> GPSTime:
    """Convert UTC Time to GPS Time

    This function adjust the UTC Time using the number of leap seconds to get
    the GPS time.

    Parameters
    ----------
    utc_time : datetime.datetime
        The current UTC Time

    Returns
    -------
    GPSTime
        The GPS Time

    """
    assert isinstance(utc_time, datetime.datetime), "utc_time must be a datetime"

    leap_seconds = LeapSeconds.get_leap_seconds(GPSTime.from_datetime(utc_time))
    gps_time = GPSTime.from_datetime(utc_time) + leap_seconds

    return gps_time