"""
Binho Pulsar Python package

This Python package is developed to control the new Pulsar USB Host adapter.
The new Binho Pulsar device is an USB HID device that is controlled using a set
of USB commands that are transferred over USB HID Interrupt transfers.
"""
# Import drivers.
from .supernovasdk.binhosupernova.supernova import Supernova

# Import USB HID commands library.
from .commands.gpio.definitions import *
from .commands.i2c.definitions import *
from .commands.spi.definitions import *
from .commands.uart.definitions import *
from .commands.system.definitions import *

# Import other modules
from .supernovasdk.binhosupernova.utils.system_message import *

class Pulsar():
    """
    This class represents the Binho Pulsar device.

    It involves the drivers used to communicate with the device over USB HID,
    and the different processes that control the commands request and responses.
    """

    def __init__(self):
        """
        Constructor defined to add the Pulsar attributes and initialize them as None.
        """
        self.__driver = Supernova() # We utilize the supernova class as low level driver, since its API and pulsars is very similar

    # This function tries to hotfix a hardcoded string used in a wrapper in the Supernova class.
    def __error_message_parser_wrapper(func):
        def __check_error_message(self, *args, **kwargs):
            result = func(self, *args, **kwargs)
            if result["opcode"] == SystemOpcode.CONFIGURATION_ERROR.value:
                result["message"] = "Pulsar not configured appropriately, please check if connection is open and callback is set"
            return result
        return __check_error_message

    # Public methods ----------------------------------------------------------------------------------

    def open(self, serial: int = None, path: str = None) -> dict:
        """
        This method establishes the USB communication with the Pulsar. When this method is invoked, the USB Host starts to send USB frames to the
        USB device, and to poll the Input endpoint.

        By default, the usb hid manager, opened connection with the first Pulsar listed based on the
        VID and PID. Optionally, the serial number or the OS path string can be used to identify the
        device, so passing the serial number or the path only is enough to connect with the desired device.

        To get the device path or serial number, the BinhoPulsar.getConnectedPulsarDevicesList() method
        can be invoked.

        Parameters
        ----------
        serial : int
            USB device serial number. By default is None.
        
        path : str
            String path generated by the OS that can be read using the BinhoPulsar.getConnectedPulsarDevicesList() method.

        Returns
        -------
        dict:
            A python dictionary representation of a SystemMessage instance. The opcode of the message is:
            - SystemOpcode.OK if the connection with the HID device is completed successfully.
            - SystemOpcode.OPEN_CONNECTION_FAIL if the connection of the HID instance fails.

        """

        result = self.__driver.open(serial=serial, path=path)

        if result["opcode"] == SystemOpcode.OK.value:
            result["message"] = "Connection with Pulsar device opened successfully."

        return result

    def close(self) -> dict:
        """
        This method closes the communication with the Pulsar and releases the used memory.
        
        Returns
        -------
        dict
            A python dictionary representation of a SystemMessage instance. The opcode of the message is:
            - SystemOpcode.OK if the connection with the HID device is closed successfully.
            - SystemOpcode.OPEN_CONNECTION_REQUIRED if the was not previously opened.

        """

        result = self.__driver.close()

        if result["opcode"] == SystemOpcode.OPEN_CONNECTION_REQUIRED.value:
            result["message"] = "It is required to open connection with a Pulsar first. Invoke open() method."
        
        return result

    def onEvent(self, callback_func) -> dict:
        """
        This method registers the callback invoked every time a new USB response or notification
        arrives from the USB device. The callback function must implement the following signature:

            def callback(pulsar_response, system_message) -> None:

        It's important to note that the callback is called directly. If the callback function
        introduces a delay, it can impact the receiving mechanism. Developers using this SDK are
        advised to implement a queuing mechanism to quickly liberate the SDK as soon as the callback
        is called. This approach helps in managing responses efficiently without blocking the SDK's
        processing capabilities.

        Parameters
        ----------
        callback_func : function
            Callback function that will be invoked every time a new USB response or notification is
            sent by the USB device.

        Returns
        -------
        dict
            The SystemMessage in dictionary format.

        """

        def wrapped_callback_func(msg, sys):  
            if sys and sys.opcode == SystemOpcode.UNEXPECTED_DISCONNECTION:  
                sys.message =  f"Error {SystemOpcode.UNEXPECTED_DISCONNECTION.name}: Unexpected Pulsar disconnection."  
            callback_func(msg, sys)

        # this is to ensure the core callback is appropriate before setting.
        result = self.__driver.onEvent(callback_func=callback_func)  

        if result["opcode"] == SystemOpcode.INVALID_CALLBACK_SIGNATURE.value:  
            result["message"] = "The function must accept 2 Parameters: callback(pulsar_response, system_message)."  
            return result  

        # if core callback did not error, we can set the wrapper over it
        result = self.__driver.onEvent(callback_func=wrapped_callback_func)  
        
        return result

    # -----------------------------------------------------------------------------------
    # Communication API - USB commands
    # -----------------------------------------------------------------------------------

    # Get USB strings -------------------------------------------------------------------
    
    @__error_message_parser_wrapper
    def getUsbString(self, id: int, subCommand : GetUsbStringSubCommand) -> dict:

        """
        This function sends a Get USB String command taking the subcommand
        passed as parameter. The list of subcommands:

        GetUsbStringSubCommand.MANUFACTURER - Returns the manufacturer string ("Binho").
        GetUsbStringSubCommand.PRODUCT_NAME - Returns the product name string ("Binho Pulsar").
        GetUsbStringSubCommand.SERIAL_NUMBER - Returns the product serial number. Up to 32 characters.
        GetUsbStringSubCommand.FW_VERSION - Returns the product firmware version (VX.X.X).
        GetUsbStringSubCommand.HW_VERSION - Returns the product hardware version (HW-X).
        GetUsbStringSubCommand.BT_VERSION - Returns the product bootloader version (VX.X.X).

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The range allowed is [1, 65535].

        subCommand : GetUsbStringSubCommand
            Subcommand that indicates what string must be retrieved.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.getUsbString(id=id, subCommand=subCommand)

    @__error_message_parser_wrapper
    def resetDevice(self, id: int) -> dict:
        """
        This function sends a RESET DEVICE command.

        Parameters
        ----------
        id : c_uint16
            A 2-byte unsigned integer representing the transfer ID. The range allowed is [1, 65535].

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.  
        """
        return self.__driver.resetDevice(id=id)

    @__error_message_parser_wrapper
    def enterBootMode(self, id: int) -> dict:
        """
        This function sends a ENTER BOOT MODE command.

        Parameters
        ----------
        id : c_uint16
            A 2-byte unsigned integer representing the transfer ID. The range allowed is [1, 65535].

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.  
        """
        
        return self.__driver.enterBootMode(id=id)
    
    @__error_message_parser_wrapper
    def enterIspMode(self, id: int) -> dict:
        """
        This method sends a ENTER_ISP_MODE command

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The allowed range is [1, 65535].

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """

        return self.__driver.enterIspMode(id=id)

    # Set I2C SPI UART Bus Voltage strings -------------------------------------------------------------------

    @__error_message_parser_wrapper
    def setI2cSpiUartGpioVoltage(self, id: int, voltage_mV : c_uint16) -> dict:
        """
        This method sends a SET_I2C_SPI_UART_GPIO_VOLTAGE command taking desired bus voltage

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The allowed range is [1, 65535].

        voltage_mV : c_uint16
            It is a 2-bytes integer that indicates the I2C, SPI, UART and GPIO operating voltage. The allowed range is [1200, 3300] mV
            and the value 0 mV to power off the output voltage.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.setI2cSpiUartGpioVoltage(id=id, voltage_mV=voltage_mV)

    @__error_message_parser_wrapper
    def getAnalogMeasurements(self, id: int) -> dict:
        """
        This method sends a GET_ANALOG_MEASUREMENTS command and retrieves the analog measures in mV.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The allowed range is [1, 65535].

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.getAnalogMeasurements(id=id)

    @__error_message_parser_wrapper    
    def useExternalI2cSpiUartGpioVoltage(self, id: int) -> dict:
        """
        This method sends a USE_EXT_SRC_I2C_SPI_UART_GPIO_VOLTAGE command, sets the bus voltage to a measured value and
        retrieves the analog signal measure in mV.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The allowed range is [1, 65535].

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.useExternalI2cSpiUartGpioVoltage(id=id)
    
    @__error_message_parser_wrapper
    def getDeviceInfo(self, id: int) -> dict:
        """
        This function sends a GET_DEVICE_INFO command to the device. The device returns a dictionary
        with its information.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The range allowed is [1, 65535].

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.getDeviceInfo(id=id)

    # I2C management --------------------------------------------------------------------

    @__error_message_parser_wrapper
    def i2cControllerInit(self, id: int, busId: I2cBus, frequency: int, pullUpResistorsValue: I2cPullUpResistorsValue) -> dict:
        """
        This method initialize the I2C bus in controller mode with
        the frequency and pull up resistors value selected.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The range allowed is [1, 65535].
        
        busId : I2cBus
            The I2C bus identifier.

        frequency : int
            This parameter represents the I2C SCL frequency in Hz. Currently, the minimum allowed value
            is 100000 Hz and the maximum allowed value is 1000000 Hz.

        pullUpResistorsValue : I2cPullUpResistorsValue
            Value of the pull up enum that represents the desired Pull Up resistors value

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.i2cControllerInit(id=id, busId=busId, frequency=frequency, pullUpResistorsValue=pullUpResistorsValue)

    @__error_message_parser_wrapper
    def i2cControllerSetParameters(self, id: int, busId: I2cBus, frequency: int, pullUpResistorsValue: I2cPullUpResistorsValue) -> dict:
        """
        This method sets the I2C transfers baudrate and pull up resistors value for the SDA and SCL lines.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The range allowed is [1, 65535].
        
        busId : I2cBus
            The I2C bus identifier.

        frequency : int
            This parameter represents the I2C SCL frequency in Hz. Currently, the minimum allowed value
            is 100000 Hz and the maximum allowed value is 1000000 Hz.
        
        pullUpResistorsValue : I2cPullUpResistorsValue
            Value of the pull up enum that represents the desired Pull Up resistors value

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.i2cControllerSetParameters(id=id, busId=busId, frequency=frequency, pullUpResistorsValue=pullUpResistorsValue)

    @__error_message_parser_wrapper
    def i2cSetPullUpResistors(self, id: int, busId: I2cBus, pullUpResistorsValue: I2cPullUpResistorsValue) -> dict:
        """
        This method sets the I2C pull up resistors for the SDA and SCL lines.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The range allowed is [1, 65535].

        busId : I2cBus
            The I2C bus identifier.

        pullUpResistorsValue : I2cPullUpResistorsValue Enum
            This parameter represents the different values for the pull up resistors.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.i2cSetPullUpResistors(id=id, busId=busId, pullUpResistorsValue=pullUpResistorsValue)

    @__error_message_parser_wrapper
    def i2cControllerWrite(self, id: int, busId: I2cBus, targetAddress : int, registerAddress: list, data: list, isNonStop: bool = False, is10BitTargetAddress: bool = False) -> dict:
        """
        This method is used to request to the the Supernova device to perform an I2C write transfer. The
        I2C write transfer starts with a START condition and ends with a STOP condition. To perform a write
        transfer without the STOP condition, the nonStop parameter must be set to True.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The allowed range is [1, 65535].

        busId : I2cBus
            The I2C bus identifier.

        targetAddress : byte
            I2C slave static address.

        registerAddress : list
            Python list that contains the memory/register address of the I2C slave internal memory, whose data
            will be written. The list holds bytes, and can hand hold from 0 bytes up to 4 bytes. 0 bytes means
            that the list can be left empty and the Supernova will ignore it and write only the data payload.

        data : list
            Python list that contains the I2C data transferred in the I2C Write transfer. The list holds
            bytes elements, and the maximum length is 1024 bytes.
        
        isNonStop : bool
            This parameter indicates if the I2C write transfer is non-stop, meaning that it ends without a STOP 
            condition. If the flag is set to False, the transfer ends with STOP; otherwise, if it's True, the 
            transfer is non-stop, without a STOP condition at the end.

        is10BitTargetAddress : bool
            This parameter indicates if the target address is a 10-bit address. If the flag is set to False, the
            target address is a 7-bit address; otherwise, if it's True, the target address is a 10-bit address.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.i2cControllerWrite(id=id, busId=busId, targetAddress=targetAddress, registerAddress=registerAddress, data=data, isNonStop=isNonStop, is10BitTargetAddress=is10BitTargetAddress)

    @__error_message_parser_wrapper
    def i2cControllerRead(self, id: int, busId: I2cBus, targetAddress: int, requestDataLength: int, registerAddress: list = [], is10BitTargetAddress: bool = False) -> dict:
        """
        This method is used to request to the the USB device to perform an I2C read transfer. The
        I2C read transfer starts with a START condition and ends with a STOP condition.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The allowed range is [1, 65535].

        busId : I2cBus
            The I2C bus identifier.

        targetAddress : byte
            I2C target static address.

        requestDataLength : int
            Length of the read data. The maximum value is 1024 bytes.
        
        registerAddress : list
            Python list that contains the memory/register address of the I2C target internal memory, whose data
            will be read. The list holds bytes, and can hand hold from 0 bytes up to 4 bytes. 0 bytes means
            that the list can be left empty, the Supernova will ignore it and will perform only a read transfer.

        is10BitTargetAddress : bool
            This parameter indicates if the target address is a 10-bit address. If the flag is set to False, the
            target address is a 7-bit address; otherwise, if it's True, the target address is a 10-bit address.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """

        return self.__driver.i2cControllerRead(id=id, busId=busId, targetAddress=targetAddress, requestDataLength=requestDataLength, registerAddress=registerAddress, is10BitTargetAddress=is10BitTargetAddress)
    
    @__error_message_parser_wrapper
    def i2cControllerScanBus(self, id: int, busId: I2cBus, include10BitAddresses: bool = False) -> dict:
        """
        This method is used to request to the the USB device to perform an I2C scan of the I2C bus.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The allowed range is [1, 65535].

        busId : I2cBus
            The I2C bus identifier.
        
        include10BitAddresses : bool
            This parameter indicates if the scan must include 10-bit addresses. If the flag is set to False, the
            scan will not include 10-bit addresses; otherwise, if it's True, the scan will include 10-bit addresses.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """

        return self.__driver.i2cControllerScanBus(id=id, busId=busId, include10BitAddresses=include10BitAddresses)

    # --------------------------UART management --------------------------------------------------------------------

    # ------------------------------UART INIT ----------------------------#

    @__error_message_parser_wrapper
    def uartInit(self, id: int, baudrate: UartBaudRate, hardwareHandshake:bool, parityMode:UartParity, dataSize:UartDataSize, stopBit: UartStopBit ) -> dict:
        """
        This method initializes the UART peripherals

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represents the transfer id. The range allowed is [1, 65535].

        baudrate : UartBaudRate
            This parameter represents the UART TX and RX frequency from the options provided by the UartBaudRate enum.
            The frequency goes from 600bps to up to 115200bps.

        hardwareHandshake : bool
            This parameter represents a boolean flag to enable or disable this option.

        parityMode: UartParity
            This parameter represents the different parity modes available in the UartParity enum.
            The parity modes are: none, even or odd.

        dataSize: UartDataSize
            This parameter represents the different data sizes available in the UartDataSize enum.
            The data sizes are either 7 or 8.

        stopBit: UartStopBit
            This parameter represent the different stop bit configuration available in the UartStopBit enum.
            The stop bit can be of size 1 or 2.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """

        return self.__driver.uartInit(id=id, baudrate=baudrate, hardwareHandshake=hardwareHandshake, parityMode=parityMode, dataSize=dataSize, stopBit=stopBit)
    # ------------------------------UART SET ----------------------------#

    @__error_message_parser_wrapper
    def uartSetParameters(self, id: int, baudrate: UartBaudRate, hardwareHandshake:bool, parityMode:UartParity, dataSize:UartDataSize, stopBit: UartStopBit ) -> dict:
        """
        This method sets the UART peripheral parameters

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The range allowed is [1, 65535].

        baudrate : UartBaudRate
            This parameter represents the UART TX and RX frequency from the options provided by the UartBaudRate enum.
            The frequency goes from 600bps to up to 115200bps.

        hardwareHandshake : bool
            This parameter represents a boolean flag to enable or disable this option.

        parityMode: UartParity
            This parameter represents the different parity modes available in the UartParity enum.
            The parity modes are: none, even or odd.

        dataSize: UartDataSize
            This parameter represents the different data sizes available in the UartDataSize enum.
            The data sizes are either 7 or 8.

        stopBit: UartStopBit
            This parameter represent the different stop bit configuration available in the UartStopBit enum.
            The stop bit can be of size 1 or 2.


        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """

        return self.__driver.uartSetParameters(id=id, baudrate=baudrate, hardwareHandshake=hardwareHandshake, parityMode=parityMode, dataSize=dataSize, stopBit=stopBit)
    
    # ------------------------------UART SEND ----------------------------#

    @__error_message_parser_wrapper
    def uartSendMessage(self, id: int, data: list) -> dict:
        """
        This method is used to request to the the Pulsar device to perform an UART send transfer.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represent the transfer id. The range allowed is [1, 65535].

        data : list
            Python list that contains the data transferred in the UART Send. The list holds
            bytes elements, and the maximum length is 1024 bytes.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        
        return self.__driver.uartSendMessage(id=id, data=data)
    
    # SPI management --------------------------------------------------------------------

    @__error_message_parser_wrapper               
    def spiControllerInit(self,
                          id: int,
                          bitOrder: SpiControllerBitOrder,
                          mode: SpiControllerMode,
                          dataWidth: SpiControllerDataWidth,
                          chipSelect: SpiControllerChipSelect,
                          chipSelectPol: SpiControllerChipSelectPolarity,
                          frequency: c_uint32) -> dict:
        """
        This method initializes the SPI peripheral with the specified configuration.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represents the transfer id. The range allowed is [1, 65535].

        bitOrder : SpiControllerBitOrder
            Sets the bit order of the SPI peripheral, could be SpiControllerBitOrder.MSB or SpiControllerBitOrder.LSB.
        
        mode : SpiControllerMode
            Sets the SPI mode, could be SpiControllerMode.MODE_0, SpiControllerMode.MODE_1, SpiControllerMode.MODE_2 or SpiControllerMode.MODE_3.
        
        dataWidth : SpiControllerDataWidth
            Sets the SPI data width, could be SpiControllerDataWidth._8_BITS_DATA, SpiControllerDataWidth._16_BITS_DATA.

        chipSelect : SpiControllerChipSelect
            Sets the SPI chip select, could be SpiControllerChipSelect.CHIP_SELECT_0, SpiControllerChipSelect.CHIP_SELECT_1,
            SpiControllerChipSelect.CHIP_SELECT_2 or SpiControllerChipSelect.CHIP_SELECT_3.

        chipSelectPol : SpiControllerChipSelectPolarity
            Sets the SPI chip select polarity, could be SpiControllerChipSelectPolarity.ACTIVE_LOW or SpiControllerChipSelectPolarity.ACTIVE_HIGH.
        
        frequency : c_uint32
            Sets the SPI Clock frequency in Hz. Currently, the minimum allowed value is 10000 Hz and the maximum allowed value is 50000000 Hz.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        return self.__driver.spiControllerInit(id=id, bitOrder=bitOrder, mode=mode, dataWidth=dataWidth, chipSelect=chipSelect, chipSelectPol=chipSelectPol, frequency=frequency)

    @__error_message_parser_wrapper
    def spiControllerSetParameters(self,
                                   id: int,
                                   bitOrder: SpiControllerBitOrder,
                                   mode: SpiControllerMode,
                                   dataWidth: SpiControllerDataWidth,
                                   chipSelect: SpiControllerChipSelect,
                                   chipSelectPol: SpiControllerChipSelectPolarity,
                                   frequency: c_uint32) -> dict:
        """
        This method sets the SPI peripheral configuration.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represents the transfer id. The range allowed is [1, 65535].

        bitOrder : SpiControllerBitOrder
            Sets the bit order of the SPI peripheral, could be SpiControllerBitOrder.MSB or SpiControllerBitOrder.LSB.
        
        mode : SpiControllerMode
            Sets the SPI mode, could be SpiControllerMode.MODE_0, SpiControllerMode.MODE_1, SpiControllerMode.MODE_2 or SpiControllerMode.MODE_3.
        
        dataWidth : SpiControllerDataWidth
            Sets the SPI data width, could be SpiControllerDataWidth._8_BITS_DATA, SpiControllerDataWidth._16_BITS_DATA.

        chipSelect : SpiControllerChipSelect
            Sets the SPI chip select, could be SpiControllerChipSelect.CHIP_SELECT_0, SpiControllerChipSelect.CHIP_SELECT_1,
            SpiControllerChipSelect.CHIP_SELECT_2 or SpiControllerChipSelect.CHIP_SELECT_3.

        chipSelectPol : SpiControllerChipSelectPolarity
            Sets the SPI chip select polarity, could be SpiControllerChipSelectPolarity.ACTIVE_LOW or SpiControllerChipSelectPolarity.ACTIVE_HIGH.
        
        frequency : c_uint32
            Sets the SPI Clock frequency in Hz. Currently, the minimum allowed value is 10000 Hz and the maximum allowed value is 50000000 Hz.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        return self.__driver.spiControllerSetParameters(id=id, bitOrder=bitOrder, mode=mode, dataWidth=dataWidth, chipSelect=chipSelect, chipSelectPol=chipSelectPol, frequency=frequency)

    @__error_message_parser_wrapper
    def spiControllerTransfer(self, id: int, transferLength: int, payload: list) -> dict:
        """
        This method performs a SPI transfer.
        
        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represents the transfer id. The range allowed is [1, 65535].

        transferLength : int
            It is a 2-bytes integer that represents the transfer length. The range allowed is [1, 1024].

        payload : list  
            Python list that contains the SPI data transferred. The list holds bytes elements and
            the maximum length is 1024 bytes.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        return self.__driver.spiControllerTransfer(id=id, transferLength=transferLength, payload=payload)

    # GPIO management --------------------------------------------------------------------

    @__error_message_parser_wrapper
    def gpioConfigurePin(self, id: int, pinNumber: GpioPinNumber, functionality: GpioFunctionality, initialOutputLogicLevel: GpioLogicLevel = GpioLogicLevel.LOW) -> dict:
        """
        This method configures a GPIO pin with the specified functionality.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represents the transfer id. The range allowed is [1, 65535].

        pinNumber : GpioPinNumber
            An enum representing the GPIO pin number to configure.

        functionality : GpioFunctionality
            An enum representing the desired functionality for the GPIO pin.
        
        initialOutputLogicLevel : GpioLogicLevel
            An enum representing the initial output logic level for the GPIO pin. This parameter is optional
            and only required when the GPIO pin is configured as an output.
            This feature is reserved for future use.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        return self.__driver.gpioConfigurePin(id=id, pinNumber=pinNumber, functionality=functionality)

    @__error_message_parser_wrapper
    def gpioDigitalWrite(self, id: int, pinNumber: GpioPinNumber, logicLevel: GpioLogicLevel) -> dict:
        """
        This method writes a digital logic level to a GPIO pin.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represents the transfer id. The range allowed is [1, 65535].

        pinNumber : GpioPinNumber
            An enum representing the GPIO pin number to write to.

        logicLevel : GpioLogicLevel
            An enum representing the logic level to write to the GPIO pin.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        return self.__driver.gpioDigitalWrite(id=id, pinNumber=pinNumber, logicLevel=logicLevel)

    @__error_message_parser_wrapper    
    def gpioDigitalRead(self, id: int, pinNumber: GpioPinNumber) -> dict:
        """
        This method reads the digital logic level from a GPIO pin.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represents the transfer id. The range allowed is [1, 65535].

        pinNumber : GpioPinNumber
            An enum representing the GPIO pin number to read from.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        return self.__driver.gpioDigitalRead(id=id, pinNumber=pinNumber)

    @__error_message_parser_wrapper    
    def gpioSetInterrupt(self, id: int, pinNumber: GpioPinNumber, trigger: GpioTriggerType) -> dict:
        """
        This method sets an interruption to a GPIO pin.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represents the transfer id. The range allowed is [1, 65535].

        pinNumber : GpioPinNumber
            An enum representing the GPIO pin number to read from.

        trigger : GpioTriggerType
            The trigger type used for the interruption. Must be one of the options provided by the GpioTriggerType enum.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        return self.__driver.gpioSetInterrupt(id=id, pinNumber=pinNumber, trigger=trigger) 

    @__error_message_parser_wrapper    
    def gpioDisableInterrupt(self, id: int, pinNumber: GpioPinNumber) -> dict:
        """
        This method disables interruptions of a GPIO pin.

        Parameters
        ----------
        id : int
            It is a 2-bytes integer that represents the transfer id. The range allowed is [1, 65535].

        pinNumber : GpioPinNumber
            An enum representing the GPIO pin number to read from.

        Returns
        -------
        dict
            Return the SystemMessage in dictionary format.

        """
        return self.__driver.gpioDisableInterrupt(id=id, pinNumber=pinNumber)