#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Interface with an LX200-based device
"""

# System modules
import socket

# Custom modules
from devices.basedevice import BaseDevice
from utils import check
from utils.logger import log


class LX200(BaseDevice):
    '''Base wrapper class for LX200 stuff'''

    def __init__(self, url):
        '''Constructor.'''
        
        super().__init__(url)
        self.url = url.split(":")[0] or url
        self.port = url.split(":")[1] or 22
        self.timeout = 3
        self.connection = None

    @check.socket_errors
    def _connect(self):
        '''Setup a connection to the telescope, if needed.'''


    @check.socket_errors
    def _send(self, get_or_set, message=None):
        '''
        Send messages to the telescope to set or get properties, if it is
        useful to have a common metod. If not, implement directly on get/put
        '''
        
    def get(self, message):
        '''Getter method'''

        return self._send("get", message)

    def put(self, key, val):
        '''Setter method'''
        
        return self._send("set", message)


class Telescope(LX200):
    '''Implementation of the Telescope commands mocking an Alpaca
       Telescope wrapper.'''
         
    @property
    def tracking(self):
        '''Tracking status'''
        message = "POINTING.TRACK"
        res = self.get(message)
        try:
            self._tracking = True if int(res) else False
        except TypeError as e:            
            self._tracking = None
        return self._tracking

    @tracking.setter
    def tracking(self, b):
        '''(Re)Start/Stop tracking of telescope in function of currently configured target'''
        ''' 1 slew to currently configured target and start tracking'''
        ''' 0 stop tracking'''
        message = "POINTING.TRACK"
        track = 1 if b else 0
        self.put(message, track)
        return self.tracking

    def track(self):
        '''More direct way to start tracking'''
        self.tracking = True
        return self._tracking

    def abort(self):
        '''Direct way to stop tracking'''
        '''Check if need to add other abort'''
        
        self.tracking = False
        return self._tracking

    @property
    def is_moving(self):
        '''Check the motion state of the telescope'''
        
        message = "TELESCOPE.MOTION_STATE"
        res = self.get(message)
        return res

    @property
    def temperature(self):
        '''Mirrors temperature'''
        message = ["AUXILIARY.SENSOR[2].VALUE",
                   "AUXILIARY.SENSOR[3].VALUE",
                   "AUXILIARY.SENSOR[4].VALUE"]
        res = self.get(message)
        return res

    @property
    def status(self):
        '''need to check exit and error from get function'''
        message = "TELESCOPE.STATUS.LIST"
        res = self.get(message)
        return res

    @property
    def state(self):

        message = "TELESCOPE.STATUS.GLOBAL"
        res = self.get(message)
        return res

    def clear(self, n):

        res = self.put("TELESCOPE.STATUS.CLEAR_ERROR", n)
        return res

    @property
    def clock(self):
        """
        in UNIX time
        """

        message = "POSITION.LOCAL.UTC"
        res = self.get(message)
        return res

    @property
    def cover(self):

        message = "AUXILIARY.COVER.REALPOS"
        res = self.get(message)
        self._cover = res
        return self._cover

    @property
    def open(self):

        res = self.cover
        if res == 1.0:
            self._open = True
        elif res == 0.0:
            self._open = False
        else:
            self._open = None
        return self._open

    @open.setter
    def open(self, b):
        pos = 1.0 if b else 0.0
        res = self.put("AUXILIARY.COVER.TARGETPOS", pos)
        self._open = self.open

    @property
    def park(self):
        '''Get if the telescope is in parked position'''
        
        message = "TELESCOPE.READY_STATE"
        res = self.get(message)
        try:
            self._park = False if int(res) else True
        except TypeError as e:
            self._park = None
        return self._park

    @park.setter
    def park(self, b, timeout=90):
        '''Init the telescope homing the axes,
        or put the telescope in park position'''
        
        message = "TELESCOPE.READY"
        pos = 0.0 if b else 1.0
        res = self.put(message, pos)
        self._park = self.park

    @property
    def altaz(self):

        message = ["POSITION.HORIZONTAL.ALT",
                   "POSITION.HORIZONTAL.AZ"]
        res = self.get(message)
        altaz = res
        self._altaz = altaz  # [alt, az]
        return self._altaz

    @altaz.setter
    def altaz(self, a):
        
        #self.tracking = False
        keys = ["POINTING.SETUP.DEROTATOR.SYNCMODE",
                "OBJECT.HORIZONTAL.ALT",
                "OBJECT.HORIZONTAL.AZ",
                "POINTING.TRACK"]
        derot = 2 # from astelos log file "true orientation"
        track = 2 # 2: go and stay there
        values = [derot, a[0], a[1], track]
        res = self.put(keys, values)
        self._altaz = self.altaz

    @property
    def targetradec(self):
        '''Get target Right Ascension / Declination'''

        message = ["POSITION.EQUATORIAL.RA_J2000",
                   "POSITION.EQUATORIAL.DEC_J2000"]
        res = self.get(message)
        targetradec = res
        self._targetradec = targetradec
        return self._targetradec

    @targetradec.setter
    def targetradec(self, a):
        '''Set target Right Ascension / Declination'''

        keys = ["POINTING.SETUP.DEROTATOR.SYNCMODE",
                "OBJECT.EQUATORIAL.RA",
                "OBJECT.EQUATORIAL.DEC",
                "POINTING.TRACK"]
        derot = 2 # from astelos log file "true orientation"
        track = 1 # 1: go and track
        values = [derot, a[0], a[1], track]
        res = self.put(keys, values)
        self._targetradec = self.targetradec

    @property
    def radec(self):
        '''Get Right Ascension / Declination'''

        message = ["POSITION.EQUATORIAL.RA_J2000",
                   "POSITION.EQUATORIAL.DEC_J2000"]
        res = self.get(message)
        radec = res
        self._radec = radec
        return self._radec

    @radec.setter
    def radec(self, a):
        '''Set Right Ascension / Declination'''

        keys = ["POINTING.SETUP.DEROTATOR.SYNCMODE",
                "OBJECT.EQUATORIAL.RA",
                "OBJECT.EQUATORIAL.DEC",
                "POINTING.TRACK"]
        derot = 2 # from astelos log file "true orientation"
        track = 1 # 1: go and track
        values = [derot, a[0], a[1], track]
        res = self.put(keys, values)
        self._radec = self.radec

    @property
    def offset(self):
        '''Get Telescope offset'''

        message = ["POSITION.INSTRUMENTAL.ZD.OFFSET",
                   "POSITION.INSTRUMENTAL.AZ.OFFSET"]
        res = self.get(message)
        offset = res
        self._offset = offset
        return self._offset

    @offset.setter
    def offset(self, a):
        '''Set Telescope offset'''
        
        keys = ["POSITION.INSTRUMENTAL.ZD.OFFSET",
                "POSITION.INSTRUMENTAL.AZ.OFFSET"]
        values = [a[0], a[1]]
        if abs(values[0]) > 0.9:  # 54 arcmin
            log.error(f"zd {a[0]} too large. Maybe arcsec instead of deg?")
            return
        if abs(values[1]) > 0.9:  # 54 arcmin
            log.error(f"az {a[1]} too large. Maybe arcsec instead of deg?")
            return
        res = self.put(keys, values)
        self._offset = self.offset

    @property
    def coordinates(self):
        """
        Ask simultaneously
        RA2000, DEC2000, ALT, AZ, LST, UTC.
        Answer is given as a dict of decimal numbers.
        UTC is given as unix time.
        """

        message = ["POSITION.EQUATORIAL.RA_J2000",
                   "POSITION.EQUATORIAL.DEC_J2000",
                   "POSITION.HORIZONTAL.ALT",
                   "POSITION.HORIZONTAL.AZ",
                   # "OBJECT.HORIZONTAL.ALT",
                   # "OBJECT.HORIZONTAL.AZ",
                   "POSITION.LOCAL.SIDEREAL_TIME",
                   "POSITION.LOCAL.UTC"]

        res = self.get(message)

        # print(telnet_dict)

        simple_dict = {
            "radec": [res[0],
                      res[1]],
            "altaz": [res[2],
                      res[3]],
            "lst": res[4],
            "utc": res[5],
        } if res else None

        return simple_dict


class Focuser(LX200):
    '''Implementation of the Focuser commands mocking my Alpaca Telescope
       wrapper.'''

    @property
    def is_moving(self):
        '''TBD with the right telnet commands'''
        #log.warning("TBD with the right telnet commands")
        
    @property
    def position(self):
        '''Get Relative focuser position from telnet'''

        message = "POSITION.INSTRUMENTAL.FOCUS.OFFSET"
        pos = self.get(message)
        try:
            res = float(pos)*1000
        except TypeError as e:
            res =  None            
        self._position = res
        return self._position

    @position.setter
    def position(self, s):  # 0-34500=micron?
        '''Set Relative focuser position from telnet'''

        message = "POSITION.INSTRUMENTAL.FOCUS.OFFSET"
        pos = s/1000
        res = self.put(message, pos)
        self._position = self.position

    @property
    def absolute(self):
        '''Get Absolute focuser position from telnet'''

        message = "POSITION.INSTRUMENTAL.FOCUS.REALPOS"
        res = self.get(message)
        self._absolute = res
        return self._absolute

    @absolute.setter
    def absolute(self, s):  # 0-34500=micron?
        '''Set Absolute focuser position from telnet'''

        message = "POSITION.INSTRUMENTAL.FOCUS.OFFSET"
        pos = s/1000
        res = self.put(message, pos)
        self._absolute = self.absolute


class Rotator(LX200):
    '''Implementation of the Rotator commands mocking my Alpaca Telescope
       wrapper.'''

    @property
    def is_moving(self):
        '''TBD with the right telnet commands'''
        #log.warning("TBD with the right telnet commands")

    @property
    def position(self):
        '''Get Relative rotator position from telnet'''

        message = "POSITION.INSTRUMENTAL.DEROTATOR[2].OFFSET"
        res = self.get(message)
        self._position = res
        return self._position

    @position.setter
    def position(self, s):  # 0-270 deg?
        '''Set Relative rotator position from telnet'''
         
        message = "POSITION.INSTRUMENTAL.DEROTATOR[2].OFFSET"
        res = self.put(message, s)
        self._position = self.position

    @property
    def absolute(self):
        '''Get Absolute rotator position from telnet'''
        
        message = "POSITION.INSTRUMENTAL.DEROTATOR[2].REALPOS"
        res = self.get(message)
        self._absolute = res
        return self._absolute


