import gdb


class ListManager:
    def __init__(self, handle, types):
        self._list = None
        self.types = types
        self.assign(handle)

    def assign(self, list_obj):
        if type(list_obj) is gdb.Value and list_obj.type == self.types.list_type:
            self._list = list_obj
            return

        symbol, _ = gdb.lookup_symbol(list_obj)
        if symbol is not None:
            self._list = symbol.value()
        elif list_obj.isdigit():
            addr_int = int(list_obj, 0)
            list_obj_ptr = gdb.Value(addr_int).cast(self.types.list_type.pointer())
            self._list = list_obj_ptr.dereference()
        else:
            self._list = None

    def get_elements(self, cast_type):
        if self._list is not None:

            assert type(cast_type) is gdb.Type

            resp = []
            num_elems = self._list['uxNumberOfItems']

            if num_elems > 0:
                prev_list_elem_ptr = -1
                list_elem_ptr = self._list['xListEnd']['pxPrevious']
                items_found = 0
                while list_elem_ptr != 0 and list_elem_ptr != prev_list_elem_ptr and items_found < num_elems:
                    owner = list_elem_ptr['pvOwner']
                    owner_obj = owner.cast(cast_type.pointer()).dereference()
                    resp.append((owner_obj, list_elem_ptr['xItemValue'].cast(self.types.uint32_type)))

                    items_found += 1
                    prev_list_elem_ptr = list_elem_ptr
                    list_elem_ptr = prev_list_elem_ptr['pxPrevious']

            return resp

        else:
            raise ValueError("Invalid List Object!")


def get_list_container(list_item):
    assert type(list_item) is gdb.Value
    try:
        return int(list_item['pvContainer'])
    except gdb.error:
        # configENABLE_BACKWARD_COMPATIBILITY is set to 0
        return int(list_item['pxContainer'])
