import pvs_src
import pvs_src_bits
import pvs_dst
import pvs_dst_bits
import itertools
from functools import partial
import sys

def out(level, *args):
    sys.stdout.write("    " * level + " ".join(args))

def parse_code(code):
    ix = 0

    pvs_dst_maxlen = max(len(k) for k, _ in  pvs_dst.table)
    print(pvs_dst_maxlen)

    while ix < len(code):
        print(f"ix: {ix // 4}")
        dst_op = code[ix + 0]
        src_op0 = code[ix + 1]
        src_op1 = code[ix + 2]
        src_op2 = code[ix + 3]
        print(f"  dst: {dst_op:08x}")
        for name, parse in pvs_dst.table:
            namepad = name.ljust(pvs_dst_maxlen + 1)
            value = parse(dst_op)
            if name == "OPCODE":
                if pvs_dst.MATH_INST(dst_op):
                    print("   ", namepad, pvs_dst_bits.MATH_OPCODE[value])
                else:
                    print("   ", namepad, pvs_dst_bits.VECTOR_OPCODE[value])
            elif name == "REG_TYPE":
                print("   ", namepad, pvs_dst_bits.PVS_DST_REG[value])
            else:
                print("   ", namepad, value)

        print(f"  src0: {src_op0:08x}")
        for name, parse in pvs_src.table:
            print("   ", name, parse(src_op0))
        print(f"  src1: {src_op1:08x}")
        for name, parse in pvs_src.table:
            print("   ", name, parse(src_op1))
        print(f"  src2: {src_op2:08x}")
        for name, parse in pvs_src.table:
            print("   ", name, parse(src_op2))

        ix += 4

def dst_swizzle_from_we(dst_op):
    table = [
        (pvs_dst.WE_X, "x"),
        (pvs_dst.WE_Y, "y"),
        (pvs_dst.WE_Z, "z"),
        (pvs_dst.WE_W, "w"),
    ]
    return ''.join(
        c for func, c in table
        if func(dst_op)
    )

_op_substitutions = [
    ("DOT_PRODUCT", "DOT"),
    ("MULTIPLY_ADD", "MAD"),
    ("FRACTION", "FRC"),
    ("MULTIPLY", "MUL"),
    ("MAXMIUM", "MAX"),
    ("MINIMUM", "MIN"),
]

def op_substitutions(s):
    for k, v in _op_substitutions:
        if k in s:
            s = s.replace(k, v)
    return s

def parse_dst_op(dst_op):
    opcode = pvs_dst.OPCODE(dst_op)
    math_inst = pvs_dst.MATH_INST(dst_op)
    macro_inst = pvs_dst.MACRO_INST(dst_op)
    reg_type = pvs_dst.REG_TYPE(dst_op)
    addr_mode = ( (pvs_dst.ADDR_MODE_1(dst_op) << 1)
                | (pvs_dst.ADDR_MODE_0(dst_op) << 0))
    offset = pvs_dst.OFFSET(dst_op)
    ve_sat = pvs_dst.VE_SAT(dst_op)
    me_sat = pvs_dst.ME_SAT(dst_op)
    pred_enable = pvs_dst.PRED_ENABLE(dst_op)
    pred_sense = pvs_dst.PRED_SENSE(dst_op)
    dual_math_op = pvs_dst.DUAL_MATH_OP(dst_op)
    addr_sel = pvs_dst.ADDR_SEL(dst_op)

    assert addr_mode == 0
    assert pred_enable == 0
    assert pred_sense == 0
    assert dual_math_op == 0
    assert addr_sel == 0

    parts = []

    reg_str = pvs_dst_bits.PVS_DST_REG[reg_type]
    reg_str = reg_str.replace("TEMPORARY", "TEMP").lower()
    we_swizzle = dst_swizzle_from_we(dst_op)
    parts.append(f"{reg_str}[{offset}].{we_swizzle}")

    def get_opcode_str():
        if math_inst:
            assert not macro_inst
            return op_substitutions(pvs_dst_bits.MATH_OPCODE[opcode])
        elif macro_inst:
            return op_substitutions(pvs_dst_bits.MACRO_OPCODE[opcode])
        else:
            return op_substitutions(pvs_dst_bits.VECTOR_OPCODE[opcode])

    opcode_str = get_opcode_str()
    if ve_sat:
        assert not math_inst
        opcode_str += '.SAT'
    if me_sat:
        assert math_inst
        opcode_str += '.SAT'
    parts.append(opcode_str)

    return parts

def src_swizzle_from_src_op(src_op):
    swizzle_x = pvs_src.SWIZZLE_X(src_op)
    swizzle_y = pvs_src.SWIZZLE_Y(src_op)
    swizzle_z = pvs_src.SWIZZLE_Z(src_op)
    swizzle_w = pvs_src.SWIZZLE_W(src_op)
    modifier_x = pvs_src.MODIFIER_X(src_op)
    modifier_y = pvs_src.MODIFIER_Y(src_op)
    modifier_z = pvs_src.MODIFIER_Z(src_op)
    modifier_w = pvs_src.MODIFIER_W(src_op)

    modifiers = [
        '' if modifier == 0 else '-'
        for modifier
        in [modifier_x, modifier_y, modifier_z, modifier_w]
    ]
    src_swizzle_select = [
        'x', 'y', 'z', 'w', '0', '1', 'h', '_'
    ]
    swizzles = [
        src_swizzle_select[swizzle]
        for swizzle
        in [swizzle_x, swizzle_y, swizzle_z, swizzle_w]
    ]

    return ''.join(map(''.join, zip(modifiers, swizzles)))

def parse_src_op(src_op):
    reg_type = pvs_src.REG_TYPE(src_op)
    abs_xyzw = pvs_src.ABS_XYZW(src_op)
    addr_mode = ( (pvs_src.ADDR_MODE_1(src_op) << 1)
                | (pvs_src.ADDR_MODE_0(src_op) << 0))
    offset = pvs_src.OFFSET(src_op)
    addr_sel = pvs_src.ADDR_SEL(src_op)

    assert addr_mode == 0
    assert addr_sel == 0

    reg_type_str = pvs_src_bits.PVS_SRC_REG_TYPE[reg_type]
    reg_type_str = reg_type_str.removeprefix("PVS_SRC_REG_")
    reg_type_str = reg_type_str.replace("TEMPORARY", "TEMP")
    reg_type_str = reg_type_str.replace("CONSTANT", "CONST")
    reg_type_str = reg_type_str.lower()

    src_swizzle = src_swizzle_from_src_op(src_op)
    if abs_xyzw:
        src_swizzle = f"abs({src_swizzle})"

    return f"{reg_type_str}[{offset}].{src_swizzle}"

def parse_instruction(instruction):
    dst_op = instruction[0]
    src_op0 = instruction[1]
    src_op1 = instruction[2]
    src_op2 = instruction[3]

    dst, op, *rest = itertools.chain(
        parse_dst_op(dst_op),
        [
            parse_src_op(src_op0),
            parse_src_op(src_op1),
            parse_src_op(src_op2),
        ]
    )

    print(dst.ljust(12), "=", op.ljust(12), " ".join(map(lambda s: s.ljust(17), rest)))

def parse_hex(s):
    assert s.startswith('0x')
    return int(s.removeprefix('0x'), 16)

if __name__ == "__main__":
    filename = sys.argv[1]
    with open(filename) as f:
        buf = f.read()
    code = [parse_hex(c.strip()) for c in buf.split(',') if c.strip()]
    for i in range(len(code) // 4):
        parse_instruction(code[i*4:i*4+4])
