from __future__ import annotations
import logging

import claripy
from archinfo.arch_arm import is_arm_arch

from angr import errors
from angr.errors import SimError, SimCCallError
from angr.sim_options import USE_SIMPLIFIED_CCALLS
from angr.state_plugins.sim_action_object import _raw_ast, SimActionObject

l = logging.getLogger(name=__name__)

# pylint: disable=R0911
# pylint: disable=W0613
# pylint: disable=W0612
# pylint: disable=invalid-unary-operand-type

###############
### Helpers ###
###############


# There might be a better way of doing this
def calc_paritybit(p, msb=7, lsb=0):
    p_part = p[msb:lsb] if len(p) > msb else p

    b = claripy.BVV(1, 1)
    for i in range(p_part.size()):
        b = b ^ p_part[i]
    return b


def _cond_flag(condition):
    return claripy.If(condition, claripy.BVV(1, 1), claripy.BVV(0, 1))


def calc_zerobit(p):
    return _cond_flag(p == 0)


def boolean_extend(O, a, b, size):
    return claripy.If(O(a, b), claripy.BVV(1, size), claripy.BVV(0, size))


def op_concretize(op):
    if type(op) is int:
        return op
    op_e = claripy.excavate_ite(op)
    if op_e.op == "If":
        cases = list(claripy.reverse_ite_cases(op_e))
        if all(c.op == "BVV" for _, c in cases):
            raise CCallMultivaluedException(cases, op)
    if op.op == "__or__":
        # I've seen cases like this in AARCH64:
        #    CCMP            W9, W8, #4, NE
        #
        #    00 | ------ IMark(0x0, 4, 0) ------
        #    01 | t12 = GET:I64(cc_op)
        #    02 | t11 = Or64(t12,0x0000000000000010)
        #    03 | t13 = GET:I64(cc_dep1)
        #    04 | t14 = GET:I64(cc_dep2)
        #    05 | t15 = GET:I64(cc_ndep)
        #    06 | t16 = arm64g_calculate_condition(t11,t13,t14,t15):Ity_I64
        #    07 | t10 = 64to1(t16)
        #    08 | t18 = GET:I64(x9)
        #    09 | t17 = 64to32(t18)
        #    10 | t20 = GET:I64(x8)
        #    11 | t19 = 64to32(t20)
        #    12 | t21 = 32Uto64(t17)
        #    13 | t22 = 32Uto64(t19)
        #    14 | t23 = ITE(t10,0x0000000000000003,0x0000000000000000)
        #    15 | t24 = ITE(t10,t21,0x0000000040000000)
        #    16 | t25 = ITE(t10,t22,0x0000000000000000)
        #    17 | PUT(cc_op) = t23
        #    18 | PUT(cc_dep1) = t24
        #    19 | PUT(cc_dep2) = t25
        #    20 | PUT(cc_ndep) = 0x0000000000000000
        if op.args[0].op == "BVV":
            return op.args[0].args[0]
        if op.args[1].op == "BVV":
            return op.args[1].args[0]
    if op.op != "BVV":
        raise SimError(
            f"Hit a symbolic conditional operation (need If or BVV, got {op.op}). " + "Something has gone wildly wrong."
        )
    return op.args[0]


def strip_simaction(val):
    if isinstance(val, SimActionObject):
        return _raw_ast(val)
    return val


class CCallMultivaluedException(Exception):
    pass


##################
### x86* data ###
##################

data: dict[str, dict[str, dict[str, int | None]]] = {
    "AMD64": {
        "CondTypes": {},
        "CondBitOffsets": {},
        "CondBitMasks": {},
        "OpTypes": {},
    },
    "X86": {
        "CondTypes": {},
        "CondBitOffsets": {},
        "CondBitMasks": {},
        "OpTypes": {},
    },
}

# condition types
data["AMD64"]["CondTypes"]["CondO"] = 0  # /* overflow           */
data["AMD64"]["CondTypes"]["CondNO"] = 1  # /* no overflow        */
data["AMD64"]["CondTypes"]["CondB"] = 2  # /* below              */
data["AMD64"]["CondTypes"]["CondNB"] = 3  # /* not below          */
data["AMD64"]["CondTypes"]["CondZ"] = 4  # /* zero               */
data["AMD64"]["CondTypes"]["CondNZ"] = 5  # /* not zero           */
data["AMD64"]["CondTypes"]["CondBE"] = 6  # /* below or equal     */
data["AMD64"]["CondTypes"]["CondNBE"] = 7  # /* not below or equal */
data["AMD64"]["CondTypes"]["CondS"] = 8  # /* negative           */
data["AMD64"]["CondTypes"]["CondNS"] = 9  # /* not negative       */
data["AMD64"]["CondTypes"]["CondP"] = 10  # /* parity even        */
data["AMD64"]["CondTypes"]["CondNP"] = 11  # /* not parity even    */
data["AMD64"]["CondTypes"]["CondL"] = 12  # /* jump less          */
data["AMD64"]["CondTypes"]["CondNL"] = 13  # /* not less           */
data["AMD64"]["CondTypes"]["CondLE"] = 14  # /* less or equal      */
data["AMD64"]["CondTypes"]["CondNLE"] = 15  # /* not less or equal  */

