from xmlrpc.client import ServerProxy, Error
import re
import time
import ephem
import numpy as np
from stardice.targets import deg2dms, deg2hms
import datetime
import stardice

def sanitize(s):
    return s.replace('\xdf', ':')

class FakePulsar2():
    def __init__(self, **keys):
        self.fakeinstrument = ServerProxy('http://localhost:8900/')
        time.sleep(1)
        for k in keys:
            setattr(self, k, keys[k])
        self._open = False
        
        _commands = {
            '^#:YV#$': self._version,
            '^#:Q#$': self._stop,
            '^#:GR#$': self._get_ra,
            '^#:Gr#$': self._get_target_ra,
            '^#:GD#$': self._get_dec,
            '^#:Gd#$': self._get_target_dec,
            '^#:St ([+-]\d\d:\d\d:\d\d)#$': self._set_latitude,
            '^#:Sg (\d\d\d:\d\d:\d\d)#$': self._set_longitude,
            '^#:Gt#$': self._get_lat,
            '^#:Gg#$': self._get_lon,
            #'^#:GL#$': self._get_local_time,
            '^#:Sr (\d\d:\d\d:\d\d)#$': self._set_ra,
            '^#:Sd ([+-]\d\d:\d\d:\d\d)#$': self._set_dec,
            '^#:SL (\d\d:\d\d:\d\d)#$': self._set_local_time,
            '^#:GS#$': self._get_sideral_time,
            '^#:SS#$': self._set_sideral_time,
            '^#:YGS#$': self._get_actual_rate,
            '^#:MS#$': self._start_slewing,
            '^#:YGi#$': self._is_slewing,
            '^#:YGj#$': self._is_parking,
            '^#:YGk#$': self._is_parked,
            '^#:YGW#$': self._is_flip_needed,
            '^#:YGh#$': self._is_home_set,
            '^#:YS(A)(\d)#$': self._set_speed_value,
            '^#:YG([BCDE])#$': self._get_speed_value,
            '^#:YS([BCDE])(\d\d\d)#$': self._set_speed_value,
            '^#:YG([MNPQR])#$': self._get_property,
            '^#:YS([MNG])(\d)#$': self._set_property,
            '^#:YS([PR])(\d),(\d)#$': self._set_property,
            '^#:YH#$': self._park,
            '^#:YL#$': self._unpark,
            '^#:MG([swen])(\d\d\d)3#$': self._pulse_move,
            '^#:Mg([swen])(\d\d\d)3#$': self._pulse_move,
            '^#:R([GCMS])#$': self._set_speed,
            '^#:YSS(\d),(\d)#$': self._set_tracking,
            '^#:YGS#$': self._get_tracking,
        }

        self.commands = {}
        for k in _commands:
            self.commands[re.compile(k)] = _commands[k]
        self._buffer = ''
        
        self._current_rate = 1
        self.observer = ephem.Observer()
        self._tracking = 1,1
        self._speeds = {'A': 9, #guiding speed
                        'B': 9, #center speed
                        'C': 99, #find speed
                        'D': 400, #slew speed
                        'E': 400} #goto speed
        self.selected_speed = 'C'
        self._properties = {
            'M': 1, # mount type 3/2/1 (3=AltAz, 2=fork,1=German)
            'N': 0, # phisical side of pier 0/1# (East=0/West=1)
            'P': (0,0), # PEC corr
            'Q': 0, # polecross
            'R': (0, 0), # refract corr
            'S': (0, 0), #speed rate sel 
        }
        
    def _write(self, s):
        self._buffer += (s + '#\n')
        print((self._buffer))
        
    def _polecross(self, c, s):
        self._write('1')
        
    def _version(self, c, s):
        self._write('PULSAR V.DUMMY,2008.10.10')

    def _get_actual_rate(self, c, s):
        self._write('%d' % self._current_rate)
        
    def _get_ra(self, c, s):
        ra, dec = self.fakeinstrument.get_radec()
        self._write(deg2hms(ra))

    def _get_dec(self, c, s):
        ra, dec = self.fakeinstrument.get_radec()
        self._write(deg2dms(dec))
        
    def _get_lat(self, c, s):
        lat = self.observer.lat
        self._write(str(lat))

    def _get_lon(self, c, s):
        lon = self.observer.lon
        self._write(stardice.targets.long2dms(np.degrees(-lon)))
        
    def _get_target_ra(self, c, s):
        ra, dec = self.fakeinstrument.get_target_radec()
        self._write(deg2hms(ra))

    def _get_target_dec(self, c, s):
        ra, dec = self.fakeinstrument.get_target_radec()
        self._write(deg2dms(dec))

    def _get_sideral_time(self, c, s):
        self.observer.date = datetime.datetime.utcnow()
        self._write(str(self.observer.sidereal_time()).split('.')[0])

    # To check
    def _set_speed(self, c, s):
        self.selected_speed = c.findall(s)[0]
        self._write('1')

    def _set_tracking(self, c, s):
        self._tracking = [int(e) for e in c.findall(s)[0]]
        self.fakeinstrument.set_speed(self._tracking)
        self._write('1')
        
    def _get_tracking(self, c, s):
        speed = self.fakeinstrument.get_speed()
        self._tracking = int(speed[0] == 1), int(speed[1] == 1) 
        self._write('%d,%d' % self._tracking)
        
    def _set_property(self, c, s):
        parse = c.findall(s)[0]
        if len(parse) == 3:
            prop, v1, v2 = parse[0], int(parse[1]), int(parse[2])
            self._properties[prop] = (v1, v2)
        else:
            prop, v1 = parse[0], int(parse[1])
            self._properties[prop] = v1
        self._write('1')
        
    def _get_property(self, c, s):
        prop = self._properties[c.findall(s)[0]]
        try:
            self._write('%d,%d' % prop)
        except TypeError:
            self._write('%d' % prop)
        
    def _pulse_move(self, c, s):
        direction, duration = c.findall(s)[0]
        delta = self._speed * int(duration)
        ra, dec = self.fakeinstrument.get_ra_dec()
        if direction == 's':
            dec += delta
        elif direction == 'n':
            dec -= delta
        elif direction == 'e':
            ra += delta
        else:
            ra -= delta
        self.fakeinstrument.set_ra_dec(ra, dec)
        self._write('1')
            
    def _set_latitude(self, c, s):
        self.observer.lat = ephem.degrees(sanitize(c.findall(s)[0]))
        self._write('1')

    def _set_longitude(self, c, s):
        self.observer.lon = -ephem.degrees(sanitize(c.findall(s)[0]))
        self._write('1')

    def _set_local_time(self, c, s):
        self.observer.date = str(datetime.datetime.utcnow()).split(' ')[0] + ' ' + sanitize(c.findall(s)[0])
        self._write('1')
        
    def _set_sideral_time(self, c, s):
        lst = ephem.hours(c.findall(s)[0])
        cst = self.observer.sideral_time()
        self.observer.date -= (cst - lst)/(2*np.pi)*0.9972696
        self._write('1')
        
    def _set_ra(self, c, s):
        self.ra = ephem.hours(c.findall(s)[0])
        self.fakeinstrument.set_target_ra(float(np.degrees(self.ra)))
        self._write('1')

    def _set_dec(self, c, s):
        self.dec = ephem.degrees(c.findall(s)[0])
        self.fakeinstrument.set_target_dec(float(np.degrees(self.dec)))
        self._write('1')

    def _start_slewing(self, c, s):
        self.fakeinstrument.slew()
        self._write('0')

    def _stop(self, c, s):
        self.fakeinstrument.stop()
        self._write('0')
        

        
    def _is_slewing(self, c, s):
        if self.fakeinstrument.is_slewing():
            self._write('1')
        else:
            self._write('0')

    def _set_speed_value(self, c, s):
        typ, speed = c.findall(s)[0]
        self._speeds[typ] = int(speed)
        self._write('1')
        
    def _get_speed_value(self, c, s):
        typ = c.findall(s)[0]
        self._write('%d' % typ)

    # To implement
    def _get_pier_side(self, c, s):
        self._write('0')
        
    def _set_pier_side(self, c, s):
        self._write('0')
        
    def _is_parked(self, c, s):
        self._write('0')
        
    def _is_parking(self, c, s):
        self._write('0')

    def _is_flip_needed(self, c, s):
        self._write('0')

    def _is_home_set(self, c, s):
        self._write('0')

    def _park(self, c, s):
        self._write('0')

    def _unpark(self, c, s):
        self._write('0')

    # other
    def open(self):
        self.fakeinstrument.start_loop()
        self._open = True
        return True
    
    def flushOutput(self):
        pass

    def flushInput(self):
        pass

    def isOpen(self):
        return self._open
    
    def close(self):
        pass
    
    def write(self, s):
        for c in self.commands:
            if c.match(s):
                self.commands[c](c, s)

    def read(self):
        r = self._buffer[0]
        self._buffer = self._buffer[1:]
        return r

    def readline(self):
        end = self._buffer.find('\n')
        r = self._buffer[:end]
        self._buffer = self._buffer[end+1:]
        return r

    def readlines(self):
        r = self._buffer.splitlines()
        self._buffer = ''
        return r
