
import inspect
import logging
from xmlrpc.server import list_public_methods
from xmlrpc.client import ServerProxy, Error
import serial
import time

"""
From the developer Evangelos Souglakos <evansgl@gmail.com>
#: Check if focuser is up and running (Prints OK_SMFC)
H: Halt motor
P: Print current position
I: Check motor status (boolean: true for moving, false for idle
M: Move focuser to position (M: long_value)
T: Print temperature in Celcius Degrees
N: Invert pins (boolean: true for invert, false for normal)
S: Save motor max speed to EPROM (e.g S:500)
V: Firmware Version
G: Go +- steps steps from current position. (G:-30 will execute: position-30steps etc)
W: Set a new motor position. (e.g W:0 Reset position to 0 or W:3000 Sets position to 3000)
L: Enable/Disable LED (L:2 = enable, L:1=disable)
A: Combines all status command. product_status:version:temperature:current_position:motor_status:led_status:invert_motor
e.g 
transmit: A \n
receive: OK_SMFC:2.5:22.3:5000:0:2:0 \n

"""
class Focuser(object):
    def __init__(self, port="/dev/ttyUSB1"):
        self.ser = serial.Serial()
        self.ser.port = port
        self.ser.baudrate = 19200
        self.ser.parity = serial.PARITY_NONE
        self.ser.timeout = 0.25
        self.ser.open()

    def is_ok(self):
        """Check if focuser is up and running"""
        self.ser.write("#\n")
        res = self.ser.read(100)
        if res in ["OK_DMFCN\n","OK_DMFC\n","OK_SMFC\n"]:
            return True
        else:
            return False

    def stop(self):
        """Halt motor"""
        self.ser.write("H\n")
        return True

    def current_position(self):
        """Print current position"""
        self.ser.write("P\n")
        res = self.ser.read(100)
        return int(res.strip('\n'))

    def is_stepping(self):
        """Check motor status (boolean: true for moving, false for idle"""
        self.ser.write("I\n")
        res = self.ser.read(100)
        if res.strip('\n')=='0':
            return False #idle
        else:
            return True #moving

    def move(self, value):
        """Move focuser to position (M: long_value)"""
        self.ser.write("M:%d\n"%value)
        return True

    def get_temperature(self):
        """Print temperature in Celcius Degrees"""
        self.ser.write("T\n")
        res = self.ser.read(100)
        return res.strip('\n')

    def invert_pins(self, value):
        if value not in [0,1, True, False]:
            raise TypeError
        """Invert pins (boolean: true for invert, false for normal)"""
        self.ser.write("N:%d\n"%value)
        return True

    def set_max_speed(self, value):
        """Save motor max speed to EPROM"""
        self.ser.write("S:%d\n"%value)
        return True

    def get_version(self):
        """Firmware Version"""
        self.ser.write("V\n")
        res = self.ser.read(100)
        return res.strip('\n')

    def get_allinfo(self):
        """ Combines all status command.
         product_status:version:temperature:current_position:motor_status:led_status:invert_motor"""
        self.ser.write("A\n")
        res = self.ser.read(100)
        return res.strip('\n')

    def go(self, value):
        """Go +- steps steps from current position. (G:-30 will execute: position-30steps etc)"""
        self.ser.write("G:%d\n"%value)
        return True # CHECK THIS - what about error handling ?

    def set_position(self, value):
        """Set a new motor position.
        (e.g W:0 Reset position to 0 or W:3000 Sets position to 3000)"""
        self.ser.write("W:%d\n"%value)
        return True

    def throttle_led(self, value):
        if value not in [1, 2, "OFF", "ON", "off", "on", "On", "Off"]:
            raise ValueError
        if value==1:
            self.ser.write("L:1\n")
        elif value==2:
            self.ser.write("L:2\n")
        elif value.upper()=="OFF":
            self.ser.write("L:1\n")
        else:
            self.ser.write("L:2\n")
        return True

    # ----------------------- Introspection -----------------------------

    def _listMethods(self):
        logging.info("Focuser._listMethods() called.")
        methods = list_public_methods(self)
        logging.info("Focuser._listMethods() done.")
        return methods

    def _methodHelp(self, method):
        f = getattr(self, method)
        return inspect.getdoc(f)

class DummyFocuser(Focuser):
    def __init__(self, port="/dev/ttyUSB1"):
        self.port = port
        self.open()

    def open(self):
        self.fi = ServerProxy('http://localhost:8900/')
        time.sleep(1)

    def is_ok(self):
        return True

    def stop(self):
        return self.fi.focus_stop()

    def current_position(self):
        return self.fi.focus_position()
    
    def is_stepping(self):
        return self.fi.is_stepping()

    def move(self, value):
        return self.go(value + self.current_position())

    def get_temperature(self):
        return self.fi.get_focus_temperature()

    def invert_pins(self, value):
        if value not in [0,1, True, False]:
            raise TypeError
        """Invert pins (boolean: true for invert, false for normal)"""
        return True

    def set_max_speed(self, value):
        return self.fi.set_focus_max_speed(value)

    def get_version(self):
        return '1'

    def get_allinfo(self):
        return '1'

    def go(self, value):
        return self.fi.focus_go(value)
    
    def set_position(self, value):
        return self.fi.set_focus_position(value)

    def throttle_led(self, value):
        if value not in [1, 2, "OFF", "ON", "off", "on", "On", "Off"]:
            raise ValueError
        return True
