from collections import namedtuple

import typeid as tid


EnMember = namedtuple('EnMember', 'name value type')
En = namedtuple('En', 'intt member')


def format_enum_value(val):
    val = val.strip("()")
    suf = ""
    if "-" in val:
        s = val.split("-")
        if len(s) == 2 and len(s[0]) > 0:
            val = s[0]
            suf = " - " + s[1]
    
    elif "+" in val:
        s = val.split("-")
        val = s[0]
        suf = " + " + s[1]
    
    ty = "u32"
    if "." in val:
        ty = "f64"
    if val.endswith("f"):
        ty = "f32"
        val = val[:-1]
    if val.endswith("ULL"):
        ty = "u64"
        val = val[:-3]
    if val.endswith("U"):
        val = val[:-1]
    
    
    if val[0] == "~":
        val = "!" + val[1:]
    if val[0] == "-":
        suf = suf + "i32 as " + ty
    else:
        suf = suf + ty
    
    return (ty, val + suf)

def format_enum_bitpos(val):
    return ("u32", hex(1 << int(val)) + "u32")

def format_enum_extoffset(ext, offset, dir):
    return dir + str(1000000000 + 1000 * (int(ext) - 1) + int(offset))

def format_enum_name(name):
    return name[3:]




def parse_enum(t):
    if tid.isalias(t): return tid.makealias(t)
    return tid.maketype(t.attrib["name"], "enum", En("u32", []), [])

def parse_bitmask(t):
    if tid.isalias(t): return tid.makealias(t)

    requires = [t.find("type").text]
    if "requires" in t.attrib:
        requires.append(t.attrib["requires"])

    return tid.maketype(t.find("name").text, "bitmask", En(t.find("type").text, []), requires)

def parse_enum_member(en):
    member = []
    for e in en.findall("./enum"):
        if "value" in e.attrib:
            v = format_enum_value(e.attrib["value"])
            member.append(EnMember(e.attrib["name"], v[1], v[0]))
        if "bitpos" in e.attrib:
            v = format_enum_bitpos(e.attrib["bitpos"])
            member.append(EnMember(e.attrib["name"], v[1], v[0]))
    return member

def parse_enums(root, types):
    for en in root.findall("./enums"):
        name = en.attrib["name"]
        if name in types.types:
            t = types.types[name]
            e = t.type
            types.types[name] = tid.Type(name, t.category, En(t.type.intt, parse_enum_member(en)), t.requires)

def parse_api_constants(root):
    name = "API Constants"
    for en in root.findall("./enums"):
        if en.attrib["name"] == name:
            return tid.Type(name, "enum", En("", parse_enum_member(en)), [])

def parse_extend_enum(e, types, extnumber):
    if not "extends" in e.attrib: return
    name = e.attrib["extends"]
    if not name in types.types: return
    t = types.types[name]

    if e.attrib["name"] in map(lambda m: m.name, t.type.member): return

    if "bitpos" in e.attrib:
        v = format_enum_bitpos(e.attrib["bitpos"])
        t.type.member.append(EnMember(e.attrib["name"], v[1], v[0]))
    if "offset" in e.attrib:
        if "extnumber" in e.attrib: extnumber = e.attrib["extnumber"]
        d = ""
        if "dir" in e.attrib: d = e.attrib["dir"]
        v = format_enum_value(format_enum_extoffset(extnumber, e.attrib["offset"], d))
        t.type.member.append(EnMember(e.attrib["name"], v[1], v[0]))

        types.types[name] = tid.Type(t.name, t.category, t.type, t.requires)



def write_enum(types, t):
    s = ""
    if t.type.intt:
        s = "#[doc(hidden)] pub type " + types.format_type(t.name) + " = " + types.format_type(t.type.intt) + ";\n"
    for e in t.type.member:
        s += "#[doc(hidden)] pub const " + format_enum_name(e.name) + ": " + e.type + " = " + e.value + ";\n";
    return s


def write_api_constants(t):
    s = ""
    if t.type.intt:
        s = "#[doc(hidden)] pub type " + types.format_type(t.name) + " = " + types.format_type(t.type.intt) + ";\n"
    for e in t.type.member:
        s += "#[doc(hidden)] pub const " + format_enum_name(e.name) + ": " + e.type + " = " + e.value + ";\n";
    return s


