import struct
from time import sleep

class u32_DeviceCapabilities:
    USAGE_ID = 0x32

    def __init__(self, axiom, read = True):
        self._axiom = axiom

        # Get the usage number from the axiom class
        self._usage_revision = self._axiom.get_usage_revision(self.USAGE_ID)

        # Initialise a buffer for the usage's contents to be read into and unpacked/packed
        self._usage_binary_data = [0] * self._axiom.get_usage_length(self.USAGE_ID)

        # To handle different usage revisions, there are different methods that handle
        # register initialisation (in this python class), dumping out the contents of
        # a usage and unpacking and packing the usage's variables back into binary formats.
        # Use the getattr() find find the appropiate methods that pertain the usage revision.
        self._init_registers   = getattr(self, "_init_registers_uifrev{}".format( self._usage_revision), None)
        self._print_registers  = getattr(self, "_print_registers_uifrev{}".format(self._usage_revision), None)
        self._unpack_registers = getattr(self, "_unpack_uifrev{}".format(         self._usage_revision), None)

        # Raise an exception if an unsupported version of the usage is found.
        if (self._init_registers   is None or 
            self._print_registers  is None or 
            self._unpack_registers is None):
            raise Exception("Unsupported revision of {}. Revision: {}".format(self.__class__.__name__, self._usage_revision))

        # Initialise the variables that are supported by this revision of the usage.
        # With new firmware, the usage could be up-revved and new methods will need
        # to be added
        self._init_registers()

        # Populate the registers by reading the device
        if read:
            self.read()

    def read(self):
        self._usage_binary_data = self._axiom.read_usage(self.USAGE_ID)
        self._unpack()

    def write(self, write_to_nvm = False):
        self.read()
        raise Exception("Cannot write to u33. u33 is a read-only usage.")

    def print(self):
        self._print_registers()

    def _unpack(self):
        self._unpack_registers()

    def _pack(self):
        self._pack_registers()

#region u33 Specific Methods
#endregion