# condition bit offsets
data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_O"] = 11
data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_S"] = 7
data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_Z"] = 6
data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_A"] = 4
data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_C"] = 0
data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_P"] = 2

# masks
data["AMD64"]["CondBitMasks"]["G_CC_MASK_O"] = 1 << data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_O"]
data["AMD64"]["CondBitMasks"]["G_CC_MASK_S"] = 1 << data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_S"]
data["AMD64"]["CondBitMasks"]["G_CC_MASK_Z"] = 1 << data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_Z"]
data["AMD64"]["CondBitMasks"]["G_CC_MASK_A"] = 1 << data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_A"]
data["AMD64"]["CondBitMasks"]["G_CC_MASK_C"] = 1 << data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_C"]
data["AMD64"]["CondBitMasks"]["G_CC_MASK_P"] = 1 << data["AMD64"]["CondBitOffsets"]["G_CC_SHIFT_P"]

# operation types
data["AMD64"]["OpTypes"]["G_CC_OP_COPY"] = 0
data["AMD64"]["OpTypes"]["G_CC_OP_ADDB"] = 1
data["AMD64"]["OpTypes"]["G_CC_OP_ADDW"] = 2
data["AMD64"]["OpTypes"]["G_CC_OP_ADDL"] = 3
data["AMD64"]["OpTypes"]["G_CC_OP_ADDQ"] = 4
data["AMD64"]["OpTypes"]["G_CC_OP_SUBB"] = 5
data["AMD64"]["OpTypes"]["G_CC_OP_SUBW"] = 6
data["AMD64"]["OpTypes"]["G_CC_OP_SUBL"] = 7
data["AMD64"]["OpTypes"]["G_CC_OP_SUBQ"] = 8
data["AMD64"]["OpTypes"]["G_CC_OP_ADCB"] = 9
data["AMD64"]["OpTypes"]["G_CC_OP_ADCW"] = 10
data["AMD64"]["OpTypes"]["G_CC_OP_ADCL"] = 11
data["AMD64"]["OpTypes"]["G_CC_OP_ADCQ"] = 12
data["AMD64"]["OpTypes"]["G_CC_OP_SBBB"] = 13
data["AMD64"]["OpTypes"]["G_CC_OP_SBBW"] = 14
data["AMD64"]["OpTypes"]["G_CC_OP_SBBL"] = 15
data["AMD64"]["OpTypes"]["G_CC_OP_SBBQ"] = 16
data["AMD64"]["OpTypes"]["G_CC_OP_LOGICB"] = 17
data["AMD64"]["OpTypes"]["G_CC_OP_LOGICW"] = 18
data["AMD64"]["OpTypes"]["G_CC_OP_LOGICL"] = 19
data["AMD64"]["OpTypes"]["G_CC_OP_LOGICQ"] = 20
data["AMD64"]["OpTypes"]["G_CC_OP_INCB"] = 21
data["AMD64"]["OpTypes"]["G_CC_OP_INCW"] = 22
data["AMD64"]["OpTypes"]["G_CC_OP_INCL"] = 23
data["AMD64"]["OpTypes"]["G_CC_OP_INCQ"] = 24
data["AMD64"]["OpTypes"]["G_CC_OP_DECB"] = 25
data["AMD64"]["OpTypes"]["G_CC_OP_DECW"] = 26
data["AMD64"]["OpTypes"]["G_CC_OP_DECL"] = 27
data["AMD64"]["OpTypes"]["G_CC_OP_DECQ"] = 28
data["AMD64"]["OpTypes"]["G_CC_OP_SHLB"] = 29
data["AMD64"]["OpTypes"]["G_CC_OP_SHLW"] = 30
data["AMD64"]["OpTypes"]["G_CC_OP_SHLL"] = 31
data["AMD64"]["OpTypes"]["G_CC_OP_SHLQ"] = 32
data["AMD64"]["OpTypes"]["G_CC_OP_SHRB"] = 33
data["AMD64"]["OpTypes"]["G_CC_OP_SHRW"] = 34
data["AMD64"]["OpTypes"]["G_CC_OP_SHRL"] = 35
data["AMD64"]["OpTypes"]["G_CC_OP_SHRQ"] = 36
data["AMD64"]["OpTypes"]["G_CC_OP_ROLB"] = 37
data["AMD64"]["OpTypes"]["G_CC_OP_ROLW"] = 38
data["AMD64"]["OpTypes"]["G_CC_OP_ROLL"] = 39
data["AMD64"]["OpTypes"]["G_CC_OP_ROLQ"] = 40
data["AMD64"]["OpTypes"]["G_CC_OP_RORB"] = 41
data["AMD64"]["OpTypes"]["G_CC_OP_RORW"] = 42
data["AMD64"]["OpTypes"]["G_CC_OP_RORL"] = 43
data["AMD64"]["OpTypes"]["G_CC_OP_RORQ"] = 44
data["AMD64"]["OpTypes"]["G_CC_OP_UMULB"] = 45
data["AMD64"]["OpTypes"]["G_CC_OP_UMULW"] = 46
data["AMD64"]["OpTypes"]["G_CC_OP_UMULL"] = 47
data["AMD64"]["OpTypes"]["G_CC_OP_UMULQ"] = 48
data["AMD64"]["OpTypes"]["G_CC_OP_SMULB"] = 49
data["AMD64"]["OpTypes"]["G_CC_OP_SMULW"] = 50
data["AMD64"]["OpTypes"]["G_CC_OP_SMULL"] = 51
data["AMD64"]["OpTypes"]["G_CC_OP_SMULQ"] = 52
data["AMD64"]["OpTypes"]["G_CC_OP_ANDN32"] = 53
data["AMD64"]["OpTypes"]["G_CC_OP_ANDN64"] = 54
data["AMD64"]["OpTypes"]["G_CC_OP_BLSI32"] = 55
data["AMD64"]["OpTypes"]["G_CC_OP_BLSI64"] = 56
data["AMD64"]["OpTypes"]["G_CC_OP_BLSMSK32"] = 57
data["AMD64"]["OpTypes"]["G_CC_OP_BLSMSK64"] = 58
data["AMD64"]["OpTypes"]["G_CC_OP_BLSR32"] = 59
data["AMD64"]["OpTypes"]["G_CC_OP_BLSR64"] = 60
data["AMD64"]["OpTypes"]["G_CC_OP_ADCXL"] = 61
data["AMD64"]["OpTypes"]["G_CC_OP_ADCXQ"] = 62
data["AMD64"]["OpTypes"]["G_CC_OP_ADOXL"] = 63
data["AMD64"]["OpTypes"]["G_CC_OP_ADOXQ"] = 64
data["AMD64"]["OpTypes"]["G_CC_OP_NUMBER"] = 65

