from DevicePackages.defaultConfigForDevices import LOGGER, defaultConfig
from DevicePackages.SiglentSPD3303XEPackage.SiglentSPD3303XEConnectionModule import ConnectionClass
import traceback
from DevicePackages.TestWidgedDataClass import TestWidgedDataClass


def exceptionCheck(func):
    def wrapper(*args, **kwargs):
        try:
            status,response = func(*args, **kwargs)
            return status,response
        except Exception as e:
            print(args)
            args[0].log.log(f"{args[0].config_dictionary['DEVICENAME']} - {traceback.format_exc()}","ERROR")
            return False, f"{e}"
    return wrapper


def checkIfBussy(func):
    def wrapper(*args, **kwargs):
        #args[0] is self from Connection Class
        if args[0].busy_flag == False:
            args[0].busy_flag = True
            status,response = func(*args, **kwargs)
            args[0].busy_flag = False
            return status,response
        else:
            args[0].log.log(f"{args[0].config_dictionary['DEVICENAME']} - Hardware is doing another Test, try again later","ERROR")
            return False,"BUSSY"
    return wrapper

class DeviceTestsClass():
    ################ INIT Function ##################
    def __init__(self,config_dictionary:dict = defaultConfig,\
        log:LOGGER = LOGGER("ALL"),\
        ConnectionClass :ConnectionClass = ConnectionClass,\
        *args, **kwargs):#args = multiple input variables, **kvargs multiple keyword variables (dictionaries, touples etc.)

        self.log = log
        self.ConnectionClass = ConnectionClass
        self.config_dictionary = config_dictionary
        self.busy_flag = False #to prevent multiple tests at the same time from another thread
        self.PASS_STRING = "PASS"
        self.FAIL_STRING = "FAIL"

    @checkIfBussy
    @exceptionCheck
    def doTest(self, test_command:list, widget_data:TestWidgedDataClass = TestWidgedDataClass()):  
        """ Provides a test, returns boolean result, raw response and updates th TestWidgetData Class inserted into this Function"""
        if self.config_dictionary["COMMUNICATION"] == "VIRTUAL":
            self.log.log(f"{self.config_dictionary['DEVICENAME']} - Doing virtual Test","WARNING")
            widget_data.loadVirtualDataToItself()
            return True,"VIRTUAL"

        if type(test_command) != list: #in case of simple string
            test_command = test_command.split(" ")

        if test_command[0].upper() == "QUERYCOMMAND":
            status,response = self._queryCommand(test_command[1:])
            widget_data.loadDataBasedOnStatus(status)
            return status,response
        
        elif test_command[0] == "WRITECOMMAND":
            status,response = self._writeCommand(test_command[1:])
            widget_data.loadDataBasedOnStatus(status)
            return status,response
        
        elif test_command[0] == "READALL":
            status,response= self._readAll()
            widget_data.loadDataBasedOnStatus(status)
            return status,response


        #for EX: CH1 SET Voltage 1.2
        #    EX: CH2 READ Current
        channel = test_command[0]
        set_read_selection = test_command[1]

        if str(set_read_selection).upper() == "SET":
            match str(test_command[2]).upper(): #and ignore case sensitivity
                case "VOLTAGE":
                    status,response = self._setVoltage(channel,test_command[3:])
                
                case "CURRENT":
                    status,response = self._setCurrent(channel,test_command[3:])

                case "OUTPUT":
                    status,response = self._setOutput(channel,test_command[3:])

                case _:
                    self.log.log(f"{self.config_dictionary['DEVICENAME']} - Unsupported Command: {' '.join(test_command)}","ERROR")
                    response = "UNSUPPORTED COMMAND"
                    status = False
                
            widget_data.loadDataBasedOnStatus(status)
            return status,response
        
        elif str(set_read_selection).upper() == "READ":
            match str(test_command[2]).upper(): #and ignore case sensitivity
                case "VOLTAGE":
                    status,response = self._readVoltage(channel)
                case "CURRENT":
                    status,response = self._readCurrent(channel)
                case "OUTPUT":
                    status,response = self._readOutput(channel)
                case _:
                    self.log.log(f"{self.config_dictionary['DEVICENAME']} - Unsupported Command: {' '.join(test_command)}","ERROR")
                    response = "UNSUPPORTED COMMAND"
                    status = False

            widget_data.loadDataBasedOnStatus(status)
            return status,response
        
        elif str(set_read_selection).upper() == "MEASURE":
            match str(test_command[2]).upper(): #and ignore case sensitivity
                case "VOLTAGE":
                    status,response = self._measVoltage(channel)
                case "CURRENT":
                    status,response = self._measCurrent(channel)
                case "POWER":
                    status,response = self._measPower(channel)
                case _:
                    self.log.log(f"{self.config_dictionary['DEVICENAME']} - Unsupported Command: {' '.join(test_command)}","ERROR")
                    response = "UNSUPPORTED COMMAND"
                    status = False

            widget_data.loadDataBasedOnStatus(status)
            return status,response
        
        else:
            self.log.log(f"{self.config_dictionary['DEVICENAME']} - Unsupported Command: {' '.join(test_command)}","ERROR")
            response = "UNSUPPORTED COMMAND"
            status = False
            widget_data.loadDataBasedOnStatus(status)
            return status,response

    def _queryCommand(self,test_commands:list):
        command_to_send = " ".join(test_commands)
        self.log.log(f"{self.config_dictionary['DEVICENAME']}:: - Sending command: {command_to_send}","INFO")
        status, response = self.ConnectionClass.queryMessage(command_to_send)
        return status,response
    
    def _writeCommand(self,test_commands:list):
        command_to_send = " ".join(test_commands)
        self.log.log(f"{self.config_dictionary['DEVICENAME']}:: - Sending command: {command_to_send}","INFO")
        status,response = self.ConnectionClass.sendMessage(command_to_send)
        return status,response

    def _setVoltage(self,channel,test_commands:list):
        message_to_send = f"{channel}:VOLTage {test_commands[0]}"
        status,response = self.ConnectionClass.sendMessage(message_to_send)
        return status,response
    
    def _setCurrent(self,channel,test_commands:list) -> str:
        message_to_send = f"{channel}:CURRent {test_commands[0]}"
        status,response = self.ConnectionClass.sendMessage(message_to_send)
        return status,response
    
    def _setOutput(self,channel,test_commands:list) -> str:
        message_to_send = f"OutPut {channel},{test_commands[0]}"
        status,response = self.ConnectionClass.sendMessage(message_to_send)
        return status,response
    

    def _readVoltage(self,channel):
        message_to_send = f"{channel}:VOLTage?"
        status,response = self.ConnectionClass.queryMessage(message_to_send)
        return status,response

    
    def _readCurrent(self,channel) -> str:
        message_to_send = f"{channel}:CURRent?"
        status,response = self.ConnectionClass.queryMessage(message_to_send)
        return status,response
    
    def _readOutput(self,channel) -> str:
        message_to_send = f"System:Status?"
        status,response = self.ConnectionClass.queryMessage(message_to_send)
        #response is in form of HEX like this 0x5, CH1 output is stored in the 4th bit
        #CH2 is stored in 5th bit
        #convert HEX to binary
        binary_expression = bin(int(response,16)).replace("0b", "").zfill(10)
        if channel == "CH1":
            response = binary_expression[5]
        elif channel == "CH2":
            response = binary_expression[4]
        response = response.replace("0","OFF").replace("1","ON")
        return status,response

    def _readAll(self) -> str:
        message_to_send = f"*READALL?"
        status,response = self.ConnectionClass.queryMessage(message_to_send)
        return status,response


    def _measVoltage(self,channel):
        message_to_send = f"MEASure:VOLTage? {channel}"
        status,response = self.ConnectionClass.queryMessage(message_to_send)
        return status,response
    
    def _measCurrent(self,channel) -> str:
        message_to_send = f"MEASure:CURRent? {channel}"
        status,response = self.ConnectionClass.queryMessage(message_to_send)
        return status,response
    
    def _measPower(self,channel) -> str:
        message_to_send = f"MEASure:POWEr? {channel}"
        status,response = self.ConnectionClass.queryMessage(message_to_send)
        return status,response

