from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import numpy as np
import sys
sys.path.insert(0, '/Users/bmorris/git/tmp/astropy')
import astropy
from astropy.time import Time
from astropy.coordinates import (SkyCoord, Longitude, Latitude, GCRS, ICRS,
                                 CartesianRepresentation, AltAz)
import astropy.units as u
from astropy.utils.data import download_file

def get_spk_file():
    """
    Get the Satellite Planet Kernel (SPK) file `de430.bsp` from NASA JPL.
    Download the file from the JPL webpage once and subsequently access a
    cached copy. This file is ~120 MB, and covers years ~1550-2650 CE [1]_.
    .. [1] http://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/aareadme_de430-de431.txt
    """
    de430_url = ('http://naif.jpl.nasa.gov/pub/naif/'
                 'generic_kernels/spk/planets/de430.bsp')
    return download_file(de430_url, cache=True, show_progress=True)

def get_moon(time, location, pressure, use_pyephem=False, use_skyfield=False, use_icrs=False):
    """
    SOURCE: https://github.com/astropy/astroplan/blob/edfed62c7034f655dcd40853d29432b14961182d/astroplan/moon.py

    Position of the Earth's moon.
    Currently uses PyEphem to calculate the position of the moon by default
    (requires PyEphem to be installed). Set ``use_pyephem`` to `False` to
    calculate the moon position with jplephem (requires jplephem to be
    installed).
    Parameters
    ----------
    time : `~astropy.time.Time` or see below
        Time of observation. This will be passed in as the first argument to
        the `~astropy.time.Time` initializer, so it can be anything that
        `~astropy.time.Time` will accept (including a `~astropy.time.Time`
        object).
    location : `~astropy.coordinates.EarthLocation`
        Location of the observer on Earth
    use_pyephem : bool (default = `True`)
        Calculate position of moon using PyEphem (requires PyEphem to be
        installed). If `False`, calculates position of moon with jplephem.
    Returns
    -------
    moon_sc : `~astropy.coordinates.SkyCoord`
        Position of the moon at ``time``
    """
    if not isinstance(time, Time):
        time = Time(time)

    if use_pyephem:
        try:
            import ephem
        except ImportError:
            raise ImportError("The get_moon function currently requires "
                              "PyEphem to compute the position of the moon.")

        moon = ephem.Moon()
        obs = ephem.Observer()
        obs.lat = location.latitude.to_string(u.deg, sep=':')
        obs.lon = location.longitude.to_string(u.deg, sep=':')
        obs.elevation = location.height.to(u.m).value
        if pressure is not None:
            obs.pressure = pressure.to(u.bar).value*1000.0

        if time.isscalar:
            obs.date = time.datetime
            moon.compute(obs)
            moon_alt = float(moon.alt)
            moon_az = float(moon.az)
            moon_distance = moon.earth_distance
        else:
            moon_alt = []
            moon_az = []
            moon_distance = []
            for t in time:
                obs.date = t.datetime
                moon.compute(obs)
                moon_alt.append(float(moon.alt))
                moon_az.append(float(moon.az))
                moon_distance.append(moon.earth_distance)
        return SkyCoord(alt=moon_alt*u.rad, az=moon_az*u.rad,
                        distance=moon_distance*u.AU,
                        frame=AltAz(location=location, obstime=time))
    elif use_skyfield:
        from skyfield import api

        if use_skyfield is True:
            use_skyfield = 'de421.bsp'
        planets = api.load(use_skyfield)
        loc = planets['earth'].topos(latitude_degrees=location.latitude.deg,
                                     longitude_degrees=location.longitude.deg)
        moon_obs = loc.at(api.JulianDate(tai=time.tai.jd)).observe(planets['moon'])
        alt, az, d = moon_obs.apparent().altaz()
        return SkyCoord(alt=alt.radians*u.rad, az=az.radians*u.rad,
                        distance=d.au*u.AU,
                        frame=AltAz(location=location, obstime=time))



    else:
        try:
            import jplephem
        except ImportError:
            raise ImportError("The get_moon function currently requires "
                              "jplephem to compute the position of the moon.")

        # Calculate position of moon relative to Earth by subtracting the
        # vector pointing from the Earth-moon barycenter to the moon by
        # the vector from the Earth-moon barycenter to the Earth
        from jplephem.spk import SPK
        kernel = SPK.open(get_spk_file())
        if use_icrs:
            cartesian_position = (kernel[0,3].compute(time.jd) +
                                  kernel[3,301].compute(time.jd))
            cartrep = CartesianRepresentation(cartesian_position*u.km)
            moon_icrs = SkyCoord(cartrep, frame='icrs')
            return moon_icrs.transform_to(AltAz(obstime=time, location=location))
        else:
            cartesian_position = (kernel[3,301].compute(time.jd) -
                                  kernel[3,399].compute(time.jd))

            # Convert to GCRS coordinates
            cartrep = CartesianRepresentation(cartesian_position*u.km)
            # return SkyCoord(cartrep, frame=GCRS(obstime=time,
            #                                     obsgeoloc=location))
            return SkyCoord(cartrep, frame=GCRS(obstime=time,
                                                obsgeoloc=u.Quantity(location.to_geocentric())))


from astroplan import get_site, Observer

time = Time('1984-01-21 12:00:00')
location = get_site('APO')
obs = Observer(location=location, pressure=0)

moon_pyephem = get_moon(time, location, 0*u.bar, use_pyephem=True)
moon_skyfield = get_moon(time, location, 0*u.bar, use_skyfield=True)
moon_skyfield430 = get_moon(time, location, 0*u.bar, use_skyfield='de430.bsp')
moon_jplephem = get_moon(time, location, 0*u.bar)
moon_jplephem_icrs = get_moon(time, location, 0*u.bar, use_icrs=True)

altaz_pyephem = obs.altaz(time, moon_pyephem)
altaz_jplephem = obs.altaz(time, moon_jplephem)
altaz_jplephem_icrs = obs.altaz(time, moon_jplephem_icrs)

print("With astropy v{0}".format(astropy.__version__))
print("PyEphem (Alt, Az, Dist): ({0.alt}, {0.az}, {0.distance.km} km)".format(altaz_pyephem))

print("Skyfield (Alt, Az, Dist): ({0.alt}, {0.az}, {0.distance.km} km)".format(moon_skyfield))
print("Skyfield(de430) (Alt, Az, Dist): ({0.alt}, {0.az}, {0.distance.km} km)".format(moon_skyfield430))

print("jplephem/Astropy (Alt, Az, Dist): ({0.alt}, {0.az}, {0.distance.km} km)".format(altaz_jplephem))
print("jplephem/Astropy(ICRS) (Alt, Az, Dist): ({0.alt}, {0.az}, {0.distance.km} km)".format(altaz_jplephem_icrs))

"""
Output with astropy v1.0.5
PyEphem (Alt, Az): (55.4920153458 deg, 239.031895666 deg)
jplephem (Alt, Az): (34.177292986 deg, 135.374232837 deg)
"""