from DevicePackages.defaultConfigForDevices import LOGGER, defaultConfig
from DevicePackages.ControlBoardPackage.ControlBoardConnectionModule import ConnectionClass
from DevicePackages.ControlBoardPackage.testClassFor80pinCardModule import IO80pinCardTestClass
from DevicePackages.TestWidgedDataClass import TestWidgedDataClass
import traceback
import time
from numpy import arange


def exceptionCheck(func):
    """Decorator to catch exceptions and log them to the log file"""
    def wrapper(*args, **kwargs):
        try:
            print(*args)
            print(**kwargs)
            status,response = func(*args, **kwargs)
            return status,response
        except Exception as e:
            #args[0] is self from DeviceTestsClass
            print(e)
            args[0].log.log(f"{args[0].config_dictionary['DEVICENAME']} - {traceback.format_exc()}","ERROR",oneline=False)
            return False, f"{e}"
    return wrapper


def checkIfBussy(func):
    """Decorator to check if the device is busy with another test,
    this check is propably not needed in test class, because is also checked in ConnectionClass at PHY level,
    but is here for future use"""
    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",oneline=False)
            return False,"BUSSY"
    return wrapper

class DeviceTestsClass():
    """ This class serves as higher layer for communication with the device. This function implements the test commands and tries to make
    commands unified for all devices.

    For its internal use it uses the ConnectionClass to communicate with the device. Meanwhile ConnectionClass provides from point of programmer view the lowest
    level (PHY) for communication with device. The DeviceTestClass is the application layer for the programmer.

    Also this class provides more complex functions composed with more than one command and etc...
    Complete overview of Avaiable commands can be found in avaiableCommands.xlsx and avaiableCommands.pdf in documentation folder.
    """
    ################ 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.Io80PinCardClass = IO80pinCardTestClass(log=self.log,ConnectionClass=self.ConnectionClass)

    def _checkResult(self,status, response):
        if status == True and str(response).startswith("OK;"):
            return "PASS"
        return "FAIL"

    @checkIfBussy
    @exceptionCheck
    def doTest(self, test_command:list,widget_data:TestWidgedDataClass = TestWidgedDataClass()):
        """This function parses the command and calls the appropriate function to do the test.
        
        INPUTS: test_command:list or str -> EX: ["80_PIN_IO_CARD","SET","PIN","OUTPUT","HIGH"] or "80_PIN_IO_CARD SET PIN OUTPUT HIGH"]
        
        OUTPUTS: status:bool, response:str, data: List[float] |str
        In case of status = True, response is the response from the device and data are parsed from result
        In case of status = False, result is the error message and data are None
        In case of status = True and result = "VIRTUAL", the test was done only in VIRTUAL mode, data are None
        """
        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(" ")


        #for EX: 80_PIN_IO_CARD SET PIN OUTPUT HIGH
        #    EX2: RELAY_EXT_2 SET PIN OUTPUT HIGH
        match test_command[0].upper():
            case "QUERYCOMMAND":
                status,response = self.ConnectionClass.queryMessage(" ".join(test_command[1:]))
                return status,response
            
            case "WRITECOMMAND":
                status,response = self.ConnectionClass.sendMessage(test_command[1:])
                return status,response
            
            case "READCOMMAND":
                status,response = self.ConnectionClass.readMessage() #SIMPLE READ
                return status,response
            
            case "80_PIN_IO_CARD" | "80PINIOCARD":
                status,response = self.Io80PinCardClass.doTest(test_command[1:])
                return status,response
            case _:
                return False, f"Command {test_command} not found"
        

    