data["X86"]["CondTypes"]["CondO"] = 0
data["X86"]["CondTypes"]["CondNO"] = 1
data["X86"]["CondTypes"]["CondB"] = 2
data["X86"]["CondTypes"]["CondNB"] = 3
data["X86"]["CondTypes"]["CondZ"] = 4
data["X86"]["CondTypes"]["CondNZ"] = 5
data["X86"]["CondTypes"]["CondBE"] = 6
data["X86"]["CondTypes"]["CondNBE"] = 7
data["X86"]["CondTypes"]["CondS"] = 8
data["X86"]["CondTypes"]["CondNS"] = 9
data["X86"]["CondTypes"]["CondP"] = 10
data["X86"]["CondTypes"]["CondNP"] = 11
data["X86"]["CondTypes"]["CondL"] = 12
data["X86"]["CondTypes"]["CondNL"] = 13
data["X86"]["CondTypes"]["CondLE"] = 14
data["X86"]["CondTypes"]["CondNLE"] = 15
data["X86"]["CondTypes"]["CondAlways"] = 16

data["X86"]["CondBitOffsets"]["G_CC_SHIFT_O"] = 11
data["X86"]["CondBitOffsets"]["G_CC_SHIFT_S"] = 7
data["X86"]["CondBitOffsets"]["G_CC_SHIFT_Z"] = 6
data["X86"]["CondBitOffsets"]["G_CC_SHIFT_A"] = 4
data["X86"]["CondBitOffsets"]["G_CC_SHIFT_C"] = 0
data["X86"]["CondBitOffsets"]["G_CC_SHIFT_P"] = 2

# masks
data["X86"]["CondBitMasks"]["G_CC_MASK_O"] = 1 << data["X86"]["CondBitOffsets"]["G_CC_SHIFT_O"]
data["X86"]["CondBitMasks"]["G_CC_MASK_S"] = 1 << data["X86"]["CondBitOffsets"]["G_CC_SHIFT_S"]
data["X86"]["CondBitMasks"]["G_CC_MASK_Z"] = 1 << data["X86"]["CondBitOffsets"]["G_CC_SHIFT_Z"]
data["X86"]["CondBitMasks"]["G_CC_MASK_A"] = 1 << data["X86"]["CondBitOffsets"]["G_CC_SHIFT_A"]
data["X86"]["CondBitMasks"]["G_CC_MASK_C"] = 1 << data["X86"]["CondBitOffsets"]["G_CC_SHIFT_C"]
data["X86"]["CondBitMasks"]["G_CC_MASK_P"] = 1 << data["X86"]["CondBitOffsets"]["G_CC_SHIFT_P"]

