# Path: skyfield/constants.py
# AU_KM = 149597870.700
# 
# DAY_S = 86400.0
# 
# Path: skyfield/errors.py
# class EphemerisRangeError(ValueError):
#     """An ephemeris has been asked about positions outside its time range.
# 
#     Attributes:
# 
#     - `start_time`, `end_time`: the range of times supported by the segment
#     - `time_mask`: Boolean array where ``True`` marks out-of-range times
#     - `segment`: the ephemeris segment that was asked for positions
# 
#     """
#     def __init__(self, message, start_time, end_time, time_mask, segment):
#         self.args = message,
#         self.start_time = start_time
#         self.end_time = end_time
#         self.time_mask = time_mask
#         self.segment = segment
# 
# Path: skyfield/timelib.py
# def compute_calendar_date(jd_integer, julian_before=None):
#     """Convert Julian day ``jd_integer`` into a calendar (year, month, day).
# 
#     Uses the proleptic Gregorian calendar unless ``julian_before`` is
#     set to a specific Julian day, in which case the Julian calendar is
#     used for dates older than that.
# 
#     """
#     use_gregorian = (julian_before is None) or (jd_integer >= julian_before)
# 
#     # See the Explanatory Supplement to the Astronomical Almanac 15.11.
#     f = jd_integer + 1401
#     f += use_gregorian * ((4 * jd_integer + 274277) // 146097 * 3 // 4 - 38)
#     e = 4 * f + 3
#     g = e % 1461 // 4
#     h = 5 * g + 2
#     day = h % 153 // 5 + 1
#     month = (h // 153 + 2) % 12 + 1
#     year = e // 1461 - 4716 + (12 + 2 - month) // 12
#     return year, month, day
# 
# Path: skyfield/vectorlib.py
# class VectorFunction(object):
# class ReversedVector(VectorFunction):
# class VectorSum(VectorFunction):
#     def vector_name(self):
#     def center_name(self):
#     def target_name(self):
#     def __repr__(self):
#     def __str__(self):
#     def arrow_str(self):
#     def __add__(self, other):
#     def __neg__(self):
#     def __sub__(self, other):
#     def at(self, t):
#     def _observe_from_bcrs(self, observer):
#     def geometry_of(self, other):
#     def topos(self, latitude=None, longitude=None, latitude_degrees=None,
#               longitude_degrees=None, elevation_m=0.0, x=0.0, y=0.0):
#     def satellite(self, text):
#     def __init__(self, vector_function):
#     def vector_name(self):
#     def center_name(self):
#     def target_name(self):
#     def __neg__(self):
#     def _at(self, t):
#     def __init__(self, center, target, vector_functions):
#     def __str__(self):
#     def __repr__(self):
#     def _at(self, t):
# def _correct_for_light_travel_time(observer, target):
# def _jpl_name(target):


