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


def exceptionCheck(func):
    def wrapper(*args, **kwargs):
        try:
            status,result = func(*args, **kwargs)
            return status,result
        except Exception as e:
            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,result = func(*args, **kwargs)
            args[0].busy_flag = False
            return status,result
        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.initial_port_dict = {"EXT1":[0]*32,
                                  "EXT2":[0]*32,
                                  "EXT3":[0]*32,}

    @checkIfBussy
    @exceptionCheck
    def doTest(self, test_command:list):
        if self.config_dictionary["COMMUNICATION"] == "VIRTUAL":
            self.log.log(f"{self.config_dictionary['DEVICENAME']} - Doing virtual Test","WARNING")
            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,result = self._queryCommand(test_command[1:])
            return status,result
        
        elif test_command[0] == "WRITECOMMAND":
            status,result = self._writeCommand(test_command[1:])
            return status,result        

        match str(test_command[0]).upper(): #and ignore case sensitivity
            case "WRITECOMMAND":
                status,result = self._writeCommand(test_command[1:])
            case "QUERYCOMMAND":
                status,result = self._queryCommand(test_command[1:])

            case "SET":
                status,result = self._setFunctionsTest(test_command[1:])
            
            case "READ":
                status,result = self._readFunctionsTest(test_command[1:])

            case "MEASURE":
                status,result = self._measureFunctionsTest(test_command[1:])

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

    def _setFunctionsTest(self,test_commands:list):
        match str(test_commands[0]).upper():
            case "RELAY":
                status,result = self._relayTest(test_commands[1:])
            
            case "MULTIPLE_RELAYS" | "RELAYS":
                status,result = self._multipleRelaysTest(test_commands[1:])
            
            case _:
                self.log.log(f"{self.config_dictionary['DEVICENAME']} - Unsupported Command: {' '.join(test_commands)}","ERROR")
                status = False
                result = "UNSUPORTED_COMMAND"
           
        return status,result

    def _readFunctionsTest(self,test_commands:list):
        match str(test_commands[0]).upper():
            case "CONFIGURATION" | "CONFIG":
                status,result = self.ConnectionClass.queryMessage("IDENT")

            case _:
                status = False
                result = "UNSUPPORTED_COMMAND"
                self.log.log(f"{self.config_dictionary['DEVICENAME']} - Unsupported Command: {' '.join(test_commands)}","ERROR")
            
            
        return status,result

    def _measureFunctionsTest(self,test_commands:list):
        match str(test_commands[0]).upper():
            case "INPUT_PIN":
                status,result = self._inputPinTest(test_commands[1:])

            case "MULTIPLE_INPUT_PINS" | "INPUT_PINS":
                status,result = self._multipleInputPinsTest(test_commands[1:])

            case _:
                self.log.log(f"{self.config_dictionary['DEVICENAME']} - Unsupported Command: {' '.join(test_commands)}","ERROR")
                status = False
                result = "UNSUPORTED_COMMAND"
        return status,result
    

    def _inputPinTest(self,test_commands:list):
        pin_number = test_commands[0]
        status, response = self.ConnectionClass.queryMessage(f"READPIN;{pin_number}")
        if status == True:
            if response[2] == "1":
                result = f"Pin {pin_number}: HIGH"

            elif response[2] == "0":
                result = f"Pin {pin_number}: LOW"
            
            else:
                status = False
        else:
            result = response
            
        return status,result

    def _multipleInputPinsTest(self,test_commands:list):
        pins = test_commands[0].split(",")#pins: 3,5,7 -> read pins 3 5 and 7
        status,response = self.ConnectionClass.queryMessage(f"READPINS")#response: 0;0;1;1;0;0;1;0;ACK -> 8 pins + ACK
        if status == True:
            response_string = ""
            for pin in pins:
                
                if response[int(pin)] == "1":
                    response_string += f"Pin {pin}: HIGH, "
                else:
                    response_string += f"Pin {pin}: LOW, "

            return status,response_string[:-2]
        else:
            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,result = self.ConnectionClass.queryMessage(command_to_send)
        return status,result
    
    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,result = self.ConnectionClass.sendMessage(command_to_send)
        return status,result

    def _relayTest(self,test_commands:list) -> str:
        port = test_commands[0].replace("EXT","")
        relay_number = test_commands[1]
        if test_commands[2] == "ON":
            relay_command = "SETEXT" #Arrigo command which set EXTPORT
            self.initial_port_dict[f"EXT{port}"][int(relay_number)-1] = 1
        
        elif test_commands[2] == "OFF":
            relay_command = "RESETEXT"
            self.initial_port_dict[f"EXT{port}"][int(relay_number)-0] = 1

        command_to_send = f"{relay_command};{port};{relay_number}"
        self.log.log(f"{self.config_dictionary['DEVICENAME']}:: - Sending command: {command_to_send}","INFO")
        status,result = self.ConnectionClass.queryMessage(command_to_send)
        return status,result

    def _multipleRelaysTest(self,test_commands:list):
        port = test_commands[0].replace("EXT","")
        relay_command = "SETEXTPORT" #Arrigo command which set EXTPORT
        if test_commands[1] == "ALL":
            relay_numbers_list_from_command = [str(i) for i in range(1,33)] #create list of all relays
        else:    
            relay_numbers_list_from_command = test_commands[1].split(",") #those are only number of relays to be set not whole port
        #to be sure that only relays frome the commands will be affected we need to compare it with initial port dict
        if test_commands[2] == "ON":
            for relay_number in relay_numbers_list_from_command:
                self.initial_port_dict[f"EXT{port}"][int(relay_number)-1] = 1 #write 1 value into inital port

        if test_commands[2] == "OFF":
            for relay_number in relay_numbers_list_from_command: 
                self.initial_port_dict[f"EXT{port}"][int(relay_number)-1] = 0 #write 0 value into inital port;

        #create command from initial port dict
        command_to_send = f"{relay_command};{port};"
        for relay_number in self.initial_port_dict[f'EXT{port}']:
            command_to_send += f"{relay_number};"
            
        command_to_send = command_to_send[:-1] #remove last ";"
        self.log.log(f"{self.config_dictionary['DEVICENAME']}:: - Sending command: {command_to_send}","INFO")
        status, result = self.ConnectionClass.queryMessage(command_to_send)
        return status,result
        #if test_commands[2] == "OFF": #
        #    relay_command = "SETEXTPORT" #Arrigo command which set EXTPORT 
        #    for relay_number in relay_numbers_list:
#
        #command_to_send = f"{relay_command};{port};{';'.join(relay_numbers_list)}"




