from TemmaSerial import *

from datetime import datetime
import astropy
from astropy.time import Time
from astropy import coordinates as coords
#from astropy import units as u
import yaml, os

def set_site_info(config, mount):
    siteconfig = config['site']
    site_name = siteconfig['name']
    site_long = siteconfig['longitude']
    site_lat = siteconfig['latitude']
    siteloc=coords.EarthLocation(lon=config['site']['longitude'], lat=config['site']['latitude'])
    lat_d, lat_m, lat_s = siteloc.latitude.dms
    mount.set_latitude(lat_d, lat_m, lat_s)
    print(("site latitude : ", mount.get_latitude()))
    return site_name, siteloc

def calcLST(longi_deci):
    gmt = datetime.utcnow() #GMT time in UTC
    now = Time(gmt)
    try:
        lst = now.sidereal_time('apparent', longi_deci)
    except astropy.utils.iers.IERSRangeError:
        from astropy.utils.iers import IERS_A, IERS_A_URL
        if os.path.exists(os.path.basename(IERS_A_URL)):
            iers_a = IERS_A.open(os.path.basename(IERS_A_URL))
        else:
            iers_a = IERS_A.open(IERS_A_URL)
        now.delta_ut1_utc = now.get_delta_ut1_utc(iers_a)
        #validated with http://www.jgiesen.de/astro/astroJS/siderealClock/
        lst = now.sidereal_time('apparent', longitude=longi_deci)
    return gmt, lst


class TemmaControl(object):
    def __init__(self, configfile=None):
        if configfile is None:
            print("TemmaControl object needs yml config file as input")
            raise
        else :
            config = yaml.load(open(configfile))
        self.temma = TemmaSerial(config)
        self.site_name, self.site_loc = set_site_info(config, self.temma)
        print((self.site_name, self.site_loc))
        print(("Setting initial local sideral time:", self.set_LST()))
        
    def set_LST(self, site=None):
        """
        set LST into the mount, based on the site info if
        site argument is None.
        """
        #JCT: in a perfect world, a separate thread would send updated LST to temma, which has no clock, regularly. The alternatives is to always use autoguiding, which bypasses lst accuracy, or to set LST everywhere, which is the option here! 
        if site is not None:
            if not isinstance(site, coords.EarthLocation):
                raise ValueError('site argument needs to be an instance of astropy.coordinates.EarthLocation')
            longi_deci = site.longitude.hour
        else:
            longi_deci = self.site_loc.longitude.hour
        gmt, lst = calcLST(longi_deci)
        h, m, s = lst.hms
        self.temma.set_LST(h, m, s)
        return lst

    def goto_target(self, target, start_tracking=True):
        """
        Move telescope to point to target, an instance of astropy.coordinates.SkyCoord.
        """
        #JCT does it need a pointing attribute as in match function?
        if not isinstance(target, coords.SkyCoord):
            raise ValueError('target should be an instance of astropy.coordinates.SkyCoord')
        temma_fmt = sky2temma(target)
        #do we need to check standby?
        res, msg = self.temma.state_goto()
        if res=="s1":
            self.temma.stop()
        #update LST before sending the goto command.
        self.set_LST()
        res, msg = self.temma.goto(temma_fmt)
        print((res, msg))
        if start_tracking:
            print("Mount will track target")
            if self.temma.in_standby():
                self.temma.throttle_standby()
        return

    def get_radec(self):
        """
        Return radec from temma, after updating LST
        """
        lst = self.set_LST()
        print(("lst update:", lst))
        return self.temma.get_radec()
        
    def match(self, target, pointing=None):
        """
        Synch the encoders with the current position to the target celestial coordinates.
        If pointing is not None, check the current pointing side, and
        throttle if needed.
        """
        if pointing not in [None, 'E', 'W']:
            raise ValueError('pointing argument should be None, \"E\", or \"W\" ')
        if pointing in ['E', 'W']:
            current_pointing = tc.get_radec()[-2]
            if current_pointing != pointing:
                self.temma.throttle_pointing_side()
                
        if not isinstance(target, coords.SkyCoord):
            raise ValueError('target should be an instance of astropy.coordinates.SkyCoord')
        temma_fmt = sky2temma(target)

        #update LST
        lst = self.set_LST()
        print(("lst update:", lst))
        
        #temma-tools says that this need for the Z command
        #is a temma bug.
        #It is also there in audela
        self.temma.z_ok()
        
        res, msg = self.temma.sync(temma_fmt)
        if res != 'R0':
            raise Exception('%s: %s'%(res, msg))
        print(('match on target %s: %s %s'%(target,res, msg)))
        return

    def match_zenith(self, pointing=None):
        """
        Match to the zenith at the local position.
        This supposes that the telescope has been pointed to
        the zenith, for instance using a level.
        """
        #Audela has the code below, but temma.pl just issues the
        #Z command to temma.... CHECK
        lst = self.set_LST()
        dec = self.site_loc.latitude
        self.match(coords.SkyCoord(lst,dec), pointing)
        #JCT the lst here is going to be a bit off compared to the correct one that should be used when self.temma.sync is called in match. Need to change code to be more accurate?
        return
    
if __name__=="__main__":
    import yaml, os, sys
    #will add arg options later...
    configfile="TemmaControl.yml"
    config=yaml.load(open(configfile))
    print("yaml configuration:")
    print(config)

    tc = TemmaControl(configfile)
    
    # #1 start the temma and check connection
    # s="Checking serial port presence: "
    # if os.path.exists(config['temma']['port']):
    #     print s+"OK"
    # else:
    #     print s+"%s not found! exiting..."%config['temma']['port']
    #     sys.exit(1)
    # temma = TemmaSerial(config)
    # if temma is None:
    #     print 'temma mount failed connection, exiting...'
    #     sys.exit(1)
        
    # #2 upload site information
    # site_name, site_loc = set_site_info(config, temma)
    # rsp =  temma.get_correction_speed()
    # #"N" or "S" but we already know that from site latitude
    # hemisphere = rsp[7]
    # # RA/DEC follow speed (slow tunable speed set to 90%/90% of sidereal angular speed at switch on.
    # RA_ns_val= rsp[2:4]
    # DEC_ns_val= rsp[5:7]
    # #The East/West telescope needs to be defined
    # res = temma.get_radec()
    # print res
    
    # #3 compute and upload LST
    # longi_deci = site_loc.longitude.hour
    # gmt, lst = calcLST(longi_deci)
    # print gmt, lst
    # print "Upload LST to Temma..."
    # lst_h, lst_m, lst_s = lst.hms
    # temma.set_LST(h, m, s)
    # print temma.get_LST()

    # #3bis one can compute polaris position approximately
    # year = gmt.year
    # adpolaire=(0.01875*year)-34.9574#decimal hour
    # polaris_angle = lst - adpolaire*astropy.units.hourangle
    # #lst should be already in hourangle
    # print 'Polaris angular position : ', polaris_angle
    
    # #4 move to a target in the sky!
    # star_name="Landolt 92 245"
    # star_coord = coords.SkyCoord('00h54m16s','+00d39m51s')
    # temma.goto_target(star_coord)
    
    # print temma.state_goto()
    
