import sys
from os import path
import parse_bits
from collections import OrderedDict
from functools import partial
from pprint import pprint

class BaseRegister:
    def get(self, n, *, descriptor):
        if type(descriptor.bits) is int:
            return (n >> descriptor.bits) & 1
        else:
            high, low = descriptor.bits
            assert high > low
            mask_length = (high - low) + 1
            mask = (1 << mask_length) - 1
            return (n >> low) & mask

    def get_name(self, n, *, descriptor):
        value = self.get(n, descriptor=descriptor)
        return value, *descriptor.possible_values[value]

def parse_register(register_name):
    base = path.dirname(__file__)

    filename = path.join(base, "bits", register_name.lower() + ".txt")
    l = list(parse_bits.parse_file_fields(filename))
    #return OrderedDict(
    #    (d.field_name, d) for d in parse_bits.aggregate(l)
    #)
    cls = type(register_name, (BaseRegister,), {})
    instance = cls()
    descriptors = list(parse_bits.aggregate(l))
    for descriptor in descriptors:
        setattr(instance, descriptor.field_name, partial(instance.get, descriptor=descriptor))
        setattr(instance, f"_{descriptor.field_name}", partial(instance.get_name, descriptor=descriptor))
        func = getattr(instance, descriptor.field_name)
        for pv_value, (pv_name, _) in descriptor.possible_values.items():
            if pv_name is not None:
                setattr(func, pv_name, pv_value)
    assert getattr(instance, "descriptors", None) is None
    instance.descriptors = descriptors

    return instance

US_CMN_INST = parse_register("US_CMN_INST")
US_ALU_RGB_ADDR = parse_register("US_ALU_RGB_ADDR")
US_ALU_ALPHA_ADDR = parse_register("US_ALU_ALPHA_ADDR")
US_ALU_RGB_INST = parse_register("US_ALU_RGB_INST")
US_ALU_ALPHA_INST = parse_register("US_ALU_ALPHA_INST")
US_ALU_RGBA_INST = parse_register("US_ALU_RGBA_INST")
US_TEX_INST = parse_register("US_TEX_INST")
US_TEX_ADDR = parse_register("US_TEX_ADDR")
US_TEX_ADDR_DXDY = parse_register("US_TEX_ADDR_DXDY")
US_FC_INST = parse_register("US_FC_INST")
US_FC_ADDR = parse_register("US_FC_ADDR")

def disassemble_addr_inner(register_const, address, const, rel):
    assert rel == 0
    if const == register_const.TEMPORARY:
        if address & (1 << 7):
            value = address & 0x7f
            return f"float({value})"
        else:
            return f"temp[{address}]"
    elif const == register_const.CONSTANT:
        return f"const[{address}]"
    else:
        assert False, const

def disassemble_addr(register, addr, prefix):
    addr0 = register.ADDR0(addr)
    addr0_const = register.ADDR0_CONST(addr)
    addr0_rel = register.ADDR0_REL(addr)
    addr1 = register.ADDR1(addr)
    addr1_const = register.ADDR1_CONST(addr)
    addr1_rel = register.ADDR1_REL(addr)
    addr2 = register.ADDR2(addr)
    addr2_const = register.ADDR2_CONST(addr)
    addr2_rel = register.ADDR2_REL(addr)
    _, srcp_op, _ = register._SRCP_OP(addr)

    s0 = disassemble_addr_inner(register.ADDR0_CONST, addr0, addr0_const, addr0_rel)
    s1 = disassemble_addr_inner(register.ADDR1_CONST, addr1, addr1_const, addr1_rel)
    s2 = disassemble_addr_inner(register.ADDR2_CONST, addr2, addr2_const, addr2_rel)
    sp = srcp_op.lower()
    return [
        f"{prefix}{i} = {s}"
        for i, s in [('0', s0), ('1', s1), ('2', s2), ('p', sp)]
    ]

swizzle_strs = ['r', 'g', 'b', 'a', '0', 'h', '1', '_']
sel_strs = ['0', '1', '2', 'p']