data["X86"]["OpTypes"]["G_CC_OP_COPY"] = 0
data["X86"]["OpTypes"]["G_CC_OP_ADDB"] = 1
data["X86"]["OpTypes"]["G_CC_OP_ADDW"] = 2
data["X86"]["OpTypes"]["G_CC_OP_ADDL"] = 3
data["X86"]["OpTypes"]["G_CC_OP_SUBB"] = 4
data["X86"]["OpTypes"]["G_CC_OP_SUBW"] = 5
data["X86"]["OpTypes"]["G_CC_OP_SUBL"] = 6
data["X86"]["OpTypes"]["G_CC_OP_ADCB"] = 7
data["X86"]["OpTypes"]["G_CC_OP_ADCW"] = 8
data["X86"]["OpTypes"]["G_CC_OP_ADCL"] = 9
data["X86"]["OpTypes"]["G_CC_OP_SBBB"] = 10
data["X86"]["OpTypes"]["G_CC_OP_SBBW"] = 11
data["X86"]["OpTypes"]["G_CC_OP_SBBL"] = 12
data["X86"]["OpTypes"]["G_CC_OP_LOGICB"] = 13
data["X86"]["OpTypes"]["G_CC_OP_LOGICW"] = 14
data["X86"]["OpTypes"]["G_CC_OP_LOGICL"] = 15
data["X86"]["OpTypes"]["G_CC_OP_INCB"] = 16
data["X86"]["OpTypes"]["G_CC_OP_INCW"] = 17
data["X86"]["OpTypes"]["G_CC_OP_INCL"] = 18
data["X86"]["OpTypes"]["G_CC_OP_DECB"] = 19
data["X86"]["OpTypes"]["G_CC_OP_DECW"] = 20
data["X86"]["OpTypes"]["G_CC_OP_DECL"] = 21
data["X86"]["OpTypes"]["G_CC_OP_SHLB"] = 22
data["X86"]["OpTypes"]["G_CC_OP_SHLW"] = 23
data["X86"]["OpTypes"]["G_CC_OP_SHLL"] = 24
data["X86"]["OpTypes"]["G_CC_OP_SHRB"] = 25
data["X86"]["OpTypes"]["G_CC_OP_SHRW"] = 26
data["X86"]["OpTypes"]["G_CC_OP_SHRL"] = 27
data["X86"]["OpTypes"]["G_CC_OP_ROLB"] = 28
data["X86"]["OpTypes"]["G_CC_OP_ROLW"] = 29
data["X86"]["OpTypes"]["G_CC_OP_ROLL"] = 30
data["X86"]["OpTypes"]["G_CC_OP_RORB"] = 31
data["X86"]["OpTypes"]["G_CC_OP_RORW"] = 32
data["X86"]["OpTypes"]["G_CC_OP_RORL"] = 33
data["X86"]["OpTypes"]["G_CC_OP_UMULB"] = 34
data["X86"]["OpTypes"]["G_CC_OP_UMULW"] = 35
data["X86"]["OpTypes"]["G_CC_OP_UMULL"] = 36
data["X86"]["OpTypes"]["G_CC_OP_SMULB"] = 37
data["X86"]["OpTypes"]["G_CC_OP_SMULW"] = 38
data["X86"]["OpTypes"]["G_CC_OP_SMULL"] = 39
data["X86"]["OpTypes"]["G_CC_OP_NUMBER"] = 40

data["X86"]["OpTypes"]["G_CC_OP_SMULQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_UMULQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_RORQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_ROLQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_SHRQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_SHLQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_DECQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_INCQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_LOGICQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_SBBQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_ADCQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_SUBQ"] = None
data["X86"]["OpTypes"]["G_CC_OP_ADDQ"] = None

data_inverted = {
    k_arch: {k_data_class: {y: x for (x, y) in d_data_class.items()} for k_data_class, d_data_class in d_arch.items()}
    for k_arch, d_arch in data.items()
}

data["AMD64"]["size"] = 64
data["X86"]["size"] = 32


#
# AMD64 internal helpers
#
def pc_preamble(nbits):
    data_mask = claripy.BVV(2**nbits - 1, nbits)
    sign_mask = 1 << (nbits - 1)
    return data_mask, sign_mask


def pc_make_rdata(nbits, cf, pf, af, zf, sf, of, platform=None):
    return cf, pf, af, zf, sf, of