import os
from collections import defaultdict
from jplephem.exceptions import OutOfRangeError
from jplephem.spk import SPK
from jplephem.names import target_name_pairs
from .constants import AU_KM, DAY_S
from .errors import EphemerisRangeError
from .timelib import compute_calendar_date
from .vectorlib import VectorFunction, VectorSum, _jpl_code_name_dict
     'de421.bsp' segment 3 EARTH BARYCENTER -> 399 EARTH>
    >>> planets[499]
    <VectorSum of 2 vectors:
     'de421.bsp' segment 0 SOLAR SYSTEM BARYCENTER -> 4 MARS BARYCENTER
     'de421.bsp' segment 4 MARS BARYCENTER -> 499 MARS>

    The result will be a :class:`~skyfield.vectorlib.VectorFunction`
    instance that you can ask for a position at a given input time.

    """
    def __init__(self, path):
        self.path = path
        self.filename = os.path.basename(path)
        self.spk = SPK.open(path)
        self.segments = [SPICESegment(self, s) for s in self.spk.segments]
        self.codes = set(s.center for s in self.segments).union(
                         s.target for s in self.segments)

    def __repr__(self):
        return '<{0} {1!r}>'.format(type(self).__name__, self.path)

    def __str__(self):
        segments = self.spk.segments
        lines = ['SPICE kernel file {0!r} has {1} segments'
                 .format(self.filename, len(segments))]
        format_date = '{0}-{1:02}-{2:02}'.format
        start = end = None
        for s in segments:
            if start != s.start_jd or end != s.end_jd:
                start, end = s.start_jd, s.end_jd

# Path: skyfield/constants.py
# AU_KM = 149597870.700
# 
# DAY_S = 86400.0
# 
# Path: skyfield/errors.py
# class EphemerisRangeError(ValueError):
#     """An ephemeris has been asked about positions outside its time range.
# 
#     Attributes:
# 
#     - `start_time`, `end_time`: the range of times supported by the segment
#     - `time_mask`: Boolean array where ``True`` marks out-of-range times
#     - `segment`: the ephemeris segment that was asked for positions
# 
#     """
#     def __init__(self, message, start_time, end_time, time_mask, segment):
#         self.args = message,
#         self.start_time = start_time
#         self.end_time = end_time
#         self.time_mask = time_mask
#         self.segment = segment
# 
# Path: skyfield/timelib.py
# def compute_calendar_date(jd_integer, julian_before=None):
#     """Convert Julian day ``jd_integer`` into a calendar (year, month, day).
# 
#     Uses the proleptic Gregorian calendar unless ``julian_before`` is
#     set to a specific Julian day, in which case the Julian calendar is
#     used for dates older than that.
# 
#     """
#     use_gregorian = (julian_before is None) or (jd_integer >= julian_before)
# 
#     # See the Explanatory Supplement to the Astronomical Almanac 15.11.
#     f = jd_integer + 1401
#     f += use_gregorian * ((4 * jd_integer + 274277) // 146097 * 3 // 4 - 38)
#     e = 4 * f + 3
#     g = e % 1461 // 4
#     h = 5 * g + 2
#     day = h % 153 // 5 + 1
#     month = (h // 153 + 2) % 12 + 1
#     year = e // 1461 - 4716 + (12 + 2 - month) // 12
#     return year, month, day
# 
# Path: skyfield/vectorlib.py
# class VectorFunction(object):
# class ReversedVector(VectorFunction):
# class VectorSum(VectorFunction):
#     def vector_name(self):
#     def center_name(self):
#     def target_name(self):
#     def __repr__(self):
#     def __str__(self):
#     def arrow_str(self):
#     def __add__(self, other):
#     def __neg__(self):
#     def __sub__(self, other):
#     def at(self, t):
#     def _observe_from_bcrs(self, observer):
#     def geometry_of(self, other):
#     def topos(self, latitude=None, longitude=None, latitude_degrees=None,
#               longitude_degrees=None, elevation_m=0.0, x=0.0, y=0.0):
#     def satellite(self, text):
#     def __init__(self, vector_function):
#     def vector_name(self):
#     def center_name(self):
#     def target_name(self):
#     def __neg__(self):
#     def _at(self, t):
#     def __init__(self, center, target, vector_functions):
#     def __str__(self):
#     def __repr__(self):
#     def _at(self, t):
# def _correct_for_light_travel_time(observer, target):
# def _jpl_name(target):


# Path: skyfield/jpllib.py
import os
from collections import defaultdict
from jplephem.exceptions import OutOfRangeError
from jplephem.spk import SPK
from jplephem.names import target_name_pairs
from .constants import AU_KM, DAY_S
from .errors import EphemerisRangeError
from .timelib import compute_calendar_date
from .vectorlib import VectorFunction, VectorSum, _jpl_code_name_dict

     'de421.bsp' segment 3 EARTH BARYCENTER -> 399 EARTH>
    >>> planets[499]
    <VectorSum of 2 vectors:
     'de421.bsp' segment 0 SOLAR SYSTEM BARYCENTER -> 4 MARS BARYCENTER
     'de421.bsp' segment 4 MARS BARYCENTER -> 499 MARS>

    The result will be a :class:`~skyfield.vectorlib.VectorFunction`
    instance that you can ask for a position at a given input time.

    """
    def __init__(self, path):
        self.path = path
        self.filename = os.path.basename(path)
        self.spk = SPK.open(path)
        self.segments = [SPICESegment(self, s) for s in self.spk.segments]
        self.codes = set(s.center for s in self.segments).union(
                         s.target for s in self.segments)

    def __repr__(self):
        return '<{0} {1!r}>'.format(type(self).__name__, self.path)

    def __str__(self):
        segments = self.spk.segments
        lines = ['SPICE kernel file {0!r} has {1} segments'
                 .format(self.filename, len(segments))]
        format_date = '{0}-{1:02}-{2:02}'.format
        start = end = None
        for s in segments:
            if start != s.start_jd or end != s.end_jd:
                start, end = s.start_jd, s.end_jd

                starts = format_date(*compute_calendar_date(int(start)))