def disassemble_rgb_swizzle_sel(us_alu_rgb_inst, us_alu_rgba_inst):
    rgb_sel_a = US_ALU_RGB_INST.RGB_SEL_A(us_alu_rgb_inst)
    red_swiz_a = US_ALU_RGB_INST.RED_SWIZ_A(us_alu_rgb_inst)
    green_swiz_a = US_ALU_RGB_INST.GREEN_SWIZ_A(us_alu_rgb_inst)
    blue_swiz_a = US_ALU_RGB_INST.BLUE_SWIZ_A(us_alu_rgb_inst)
    rgb_mod_a = US_ALU_RGB_INST.RGB_MOD_A(us_alu_rgb_inst)

    rgb_sel_b = US_ALU_RGB_INST.RGB_SEL_B(us_alu_rgb_inst)
    red_swiz_b = US_ALU_RGB_INST.RED_SWIZ_B(us_alu_rgb_inst)
    green_swiz_b = US_ALU_RGB_INST.GREEN_SWIZ_B(us_alu_rgb_inst)
    blue_swiz_b = US_ALU_RGB_INST.BLUE_SWIZ_B(us_alu_rgb_inst)
    rgb_mod_b = US_ALU_RGB_INST.RGB_MOD_B(us_alu_rgb_inst)

    rgb_sel_c = US_ALU_RGBA_INST.RGB_SEL_C(us_alu_rgba_inst)
    red_swiz_c = US_ALU_RGBA_INST.RED_SWIZ_C(us_alu_rgba_inst)
    green_swiz_c = US_ALU_RGBA_INST.GREEN_SWIZ_C(us_alu_rgba_inst)
    blue_swiz_c = US_ALU_RGBA_INST.BLUE_SWIZ_C(us_alu_rgba_inst)
    rgb_mod_c = US_ALU_RGBA_INST.RGB_MOD_C(us_alu_rgba_inst)

    assert rgb_mod_a == 0
    assert rgb_mod_b == 0
    assert rgb_mod_c == 0

    rgb_swiz_a = ''.join(swizzle_strs[n] for n in [red_swiz_a, green_swiz_a, blue_swiz_a])
    rgb_swiz_b = ''.join(swizzle_strs[n] for n in [red_swiz_b, green_swiz_b, blue_swiz_b])
    rgb_swiz_c = ''.join(swizzle_strs[n] for n in [red_swiz_c, green_swiz_c, blue_swiz_c])

    rgb_swiz = [rgb_swiz_a, rgb_swiz_b, rgb_swiz_c]
    rgb_sels = [sel_strs[n] for n in [rgb_sel_a, rgb_sel_b, rgb_sel_c]]

    return ", ".join(f"rgb{sel}.{swiz}" for swiz, sel in zip(rgb_swiz, rgb_sels))

def disassemble_a_swizzle_sel(us_alu_alpha_inst, us_alu_rgba_inst):
    alpha_sel_a = US_ALU_ALPHA_INST.ALPHA_SEL_A(us_alu_alpha_inst)
    alpha_swiz_a = US_ALU_ALPHA_INST.ALPHA_SWIZ_A(us_alu_alpha_inst)
    alpha_mod_a = US_ALU_ALPHA_INST.ALPHA_MOD_A(us_alu_alpha_inst)

    alpha_sel_b = US_ALU_ALPHA_INST.ALPHA_SEL_B(us_alu_alpha_inst)
    alpha_swiz_b = US_ALU_ALPHA_INST.ALPHA_SWIZ_B(us_alu_alpha_inst)
    alpha_mod_b = US_ALU_ALPHA_INST.ALPHA_MOD_B(us_alu_alpha_inst)

    alpha_sel_c = US_ALU_RGBA_INST.ALPHA_SEL_C(us_alu_rgba_inst)
    alpha_swiz_c = US_ALU_RGBA_INST.ALPHA_SWIZ_C(us_alu_rgba_inst)
    alpha_mod_c = US_ALU_RGBA_INST.ALPHA_MOD_C(us_alu_rgba_inst)

    assert alpha_mod_a == 0
    assert alpha_mod_b == 0
    assert alpha_mod_c == 0

    a_swiz = [swizzle_strs[n] for n in [alpha_swiz_a, alpha_swiz_b, alpha_swiz_c]]
    a_sels = [sel_strs[n] for n in [alpha_sel_a, alpha_sel_b, alpha_sel_c]]

    return ", ".join(f"a{sel}.{swiz}" for swiz, sel in zip(a_swiz, a_sels))

def disassemble_alu(ix, code):
    us_cmn_inst = code[ix + 0]
    us_alu_rgb_addr = code[ix + 1]
    us_alu_alpha_addr = code[ix + 2]
    us_alu_rgb_inst = code[ix + 3]
    us_alu_alpha_inst = code[ix + 4]
    us_alu_rgba_inst = code[ix + 5]

    rgb_addr_strs = disassemble_addr(US_ALU_RGB_ADDR, us_alu_rgb_addr, "rgb")
    a_addr_strs = disassemble_addr(US_ALU_ALPHA_ADDR, us_alu_alpha_addr, "a")
    print(", ".join([*rgb_addr_strs, *a_addr_strs]))

    rgb_swizzle_sel = disassemble_rgb_swizzle_sel(us_alu_rgb_inst, us_alu_rgba_inst)
    a_swizzle_sel = disassemble_a_swizzle_sel(us_alu_alpha_inst, us_alu_rgba_inst)

    #omod
    #target
    #alu_wmask

def disassemble(ix, code):
    value = US_CMN_INST.TYPE(code[ix + 0])
    alu_types = {US_CMN_INST.TYPE.US_INST_TYPE_OUT, US_CMN_INST.TYPE.US_INST_TYPE_ALU}
    if value in alu_types:
        disassemble_alu(ix, code)
    else:
        assert False, US_CMN_INST._TYPE(code[ix + 0])

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) // 6):
        disassemble(i * 6, code)
