from types import SimpleNamespace
from time import sleep
import numpy as np

from slic.core.adjustable import Adjustable, PVAdjustable, PVEnumAdjustable
from slic.core.device import Device
from slic.utils.hastyepics import get_pv as PV
from slic.devices.general.motor import Motor


class BerninaMono(Device):

    def __init__(self, ID, name="Bernina DCM", **kwargs):
        super().__init__(ID, name=name, **kwargs)

        self.theta  = Motor(ID + ":RX12")
        self.x      = Motor(ID + ":TX12")
        self.gap    = Motor(ID + ":T2")
        self.roll1  = Motor(ID + ":RZ1")
        self.roll2  = Motor(ID + ":RZ2")
        self.pitch2 = Motor(ID + ":RX2")

        self.energy = DoubleCrystalMonoEnergy(ID, name=name)



class DoubleCrystalMonoEnergy(Adjustable):

    def __init__(self, ID, name=None):
        self.wait_time = 0.1

        pvname_setvalue = "SAROP21-ARAMIS:ENERGY_SP"
        pvname_readback = "SAROP21-ARAMIS:ENERGY"
        pvname_moving   = "SAROP21-ODCM098:MOVING"
        pvname_stop     = "SAROP21-ODCM098:STOP.PROC"

        pv_setvalue = PV(pvname_setvalue)
        pv_readback = PV(pvname_readback)
        pv_moving   = PV(pvname_moving)
        pv_stop     = PV(pvname_stop)

        units = pv_readback.units
        super().__init__(ID, name=name, units=units)

        self.pvnames = SimpleNamespace(
            setvalue = pvname_setvalue,
            readback = pvname_readback,
            moving   = pvname_moving,
            stop     = pvname_stop
        )

        self.pvs = SimpleNamespace(
            setvalue = pv_setvalue,
            readback = pv_readback,
            moving   = pv_moving,
            stop     = pv_stop
        )


    def get_current_value(self):
        return self.pvs.readback.get()

    def set_current_value(self, value):
        self.pvs.setvalue.put(value)
        sleep(3)

    def set_target_value(self, value):
        self.set_current_value(value)
#        while abs(self.wait_for_valid_value() - value) > accuracy:
        while self.is_moving():
            sleep(self.wait_time)


    def wait_for_valid_value(self):
        val = np.nan
        while not np.isfinite(val):
            val = self.get_current_value()
        return val

    def is_moving(self):
        moving = self.pvs.moving.get()
        return bool(moving)

    def stop(self):
        self.pvs.stop.put(1)