import dummy_wintypes
import itertools
from winstruct import WinStruct, WinUnion, WinStructType, Ptr, WinEnum, BitFieldValue, ComplexArrayExpression
from simpleparser import *



class WinStructParser(Parser):
    def __init__(self, *args, **kwargs):
        super(WinStructParser, self).__init__(*args, **kwargs)
        self.pack = None

    def parse_array_or_bitfield(self):
        if type(self.peek()) == OpenSquareBracketToken:
            # Array
            self.assert_token_type(OpenSquareBracketToken)
            nb_rep = ComplexArrayExpression()
            while type(self.peek()) != CloseSquareBracketToken:
                tok = self.next_token()
                if type(tok) not in (NameToken, PlusToken, StarToken):
                    raise ValueError("Array expression only accept names/+/*")
                is_name = (type(tok) == NameToken)
                nb_rep.add_token_to_expression(tok.value, is_name)
            self.assert_token_type(CloseSquareBracketToken)
            return nb_rep # YOLO, import
        # Bitfield
        self.assert_token_type(ColonToken)
        nb_bits = self.promote_to_int(self.next_token())
        return BitFieldValue(nb_bits)


    def parse_def(self):
        if type(self.peek()) != NameToken and self.peek() in (KeywordToken("union"), KeywordToken("struct")):
            # Anonymous union-structure :)
            # kword = self.assert_token_type(KeywordToken)
            # Not a name. Anon union/struct ?
            subdef_type = self.next_token() # union / struct
            if type(self.peek()) == OpenBracketToken:
                sub = self.parse_winstruct(anonymous=True, anon_type=subdef_type)
                return (sub, NameToken(None), 1)
            else:
                # struct _MYNAME_ -> no anon juste the name of the struct.
                # Nothing special: juste drop the struct
                assert subdef_type.value == "struct"
                # Name will be enforced by nest line.

        def_type_tok  = self.assert_token_type(NameToken)
        def_type = WinStructType(def_type_tok.value)
        if type(self.peek()) == StarToken:
            def_type = Ptr(def_type)
            discard_star = self.next_token()

        def_name = self.assert_token_type(NameToken)

        if type(self.peek()) == SemiColonToken:
            self.next_token()
            return (def_type, def_name, 1)

        number_rep = self.parse_array_or_bitfield()
        self.assert_token_type(SemiColonToken)
        return (def_type, def_name, number_rep)

    def parse_typedef(self, struct):
        if type(self.peek()) == SemiColonToken: # Just a ; no typedef
            self.next_token()
            return
        sep = CommaToken()
        while type(sep) == CommaToken:
            add_to_typedef = struct.add_typedef
            if type(self.peek()) == StarToken:
                self.next_token()
                add_to_typedef = struct.add_ptr_typedef
            name = self.assert_token_type(NameToken)
            # UGLY HACK for LDR definition
            if name.value == "RESTRICTED_POINTER":
                name = self.assert_token_type(NameToken)
            add_to_typedef(name.value)
            sep = self.next_token()
        self.assert_token_type(SemiColonToken, sep)

    def parse_enum(self, is_typedef):
        """Handle enum typedef with no value assignement and 1 typedef after"""
        if not type(self.peek()) == OpenBracketToken:
            # Not an ANON enum
            enum_name = self.assert_token_type(NameToken).value
            res_enum = WinEnum(enum_name)
        else:
            if not is_typedef:
                raise ValueError("Anonymous union not in a typedef")
            enum_name = "<anonymous>"
            res_enum = WinEnum(None)

        self.assert_token_type(OpenBracketToken)
        count = itertools.count()
        while type(self.peek()) != CloseBracketToken:
            i = next(count)
            name = self.assert_token_type(NameToken)
            if type(self.peek()) == EqualToken:
                # Equal sign (hard define of enum value)
                self.assert_token_type(EqualToken)
                i = self.promote_to_int(self.next_token())
                # print(i, new_i)
                # assert new_i >= i, "Cannot define eum value with inferior current value ({0})".format(enum_name)
                # Setup new counter from here
                count = itertools.count(i + 1)
            res_enum.add_enum_entry(i, name.value)
            if not type(self.peek()) == CloseBracketToken:
                self.assert_token_type(CommaToken)

        self.assert_token_type(CloseBracketToken)
        self.parse_typedef(res_enum)
        #other_name = self.assert_token_type(NameToken).value
        #res_enum.add_typedef(other_name)
        #self.assert_token_type(SemiColonToken)
        return res_enum


    def parse_winstruct(self, anonymous=False, anon_type=None):
        is_typedef = False
        peeked = self.peek()
        if peeked == KeywordToken("typedef"):
            self.assert_keyword("typedef")
            is_typedef = True

        if not anonymous:
            def_type = self.assert_token_type(KeywordToken)
        else:
            def_type = anon_type # Hack car le lexeing a ete fait avant, l'info est donc passee en param.
        if def_type.value == "enum":
            return self.parse_enum(is_typedef)
        if def_type.value == "struct":
            WinDefType = WinStruct
        elif def_type.value == "union":
            WinDefType = WinUnion
        else:
            raise ParsingError("Expecting union or struct got <{0}> instead".format(def_type.value))
        if not type(self.peek()) == OpenBracketToken:
            # Not an anonymous structure def
            struct_name = self.assert_token_type(NameToken).value
        else:
            # Anonymous structure def: check if we are ina typedef
            if not is_typedef and not anonymous:
                raise ValueError("Anonymous structure/union not in a typedef")
            struct_name = None
        self.assert_token_type(OpenBracketToken)

        result = WinDefType(struct_name, self.pack)
        while type(self.peek()) != CloseBracketToken:
            tok_type, tok_name, nb_rep = self.parse_def()
            result.add_field((tok_type, tok_name.value, nb_rep))
        self.assert_token_type(CloseBracketToken)
        if is_typedef:
            self.parse_typedef(result)
        elif anonymous:
            if type(self.peek()) == NameToken:
                result.add_typedef(self.assert_token_type(NameToken).value)
            self.assert_token_type(SemiColonToken)
        else:
            self.assert_token_type(SemiColonToken)
        return result

    def parse(self):
        strucs = []
        enums = []
        while self.peek() is not None:
            # HANDLE PRAGMA_PACK / PRAGMA_NOPACK
            if type(self.peek()) == NameToken:
                pragma = self.next_token().value
                if pragma == "PRAGMA_NOPACK":
                    self.pack = None
                    continue
                if pragma != "PRAGMA_PACK":
                    raise ValueError("Expected struct/union def or PRAGMA_[NO]PACK")
                pack_value = self.promote_to_int(self.next_token())
                self.pack = pack_value

            x = self.parse_winstruct()
            #x.packing = self.pack
            #if x.packing != None:
            #    print("{0} pack = {1}".format(x.name, x.packing))
            if type(x) == WinStruct or type(x) == WinUnion:
                strucs.append(x)
            elif type(x) == WinEnum:
                enums.append(x)
            else:
                raise ValueError("Unknow returned type {0}".format(x))
        return strucs, enums

class SimpleTypeDefine(object):
    def __init__(self, lvalue,  rvalue):
        self.lvalue = lvalue
        self.rvalue = rvalue

    def generate_ctypes(self):
        return "{self.lvalue} = {self.rvalue}".format(self=self)

class SimpleTypesParser(Parser):
    def __init__(self, data):
        self.lexer = iter(Lexer(self.initial_processing(data), newlinetoken=True))
        self.peek_token = None

    def parse(self):
        results = []
        while self.peek() is not None:
            lvalue = self.assert_token_type(NameToken).value
            self.assert_token_type(EqualToken)
            rvalue = ""
            while type(self.peek()) is not NewLineToken:
                rvalue += self.next_token().value
            results.append(SimpleTypeDefine(lvalue, rvalue))
            while type(self.peek()) is NewLineToken: # discard the NewLineToken(s)
                self.next_token()
        return results

def dbg_lexer(data):
    for i in Lexer(data).token_generation():
        print i

def dbg_parser(data):
    return WinStructParser(data).parse()

def dbg_validate(data):
    return validate_structs(Parser(data).parse())


if __name__ == "__main__":
    import sys
    #data = open(sys.argv[1], 'r').read()
    #ctypes_code = generate_ctypes(data)
