from . import pvc, ffi


class VEXObject(object):
    """
    The base class for Vex types.
    """

    __slots__ = [ ]


class IRCallee(VEXObject):
    """
    Describes a helper function to call.
    """

    __slots__ = ['regparms', 'name', 'mcx_mask', 'addr']

    def __init__(self, regparms, name, addr, mcx_mask):
        VEXObject.__init__(self)
        self.regparms = regparms
        self.name = name
        self.addr = addr
        self.mcx_mask = mcx_mask

    def __str__(self):
        return str(self.name)

    @staticmethod
    def _from_c(c_callee):
        return IRCallee(c_callee.regparms,
                        ffi.string(c_callee.name),
                        int(ffi.cast("unsigned long long", c_callee.addr)),
                        c_callee.mcx_mask)

    @staticmethod
    def _to_c(callee):
        c_callee = pvc.mkIRCallee(callee.regparms,
                                  callee.name,
                                  ffi.cast("void *", callee.addr))
        c_callee.mcx_mask = callee.mcx_mask
        return c_callee


class IRRegArray(VEXObject):
    """
    A section of the guest state that we want te be able to index at run time, so as to be able to describe indexed or
    rotating register files on the guest.

    :ivar int base:     The offset into the state that this array starts
    :ivar str elemTy:   The types of the elements in this array, as VEX enum strings
    :ivar int nElems:   The number of elements in this array
    """

    __slots__ = ['base', 'elemTy', 'nElems']

    def __init__(self, base, elemTy, nElems):
        VEXObject.__init__(self)
        self.base = base
        self.elemTy = elemTy
        self.nElems = nElems

    def __str__(self):
        return "%s:%sx%d" % (self.base, self.elemTy[4:], self.nElems)

    @staticmethod
    def _from_c(c_arr):
        return IRRegArray(c_arr.base,
                          ints_to_enums[c_arr.elemTy],
                          c_arr.nElems)

    @staticmethod
    def _to_c(arr):
        return pvc.mkIRRegArray(arr.base,
                                get_int_from_enum(arr.elemTy),
                                arr.nElems)

ints_to_enums = { }
enums_to_ints = { }
irop_enums_to_ints = { }
will_be_overwritten = ['Ircr_GT', 'Ircr_LT']

def get_enum_from_int(i):
    return ints_to_enums[i]

def get_int_from_enum(e):
    return enums_to_ints[e]

def _add_enum(s, i=None): # TODO get rid of this
    if i is None:
        while _add_enum.counter in ints_to_enums:
            _add_enum.counter += 1
        i = _add_enum.counter
        _add_enum.counter += 1 # Update for the next iteration
    if i in ints_to_enums:
        if ints_to_enums[i] not in will_be_overwritten:
            raise ValueError('Enum with intkey %d already present' % i)
    enums_to_ints[s] = i
    ints_to_enums[i] = s
    if s.startswith('Iop_'):
        irop_enums_to_ints[s] = i
_add_enum.counter = 0

for attr in dir(pvc):
    if attr[0] in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' and hasattr(pvc, attr) and isinstance(getattr(pvc, attr), int):
        _add_enum(attr, getattr(pvc, attr))

def vex_endness_from_string(endness_str):
    return getattr(pvc, endness_str)

def default_vex_archinfo():
    return {
        'hwcaps': 0,
        'endness': vex_endness_from_string('VexEndnessLE'),
        'ppc_icache_line_szB': 0,
        'ppc_dcbz_szB': 0,
        'ppc_dcbzl_szB': 0,
        'arm64_dMinLine_lg2_szB': 0,
        'arm64_iMinLine_lg2_szB': 0,
        'hwcache_info': {
            'num_levels': 0,
            'num_caches': 0,
            'caches': None,
            'icaches_maintain_coherence': True,
        },
        'x86_cr0': 0xffffffff
    }