def pc_make_rdata_if_necessary(nbits, cf, pf, af, zf, sf, of, platform=None):
    vec = [
        (data[platform]["CondBitOffsets"]["G_CC_SHIFT_C"], cf),
        (data[platform]["CondBitOffsets"]["G_CC_SHIFT_P"], pf),
        (data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"], af),
        (data[platform]["CondBitOffsets"]["G_CC_SHIFT_Z"], zf),
        (data[platform]["CondBitOffsets"]["G_CC_SHIFT_S"], sf),
        (data[platform]["CondBitOffsets"]["G_CC_SHIFT_O"], of),
    ]
    vec.sort(reverse=True)
    return _concat_flags(nbits, vec)


def pc_actions_ADD(state, nbits, arg_l, arg_r, cc_ndep, platform=None):
    data_mask, _sign_mask = pc_preamble(nbits)
    res = arg_l + arg_r

    cf = claripy.If(claripy.ULT(res, arg_l), claripy.BVV(1, 1), claripy.BVV(0, 1))
    pf = calc_paritybit(res)
    af = (res ^ arg_l ^ arg_r)[data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"]]
    zf = calc_zerobit(res)
    sf = res[nbits - 1 : nbits - 1]
    of = ((arg_l ^ arg_r ^ data_mask) & (arg_l ^ res))[nbits - 1 : nbits - 1]

    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_SUB(state, nbits, arg_l, arg_r, cc_ndep, platform=None):
    res = arg_l - arg_r

    cf = claripy.If(claripy.ULT(arg_l, arg_r), claripy.BVV(1, 1), claripy.BVV(0, 1))
    pf = calc_paritybit(res)
    af = (res ^ arg_l ^ arg_r)[data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"]]
    zf = calc_zerobit(res)
    sf = res[nbits - 1 : nbits - 1]
    of = ((arg_l ^ arg_r) & (arg_l ^ res))[nbits - 1 : nbits - 1]

    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_LOGIC(state, nbits, arg_l, arg_r, cc_ndep, platform=None):
    cf = claripy.BVV(0, 1)
    pf = calc_paritybit(arg_l)
    af = claripy.BVV(0, 1)
    zf = calc_zerobit(arg_l)
    sf = arg_l[nbits - 1]
    of = claripy.BVV(0, 1)

    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_DEC(state, nbits, res, _, cc_ndep, platform=None):
    arg_l = res + 1

    cf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_C"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_C"]]
    pf = calc_paritybit(res)
    af = (res ^ arg_l ^ 1)[data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"]]
    zf = calc_zerobit(res)
    sf = res[nbits - 1]
    of = claripy.If(arg_l == (1 << (nbits - 1)), claripy.BVV(1, 1), claripy.BVV(0, 1))
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_ADC(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform=None):
    old_c = cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_C"]
    arg_l = cc_dep1
    arg_r = cc_dep2 ^ old_c
    res = (arg_l + arg_r) + old_c

    cf = claripy.If(
        old_c != 0,
        claripy.If(res <= arg_l, claripy.BVV(1, 1), claripy.BVV(0, 1)),
        claripy.If(res < arg_l, claripy.BVV(1, 1), claripy.BVV(0, 1)),
    )
    pf = calc_paritybit(res)
    af = (res ^ arg_l ^ arg_r)[data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"]]
    zf = calc_zerobit(res)
    sf = res[nbits - 1]
    of = ((arg_l ^ arg_r ^ -1) & (arg_l ^ res))[nbits - 1]

    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_ADCX(state, nbits, cc_dep1, cc_dep2, cc_ndep, is_adc, platform=None):
    pf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_P"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_P"]]
    af = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_A"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"]]
    zf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_Z"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_Z"]]
    sf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_S"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_S"]]
    if is_adc:
        carry = claripy.LShR(cc_ndep, data[platform]["CondBitOffsets"]["G_CC_SHIFT_C"]) & 1
        of = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_O"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_O"]]
    else:
        carry = claripy.LShR(cc_ndep, data[platform]["CondBitOffsets"]["G_CC_SHIFT_O"]) & 1
        cf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_C"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_C"]]
    arg_l = cc_dep1
    arg_r = cc_dep2 ^ carry
    res = (arg_l + arg_r) + carry

    carry = claripy.If(
        carry != 0,
        claripy.If(res <= arg_l, claripy.BVV(1, 1), claripy.BVV(0, 1)),
        claripy.If(res < arg_l, claripy.BVV(1, 1), claripy.BVV(0, 1)),
    )
    if is_adc:
        cf = carry
    else:
        of = carry

    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_ANDN(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform=None):
    cf = claripy.BVV(0, 1)
    pf = claripy.BVV(0, 1)
    af = claripy.BVV(0, 1)
    of = claripy.BVV(0, 1)
    zf = _cond_flag(cc_dep1 == 0)
    sf = cc_dep1[nbits - 1]
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_BLSI(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform=None):
    pf = claripy.BVV(0, 1)
    af = claripy.BVV(0, 1)
    of = claripy.BVV(0, 1)
    cf = _cond_flag(cc_dep2 != 0)
    zf = _cond_flag(cc_dep1 == 0)
    sf = cc_dep1[nbits - 1]
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_BLSMSK(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform=None):
    pf = claripy.BVV(0, 1)
    af = claripy.BVV(0, 1)
    of = claripy.BVV(0, 1)
    zf = claripy.BVV(0, 1)
    cf = _cond_flag(cc_dep2 == 0)
    sf = cc_dep1[nbits - 1]
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_BLSR(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform=None):
    pf = claripy.BVV(0, 1)
    af = claripy.BVV(0, 1)
    of = claripy.BVV(0, 1)
    cf = _cond_flag(cc_dep2 == 0)
    zf = _cond_flag(cc_dep1 == 0)
    sf = cc_dep1[nbits - 1]
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_SBB(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform=None):
    old_c = cc_ndep[data[platform]["CondBitOffsets"]["G_CC_SHIFT_C"]].zero_extend(nbits - 1)
    arg_l = cc_dep1
    arg_r = cc_dep2 ^ old_c
    res = (arg_l - arg_r) - old_c

    cf_c = claripy.If(claripy.ULE(arg_l, arg_r), claripy.BVV(1, 1), claripy.BVV(0, 1))
    cf_noc = claripy.If(claripy.ULT(arg_l, arg_r), claripy.BVV(1, 1), claripy.BVV(0, 1))
    cf = claripy.If(old_c == 1, cf_c, cf_noc)
    pf = calc_paritybit(res)
    af = (res ^ arg_l ^ arg_r)[data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"]]
    zf = calc_zerobit(res)
    sf = res[nbits - 1]
    of = ((arg_l ^ arg_r) & (arg_l ^ res))[nbits - 1]
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_INC(state, nbits, res, _, cc_ndep, platform=None):
    arg_l = res - 1

    cf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_C"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_C"]]
    pf = calc_paritybit(res)
    af = (res ^ arg_l ^ 1)[data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"]]
    zf = calc_zerobit(res)
    sf = res[nbits - 1]
    of = claripy.If(res == (1 << (nbits - 1)), claripy.BVV(1, 1), claripy.BVV(0, 1))
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_SHL(state, nbits, remaining, shifted, cc_ndep, platform=None):
    cf = shifted[nbits - 1]
    pf = calc_paritybit(remaining[7:0])
    af = claripy.BVV(0, 1)
    zf = calc_zerobit(remaining)
    sf = remaining[nbits - 1]
    of = (sf ^ cf)[0]
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_SHR(state, nbits, remaining, shifted, cc_ndep, platform=None):
    cf = claripy.If(shifted & 1 != 0, claripy.BVV(1, 1), claripy.BVV(0, 1))
    pf = calc_paritybit(remaining[7:0])
    af = claripy.BVV(0, 1)
    zf = calc_zerobit(remaining)
    sf = remaining[nbits - 1]
    of = (remaining[nbits - 1] ^ shifted[nbits - 1])[0]
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_ROL(state, nbits, res, _, cc_ndep, platform=None):
    cf = res[0]
    pf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_P"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_P"]]
    af = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_A"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"]]
    zf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_Z"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_Z"]]
    sf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_S"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_S"]]
    of = (claripy.LShR(res, nbits - 1) ^ res)[0]
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_ROR(state, nbits, res, _, cc_ndep, platform=None):
    cf = res[nbits - 1]
    pf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_P"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_P"]]
    af = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_A"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_A"]]
    zf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_Z"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_Z"]]
    sf = (cc_ndep & data[platform]["CondBitMasks"]["G_CC_MASK_S"])[data[platform]["CondBitOffsets"]["G_CC_SHIFT_S"]]
    of = res[nbits - 1] ^ res[nbits - 2]
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_UMUL(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform=None):
    lo = (cc_dep1 * cc_dep2)[nbits - 1 : 0]
    rr = lo
    hi = (rr >> nbits)[nbits - 1 : 0]
    cf = claripy.If(hi != 0, claripy.BVV(1, 1), claripy.BVV(0, 1))
    zf = calc_zerobit(lo)
    pf = calc_paritybit(lo)
    af = claripy.BVV(0, 1)
    sf = lo[nbits - 1]
    of = cf
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_UMULQ(*args, **kwargs):
    l.error("Unsupported flag action UMULQ")
    raise SimCCallError("Unsupported flag action. Please implement or bug Yan.")


