from PyQt6 import QtWidgets,QtCore
from PyQt6.QtCore import pyqtSignal,pyqtSlot
from LowLevelWidgetsPackage import defaultConfigForWidgets 
from LowLevelWidgetsPackage.AdvancedProgrammablePSUPackage.AdvancedProgrammablePSUGUI import Ui_AdvancedProgrammablePSU
import time
import traceback
class _PSU_status():
    def __init__(self,output:bool,voltage:float,current:float,voltage_readback:float,current_readback:float,OVP_value:float, OVP_state:bool, OCP_value:float, OCP_state:bool,channel:str) -> None:
        self.output = output
        self.voltage = voltage
        self.OVP_value = OVP_value
        self.OVP_state = OVP_state
        self.OCP_value = OCP_value
        self.OCP_state = OCP_state
        self.current = current
        self.voltage_readback = voltage_readback
        self.current_readback = current_readback
        self.channel_name= channel
        self.requested_command = ""


class SignalClass(QtCore.QObject):
    user_input_changed_signal = pyqtSignal(object, str) #_PSU_status, name of widget
    received_data_signal = pyqtSignal(object, str) #_PSU_status, name of widget
        
class AdvancedProgrammablePSUClass(QtWidgets.QWidget):
    def __init__(self,channel:str = "CH1", log:defaultConfigForWidgets.LOGGER = defaultConfigForWidgets.LOGGER("ALL"),*args, **kwargs):#args = multiple input variables, **kvargs multiple keyword variables (dictionaries, touples etc.)
    ################ INIT Function ##################
        super().__init__(*args, **kwargs)#super inherits all methods and properties from its parent  (QWIDGET classes)
        #Load UI forms creaated in QTdDesigner
        self.log = log
        self.log.log("STARTING AdvancedProgrammablePSU CLASS","UNNECESSARY")
        self.ui = Ui_AdvancedProgrammablePSU() #UI form generated by UI designe
        self.ui.setupUi(self)
        self._name = "SimpleProgrammablePSU"
        self.do_once_flag = False
        self.signals = SignalClass()

        self.widgetSTatus = _PSU_status(output=self.ui.che_output_enable.isChecked(),
                                        voltage=float(self.ui.dspin_voltage.value()),
                                        current=float(self.ui.dspin_current.value()),
                                        voltage_readback=float(self.ui.la_measured_voltage.text().replace(",",".").replace("V","")),
                                        current_readback=float(self.ui.la_measured_current.text().replace(",",".").replace("A","")),
                                        OVP_value=float(self.ui.dspin_OVP.value()),
                                        OVP_state=self.ui.che_OVP.isChecked(),
                                        OCP_value=float(self.ui.dspin_OCP.value()),
                                        OCP_state=self.ui.che_OCP.isChecked(),
                                        channel= channel)

        self.ui.pb_set_current.clicked.connect(self._setCurrentCallback)
        self.ui.pb_set_voltage.clicked.connect(self._setVoltageCallback)
        self.ui.pb_set_OCP.clicked.connect(self._setOCPCallback)
        self.ui.pb_set_OVP.clicked.connect(self._setOVPCallback)
        self.ui.che_OCP.clicked.connect(self._setOCPCallback)
        self.ui.che_OVP.clicked.connect(self._setOVPCallback)
        self.ui.che_output_enable.clicked.connect(self._outputEnableCallback)
        self.ui.pb_readback.clicked.connect(self._readCallback)
        self.signals.received_data_signal.connect(self._updateGUI)

        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self._askForValues)
        self.ui.che_continuous_measurement.stateChanged.connect(self._continuousTimer)
        self.ui.che_continuous_read.stateChanged.connect(self._continuousTimer)  
        self.thread_pool = QtCore.QThreadPool()

    def _continuousTimer(self):
        if self.ui.che_continuous_measurement.isChecked() or self.ui.che_continuous_read.isChecked():
            self.timer.start(200)
        else:
            self.timer.stop()
    
    def _askForValues(self):
        if self.ui.che_continuous_measurement.isChecked() == True and self.ui.che_continuous_read.isChecked() == False:
            self.widgetSTatus.requested_command = "MEASURE READBACKS"
    
        elif self.ui.che_continuous_measurement.isChecked() == False and self.ui.che_continuous_read.isChecked() == True:
            self.widgetSTatus.requested_command = "READ SETTINGS ONLY"

        elif self.ui.che_continuous_measurement.isChecked() == True and self.ui.che_continuous_read.isChecked() == True:
            self.widgetSTatus.requested_command = "READ ALL"

        self.signals.user_input_changed_signal.emit(self.widgetSTatus,self._name)

    def _dummyAskForValues(self):
        """THIS FUNCTION MUST BE CALLED AFTER SIGNALS OF PARENT WIDGETS ARE CONNECTED
        it forces the AdvancedProgrammablePSU to ask for values from the device"""
        self.widgetSTatus.requested_command = "READ ALL"
        self.signals.user_input_changed_signal.emit(self.widgetSTatus,self._name)

    @pyqtSlot(object,str)
    def _updateGUI(self,widgetStatus: _PSU_status, name:str):
        try: 
            self.log.log(f"{self._name} Received signal from {name}","SIGNAL")
            self.ui.la_measured_current.setText(str(widgetStatus.current_readback)+"A")
            self.ui.la_measured_voltage.setText(str(widgetStatus.voltage_readback)+"V")
            self.ui.la_OCP_value.setText(str(widgetStatus.OCP_value)+"A")
            self.ui.la_OVP_value.setText(str(widgetStatus.OVP_value)+"V")
            self.ui.che_output_enable.setChecked(int(str(widgetStatus.output).replace("ON","1").replace("OFF","0").replace("True","1").replace("False","0").replace("VIRTUAL","0")))
            self.ui.che_OVP.setChecked(int(str(widgetStatus.OVP_state).replace("ON","1").replace("OFF","0").replace("True","1").replace("False","0").replace("VIRTUAL","0")))
            self.ui.che_OCP.setChecked(int(str(widgetStatus.OCP_state).replace("ON","1").replace("OFF","0").replace("True","1").replace("False","0").replace("VIRTUAL","0")))

            #do this only on initialization....
            if self.do_once_flag == False or widgetStatus.requested_command == "READ ALL" or widgetStatus.requested_command == "READ SETTINGS ONLY":
                self.do_once_flag = True
                self.log.log(f"CHANNEL STATUS IS: {widgetStatus.output}","DEBUG")
                self.ui.dspin_current.setValue(float(str(widgetStatus.current).replace("VIRTUAL","0")))
                self.ui.dspin_voltage.setValue(float(str(widgetStatus.voltage).replace("VIRTUAL","0")))
                self.ui.dspin_OCP.setValue(float(str(widgetStatus.OCP_value).replace("VIRTUAL","0")))
                self.ui.dspin_OVP.setValue(float(str(widgetStatus.OVP_value).replace("VIRTUAL","0")))
                
            self.ui.pb_set_current.setEnabled(True)
            self.ui.pb_set_voltage.setEnabled(True)
            self.ui.che_output_enable.setEnabled(True)
            self.ui.che_continuous_measurement.setEnabled(True)
            self.ui.che_continuous_read.setEnabled(True)
            self.ui.che_OCP.setEnabled(True)
            self.ui.che_OVP.setEnabled(True)
        
        except Exception as e:
            self.log.log(f"ERROR in {self._name} _updateGUI: {e}","ERROR")
            self.log.log(traceback.format_exc(),"ERROR",oneline=False)
            self.ui.la_measured_current.setText("ERROR")
            self.ui.la_measured_voltage.setText("ERROR")
            self.ui.pb_set_current.setEnabled(False)
            self.ui.pb_set_voltage.setEnabled(False)
            self.ui.che_output_enable.setEnabled(False)
            self.ui.che_continuous_measurement.setEnabled(False)
            self.ui.che_continuous_read.setEnabled(False)
            self.ui.che_OCP.setEnabled(False)
            self.ui.che_OVP.setEnabled(False)

    
    def _setCurrentCallback(self):
        self.widgetSTatus.current = float(self.ui.dspin_current.text().replace(",","."))
        self.widgetSTatus.requested_command = "SET CURRENT"
        self.signals.user_input_changed_signal.emit(self.widgetSTatus,self._name)
        
    def _setOCPCallback(self):
        self.widgetSTatus.OCP_state = self.ui.che_OCP.isChecked()
        self.widgetSTatus.OCP_value = float(self.ui.dspin_OCP.text().replace(",","."))
        self.widgetSTatus.requested_command = "SET OCP"
        self.signals.user_input_changed_signal.emit(self.widgetSTatus,self._name)

    def _setOVPCallback(self):
        self.widgetSTatus.OVP_state = self.ui.che_OVP.isChecked()
        self.widgetSTatus.OVP_value = float(self.ui.dspin_OVP.text().replace(",","."))
        self.widgetSTatus.requested_command = "SET OVP"
        self.signals.user_input_changed_signal.emit(self.widgetSTatus,self._name)  
    
    def _setVoltageCallback(self):
        self.widgetSTatus.voltage = float(self.ui.dspin_voltage.text().replace(",","."))
        self.widgetSTatus.requested_command = "SET VOLTAGE"
        self.signals.user_input_changed_signal.emit(self.widgetSTatus,self._name)
    
    def _outputEnableCallback(self):
        self.widgetSTatus.output = self.ui.che_output_enable.isChecked()
        self.widgetSTatus.requested_command = "SET OUTPUT"
        self.signals.user_input_changed_signal.emit(self.widgetSTatus,self._name)

    def _readCallback(self):
        self.widgetSTatus.output = self.ui.che_output_enable.isChecked()
        self.widgetSTatus.requested_command = "READ ALL"
        self.signals.user_input_changed_signal.emit(self.widgetSTatus,self._name)

        
    def setName(self,name:str):
        #to distinguish between different widgets change self.name -> this name will be emit in signals
        self._name = name
        