#region uXX Usage Name Usage Revision 1
    def _init_registers_uifrev1(self):
        self.reg_max_cts_nodes             = 0
        self.reg_num_cts_channels          = 0
        self.reg_num_aux_channels          = 0
        self.reg_num_cts_driven_traces     = 0
        self.reg_num_aux_driven_traces     = 0
        self.reg_num_a_channels            = 0
        self.reg_num_b_channels            = 0
        self.reg_num_c_channels            = 0
        self.reg_num_d_channels            = 0
        self.reg_num_e_channels            = 0
        self.reg_num_f_channels            = 0
        self.reg_max_map_lengthbytes       = 0
        self.reg_max_baseline_length_bytes = 0
        self.reg_num_baselines             = 0
        self.reg_slave_i2c_supported       = 0
        self.reg_slave_spi_supported       = 0
        self.reg_slave_lin_supported       = 0
        self.reg_slave_usb_supported       = 0
        self.reg_slave_uart_supported      = 0
        self.reg_irq_notify_supported      = 0
        self.reg_external_sync_supported   = 0
        self.reg_master_i2c_supported      = 0
        self.reg_master_spi_supported      = 0
        self.reg_master_gpio_supported     = 0
        self.reg_num_user_gpios            = 0
        self.reg_khz_to_jump_thousandths   = 0

    def _unpack_uifrev1(self):
        #field0 = struct.unpack("<H", bytes(bytearray(self._usage_binary_data[0:2])))
        #self.reg_0 = (field0 & 0x0001)
        max_cts_nodes, num_cts_channels, num_aux_channels, \
            num_cts_driven_traces, num_aux_driven_traces, num_a_channels, num_b_channels, \
            num_c_channels, num_d_channels, num_e_channels, num_f_channels, max_map_lengthbytes, \
            max_baseline_length_bytes, num_baselines, _, interfaces, khz_to_jump_thousands = struct.unpack("<H10B2HBBHI", bytes(bytearray(self._usage_binary_data[0:24])))

        self.reg_max_cts_nodes             = max_cts_nodes
        self.reg_num_cts_channels          = num_cts_channels
        self.reg_num_aux_channels          = num_aux_channels
        self.reg_num_cts_driven_traces     = num_cts_driven_traces
        self.reg_num_aux_driven_traces     = num_aux_driven_traces
        self.reg_num_a_channels            = num_a_channels
        self.reg_num_b_channels            = num_b_channels
        self.reg_num_c_channels            = num_c_channels
        self.reg_num_d_channels            = num_d_channels
        self.reg_num_e_channels            = num_e_channels
        self.reg_num_f_channels            = num_f_channels
        self.reg_max_map_lengthbytes       = max_map_lengthbytes
        self.reg_max_baseline_length_bytes = max_baseline_length_bytes 
        self.reg_num_baselines             = num_baselines

        self.reg_slave_i2c_supported       = (interfaces & 0x0001) >>  0
        self.reg_slave_spi_supported       = (interfaces & 0x0002) >>  1
        self.reg_slave_lin_supported       = (interfaces & 0x0004) >>  2
        self.reg_slave_usb_supported       = (interfaces & 0x0008) >>  3
        self.reg_slave_uart_supported      = (interfaces & 0x0010) >>  4
        self.reg_irq_notify_supported      = (interfaces & 0x0020) >>  5
        self.reg_external_sync_supported   = (interfaces & 0x0040) >>  6
        self.reg_master_i2c_supported      = (interfaces & 0x0080) >>  7
        self.reg_master_spi_supported      = (interfaces & 0x0100) >>  8
        self.reg_master_gpio_supported     = (interfaces & 0x0200) >>  9
        self.reg_num_user_gpios            = (interfaces & 0xF000) >> 12

        self.reg_khz_to_jump_thousandths   = khz_to_jump_thousands

    def _print_registers_uifrev1(self):
        print("u32 Device Capabilities")
        print("  Max CTS Nodes         : %u" % (self.reg_max_cts_nodes))
        print("  Num CTS Channels      : %u" % (self.reg_num_cts_channels))
        print("  Num AUX Channels      : %u" % (self.reg_num_aux_channels))
        print("  Num CTS Driven Traces : %u" % (self.reg_num_cts_driven_traces))
        print("  Num AUX Driven Traces : %u" % (self.reg_num_aux_driven_traces))
        print("  Num A Channels        : %u" % (self.reg_num_a_channels))
        print("  Num B Channels        : %u" % (self.reg_num_b_channels))
        print("  Num C Channels        : %u" % (self.reg_num_c_channels))
        print("  Num D Channels        : %u" % (self.reg_num_d_channels))
        print("  Num E Channels        : %u" % (self.reg_num_e_channels))
        print("  Num F Channels        : %u" % (self.reg_num_f_channels))
        print("  Max Map Length        : %u bytes" % (self.reg_max_map_lengthbytes))
        print("  Max Baseline Length   : %u bytes" % (self.reg_max_baseline_length_bytes))
        print("  Num Baselines         : %u" % (self.reg_num_baselines))

        print("  Slave I2C             : %sSupported" % ("" if self.reg_slave_i2c_supported == 1 else "Not "))
        print("  Slave SPI             : %sSupported" % ("" if self.reg_slave_spi_supported == 1 else "Not "))
        print("  Slave LIN             : %sSupported" % ("" if self.reg_slave_lin_supported == 1 else "Not "))
        print("  Slave USB             : %sSupported" % ("" if self.reg_slave_usb_supported == 1 else "Not "))
        print("  Slave UART            : %sSupported" % ("" if self.reg_slave_uart_supported == 1 else "Not "))
        print("  IRQ Notify            : %sSupported" % ("" if self.reg_irq_notify_supported == 1 else "Not "))
        print("  External Sync         : %sSupported" % ("" if self.reg_external_sync_supported == 1 else "Not "))
        print("  Master I2C            : %sSupported" % ("" if self.reg_master_i2c_supported == 1 else "Not "))
        print("  Master SPI            : %sSupported" % ("" if self.reg_master_spi_supported == 1 else "Not "))
        print("  Master GPIO           : %sSupported" % ("" if self.reg_master_gpio_supported == 1 else "Not "))
        print("  Num User GPIOs        : %u" % (self.reg_num_user_gpios))

        print("  KHz to Jump           : %u (thousands)" % (self.reg_khz_to_jump_thousandths))
#endregion