def pc_actions_SMUL(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform=None):
    ir = cc_dep1.sign_extend(nbits) * cc_dep2.sign_extend(nbits)
    hi = ir[2 * nbits - 1 : nbits]
    lo = ir[nbits - 1 : 0]
    cf = claripy.If(hi != (lo >> (nbits - 1)), claripy.BVV(1, 1), claripy.BVV(0, 1))
    zf = calc_zerobit(lo)
    pf = calc_paritybit(lo)
    af = claripy.BVV(0, 1)
    sf = lo[nbits - 1]
    of = cf
    return pc_make_rdata(data[platform]["size"], cf, pf, af, zf, sf, of, platform=platform)


def pc_actions_SMULQ(*args, **kwargs):
    l.error("Unsupported flag action SMULQ")
    raise SimCCallError("Unsupported flag action. Please implement or bug Yan.")


def pc_calculate_rdata_all_WRK(state, cc_op, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=None):
    # sanity check
    cc_op = op_concretize(cc_op)

    if cc_op == data[platform]["OpTypes"]["G_CC_OP_COPY"]:
        l.debug("cc_op == data[platform]['OpTypes']['G_CC_OP_COPY']")
        return cc_dep1_formal & (
            data[platform]["CondBitMasks"]["G_CC_MASK_O"]
            | data[platform]["CondBitMasks"]["G_CC_MASK_S"]
            | data[platform]["CondBitMasks"]["G_CC_MASK_Z"]
            | data[platform]["CondBitMasks"]["G_CC_MASK_A"]
            | data[platform]["CondBitMasks"]["G_CC_MASK_C"]
            | data[platform]["CondBitMasks"]["G_CC_MASK_P"]
        )

    cc_str = data_inverted[platform]["OpTypes"][cc_op]

    nbits = _get_nbits(cc_str)
    l.debug("nbits == %d", nbits)

    cc_dep1_formal = cc_dep1_formal[nbits - 1 : 0]
    cc_dep2_formal = cc_dep2_formal[nbits - 1 : 0]
    cc_ndep_formal = cc_ndep_formal[nbits - 1 : 0]

    if cc_str in ["G_CC_OP_ADDB", "G_CC_OP_ADDW", "G_CC_OP_ADDL", "G_CC_OP_ADDQ"]:
        l.debug("cc_str: ADD")
        return pc_actions_ADD(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_ADCB", "G_CC_OP_ADCW", "G_CC_OP_ADCL", "G_CC_OP_ADCQ"]:
        l.debug("cc_str: ADC")
        return pc_actions_ADC(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_SUBB", "G_CC_OP_SUBW", "G_CC_OP_SUBL", "G_CC_OP_SUBQ"]:
        l.debug("cc_str: SUB")
        return pc_actions_SUB(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_SBBB", "G_CC_OP_SBBW", "G_CC_OP_SBBL", "G_CC_OP_SBBQ"]:
        l.debug("cc_str: SBB")
        return pc_actions_SBB(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_LOGICB", "G_CC_OP_LOGICW", "G_CC_OP_LOGICL", "G_CC_OP_LOGICQ"]:
        l.debug("cc_str: LOGIC")
        return pc_actions_LOGIC(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_INCB", "G_CC_OP_INCW", "G_CC_OP_INCL", "G_CC_OP_INCQ"]:
        l.debug("cc_str: INC")
        return pc_actions_INC(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_DECB", "G_CC_OP_DECW", "G_CC_OP_DECL", "G_CC_OP_DECQ"]:
        l.debug("cc_str: DEC")
        return pc_actions_DEC(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_SHLB", "G_CC_OP_SHLW", "G_CC_OP_SHLL", "G_CC_OP_SHLQ"]:
        l.debug("cc_str: SHL")
        return pc_actions_SHL(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_SHRB", "G_CC_OP_SHRW", "G_CC_OP_SHRL", "G_CC_OP_SHRQ"]:
        l.debug("cc_str: SHR")
        return pc_actions_SHR(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_ROLB", "G_CC_OP_ROLW", "G_CC_OP_ROLL", "G_CC_OP_ROLQ"]:
        l.debug("cc_str: ROL")
        return pc_actions_ROL(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_RORB", "G_CC_OP_RORW", "G_CC_OP_RORL", "G_CC_OP_RORQ"]:
        l.debug("cc_str: ROR")
        return pc_actions_ROR(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)

    if cc_str in ["G_CC_OP_UMULB", "G_CC_OP_UMULW", "G_CC_OP_UMULL", "G_CC_OP_UMULQ"]:
        l.debug("cc_str: UMUL")
        return pc_actions_UMUL(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)
    if cc_str == "G_CC_OP_UMULQ":
        l.debug("cc_str: UMULQ")
        return pc_actions_UMULQ(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)
    if cc_str in ["G_CC_OP_SMULB", "G_CC_OP_SMULW", "G_CC_OP_SMULL", "G_CC_OP_SMULQ"]:
        l.debug("cc_str: SMUL")
        return pc_actions_SMUL(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)
    if cc_str == "G_CC_OP_SMULQ":
        l.debug("cc_str: SMULQ")
        return pc_actions_SMULQ(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)
    if cc_str in ["G_CC_OP_ANDN32", "G_CC_OP_ANDN64"]:
        l.debug("cc_str: ANDN")
        return pc_actions_ANDN(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)
    if cc_str in ["G_CC_OP_BLSI32", "G_CC_OP_BLSI64"]:
        l.debug("cc_str: BLSI")
        return pc_actions_BLSI(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)
    if cc_str in ["G_CC_OP_BLSMSK32", "G_CC_OP_BLSMSK64"]:
        l.debug("cc_str: BLSMSK")
        return pc_actions_BLSMSK(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)
    if cc_str in ["G_CC_OP_BLSR32", "G_CC_OP_BLSR64"]:
        l.debug("cc_str: BLSR")
        return pc_actions_BLSR(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, platform=platform)
    if cc_str in ["G_CC_OP_ADOXL", "G_CC_OP_ADOXQ"]:
        l.debug("cc_str: ADOX")
        return pc_actions_ADCX(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, False, platform=platform)
    if cc_str in ["G_CC_OP_ADCXL", "G_CC_OP_ADCXQ"]:
        l.debug("cc_str: ADCX")
        return pc_actions_ADCX(state, nbits, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal, True, platform=platform)

    l.error("Unsupported cc_op %d in in pc_calculate_rdata_all_WRK", cc_op)
    raise SimCCallError("Unsupported cc_op in pc_calculate_rdata_all_WRK")


# This function returns all the data
def pc_calculate_rdata_all(state, cc_op, cc_dep1, cc_dep2, cc_ndep, platform=None):
    rdata_all = pc_calculate_rdata_all_WRK(state, cc_op, cc_dep1, cc_dep2, cc_ndep, platform=platform)
    if isinstance(rdata_all, tuple):
        return pc_make_rdata_if_necessary(data[platform]["size"], *rdata_all, platform=platform)
    return rdata_all


# This function takes a condition that is being checked (ie, zero bit), and basically
# returns that bit
def pc_calculate_condition(state, cond, cc_op, cc_dep1, cc_dep2, cc_ndep, platform=None):
    rdata_all = pc_calculate_rdata_all_WRK(state, cc_op, cc_dep1, cc_dep2, cc_ndep, platform=platform)
    if isinstance(rdata_all, tuple):
        cf, pf, _af, zf, sf, of = rdata_all
        v = op_concretize(cond)

        inv = v & 1
        l.debug("inv: %d", inv)

        if v in [data[platform]["CondTypes"]["CondO"], data[platform]["CondTypes"]["CondNO"]]:
            l.debug("CondO")
            # of = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_O'])
            r = 1 & (inv ^ of)

        elif v in [data[platform]["CondTypes"]["CondZ"], data[platform]["CondTypes"]["CondNZ"]]:
            l.debug("CondZ")
            # zf = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_Z'])
            r = 1 & (inv ^ zf)

        elif v in [data[platform]["CondTypes"]["CondB"], data[platform]["CondTypes"]["CondNB"]]:
            l.debug("CondB")
            # cf = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_C'])
            r = 1 & (inv ^ cf)

        elif v in [data[platform]["CondTypes"]["CondBE"], data[platform]["CondTypes"]["CondNBE"]]:
            l.debug("CondBE")
            # cf = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_C'])
            # zf = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_Z'])
            r = 1 & (inv ^ (cf | zf))

        elif v in [data[platform]["CondTypes"]["CondS"], data[platform]["CondTypes"]["CondNS"]]:
            l.debug("CondS")
            # sf = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_S'])
            r = 1 & (inv ^ sf)

        elif v in [data[platform]["CondTypes"]["CondP"], data[platform]["CondTypes"]["CondNP"]]:
            l.debug("CondP")
            # pf = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_P'])
            r = 1 & (inv ^ pf)

        elif v in [data[platform]["CondTypes"]["CondL"], data[platform]["CondTypes"]["CondNL"]]:
            l.debug("CondL")
            # sf = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_S'])
            # of = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_O'])
            r = 1 & (inv ^ (sf ^ of))

        elif v in [data[platform]["CondTypes"]["CondLE"], data[platform]["CondTypes"]["CondNLE"]]:
            l.debug("CondLE")
            # sf = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_S'])
            # of = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_O'])
            # zf = claripy.LShR(rdata, data[platform]['G_CC_SHIFT_Z'])
            r = 1 & (inv ^ ((sf ^ of) | zf))
        else:
            raise SimCCallError("Unrecognized condition in pc_calculate_condition. Panic.")

        return claripy.Concat(claripy.BVV(0, data[platform]["size"] - 1), r)
    rdata = rdata_all
    v = op_concretize(cond)
    inv = v & 1
    l.debug("inv: %d", inv)

    # THIS IS A FUCKING HACK
    if v == 0xE:
        # jle
        pass
