'''
This module contains the classes which represent XCB data types.
'''
from xcbgen.expr import Field, Expression
import __main__

class Type(object):
    '''
    Abstract base class for all XCB data types.
    Contains default fields, and some abstract methods.
    '''
    def __init__(self, name):
        '''
        Default structure initializer.  Sets up default fields.

        Public fields:
        name is a tuple of strings specifying the full type name.
        size is the size of the datatype in bytes, or None if variable-sized.
        nmemb is 1 for non-list types, None for variable-sized lists, otherwise number of elts.
        booleans for identifying subclasses, because I can't figure out isinstance().
        '''
        self.name = name
        self.size = None
        self.nmemb = None
        self.resolved = False

        # Screw isinstance().
        self.is_simple = False
        self.is_list = False
        self.is_expr = False
        self.is_container = False
        self.is_reply = False
        self.is_union = False
        self.is_pad = False
        self.is_switch = False
        self.is_bitcase = False

    def resolve(self, module):
        '''
        Abstract method for resolving a type.
        This should make sure any referenced types are already declared.
        '''
        raise Exception('abstract resolve method not overridden!')

    def out(self, name):
        '''
        Abstract method for outputting code.
        These are declared in the language-specific modules, and
        there must be a dictionary containing them declared when this module is imported!
        '''
        raise Exception('abstract out method not overridden!')

    def fixed_size(self):
        '''
        Abstract method for determining if the data type is fixed-size.
        '''
        raise Exception('abstract fixed_size method not overridden!')

    def make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum=None):
        '''
        Default method for making a data type a member of a structure.
        Extend this if the data type needs to add an additional length field or something.

        module is the global module object.
        complex_type is the structure object.
        see Field for the meaning of the other parameters.
        '''
        new_field = Field(self, field_type, field_name, visible, wire, auto, enum)

        # We dump the _placeholder_byte if any fields are added.
        for (idx, field) in enumerate(complex_type.fields):
            if field == _placeholder_byte:
                complex_type.fields[idx] = new_field
                return

        complex_type.fields.append(new_field)

    def make_fd_of(self, module, complex_type, fd_name):
        '''
        Method for making a fd member of a structure.
        '''
        new_fd = Field(self, module.get_type_name('INT32'), fd_name, True, False, False, None, True)
        # We dump the _placeholder_byte if any fields are added.
        for (idx, field) in enumerate(complex_type.fields):
            if field == _placeholder_byte:
                complex_type.fields[idx] = new_fd
                return

        complex_type.fields.append(new_fd)

class SimpleType(Type):
    '''
    Derived class which represents a cardinal type like CARD32 or char.
    Any type which is typedef'ed to cardinal will be one of these.

    Public fields added:
    none
    '''
    def __init__(self, name, size):
        Type.__init__(self, name)
        self.is_simple = True
        self.size = size
        self.nmemb = 1

    def resolve(self, module):
        self.resolved = True

    def fixed_size(self):
        return True

    out = __main__.output['simple']


# Cardinal datatype globals.  See module __init__ method.
tcard8 = SimpleType(('uint8_t',), 1)
tcard16 = SimpleType(('uint16_t',), 2)
tcard32 = SimpleType(('uint32_t',), 4)
tcard64 = SimpleType(('uint64_t',), 8)
tint8 =  SimpleType(('int8_t',), 1)
tint16 = SimpleType(('int16_t',), 2)
tint32 = SimpleType(('int32_t',), 4)
tint64 = SimpleType(('int64_t',), 8)
tchar =  SimpleType(('char',), 1)
tfloat = SimpleType(('float',), 4)
tdouble = SimpleType(('double',), 8)


class Enum(SimpleType):
    '''
    Derived class which represents an enum.  Fixed-size.

    Public fields added:
    values contains a list of (name, value) tuples.  value is empty, or a number.
    bits contains a list of (name, bitnum) tuples.  items only appear if specified as a bit. bitnum is a number.
    '''
    def __init__(self, name, elt):
        SimpleType.__init__(self, name, 4)
        self.values = []
        self.bits = []
        self.doc = None
        for item in list(elt):
            if item.tag == 'doc':
                self.doc = Doc(name, item)

            # First check if we're using a default value
            if len(list(item)) == 0:
                self.values.append((item.get('name'), ''))
                continue

            # An explicit value or bit was specified.
            value = list(item)[0]
            if value.tag == 'value':
                self.values.append((item.get('name'), value.text))
            elif value.tag == 'bit':
                self.values.append((item.get('name'), '%u' % (1 << int(value.text, 0))))
                self.bits.append((item.get('name'), value.text))

    def resolve(self, module):
        self.resolved = True

    def fixed_size(self):
        return True

    out = __main__.output['enum']


class ListType(Type):
    '''
    Derived class which represents a list of some other datatype.  Fixed- or variable-sized.

    Public fields added:
    member is the datatype of the list elements.
    parent is the structure type containing the list.
    expr is an Expression object containing the length information, for variable-sized lists.
    '''
    def __init__(self, elt, member, *parent):
        Type.__init__(self, member.name)
        self.is_list = True
        self.member = member
        self.parents = list(parent)

        if elt.tag == 'list':
            elts = list(elt)
            self.expr = Expression(elts[0] if len(elts) else elt, self)
        elif elt.tag == 'valueparam':
            self.expr = Expression(elt, self)

        self.size = member.size if member.fixed_size() else None
        self.nmemb = self.expr.nmemb if self.expr.fixed_size() else None

    def make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum=None):
        if not self.fixed_size():
            # We need a length field.
            # Ask our Expression object for it's name, type, and whether it's on the wire.
            lenfid = self.expr.lenfield_type
            lenfield_name = self.expr.lenfield_name
            lenwire = self.expr.lenwire
            needlen = True

            # See if the length field is already in the structure.
            for parent in self.parents:
                for field in parent.fields:
                    if field.field_name == lenfield_name:
                        needlen = False

            # It isn't, so we need to add it to the structure ourself.
            if needlen:
                type = module.get_type(lenfid)
                lenfield_type = module.get_type_name(lenfid)
                type.make_member_of(module, complex_type, lenfield_type, lenfield_name, True, lenwire, False, enum)

        # Add ourself to the structure by calling our original method.
        Type.make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum)

    def resolve(self, module):
        if self.resolved:
            return
        self.member.resolve(module)
        self.expr.resolve(module, self.parents)

        # Find my length field again.  We need the actual Field object in the expr.
        # This is needed because we might have added it ourself above.
        if not self.fixed_size():
            for parent in self.parents:
                for field in parent.fields:
                    if field.field_name == self.expr.lenfield_name and field.wire:
                        self.expr.lenfield = field
                        break

        self.resolved = True

    def fixed_size(self):
        return self.member.fixed_size() and self.expr.fixed_size()

class ExprType(Type):
    '''
    Derived class which represents an exprfield.  Fixed size.

    Public fields added:
    expr is an Expression object containing the value of the field.
    '''
    def __init__(self, elt, member, *parents):
        Type.__init__(self, member.name)
        self.is_expr = True
        self.member = member
        self.parents = parents

        self.expr = Expression(list(elt)[0], self)

        self.size = member.size
        self.nmemb = 1

    def resolve(self, module):
        if self.resolved:
            return
        self.member.resolve(module)
        self.resolved = True

    def fixed_size(self):
        return True

class PadType(Type):
    '''
    Derived class which represents a padding field.
    '''
    def __init__(self, elt):
        Type.__init__(self, tcard8.name)
        self.is_pad = True
        self.size = 1
        self.nmemb = 1 if (elt == None) else int(elt.get('bytes'), 0)

    def resolve(self, module):
        self.resolved = True

    def fixed_size(self):
        return True

    
class ComplexType(Type):
    '''
    Derived class which represents a structure.  Base type for all structure types.

    Public fields added:
    fields is an array of Field objects describing the structure fields.
    '''
    def __init__(self, name, elt):
        Type.__init__(self, name)
        self.is_container = True
        self.elt = elt
        self.fields = []
        self.nmemb = 1
        self.size = 0
        self.lenfield_parent = [self]
        self.fds = []

    def resolve(self, module):
        if self.resolved:
            return
        pads = 0
        enum = None

        # Resolve all of our field datatypes.
        for child in list(self.elt):
            if child.tag == 'pad':
                field_name = 'pad' + str(pads)
                fkey = 'CARD8'
                type = PadType(child)
                pads = pads + 1
                visible = False
            elif child.tag == 'field':
                field_name = child.get('name')
                enum = child.get('enum')
                fkey = child.get('type')
                type = module.get_type(fkey)
                visible = True
            elif child.tag == 'exprfield':
                field_name = child.get('name')
                fkey = child.get('type')
                type = ExprType(child, module.get_type(fkey), *self.lenfield_parent)
                visible = False
            elif child.tag == 'list':
                field_name = child.get('name')
                fkey = child.get('type')
                type = ListType(child, module.get_type(fkey), *self.lenfield_parent)
                visible = True
            elif child.tag == 'valueparam':
                field_name = child.get('value-list-name')
                fkey = 'CARD32'
                type = ListType(child, module.get_type(fkey), *self.lenfield_parent)
                visible = True
            elif child.tag == 'switch':
                field_name = child.get('name')
                # construct the switch type name from the parent type and the field name
                field_type = self.name + (field_name,)
                type = SwitchType(field_type, child, *self.lenfield_parent)
                visible = True
                type.make_member_of(module, self, field_type, field_name, visible, True, False)
                type.resolve(module)
                continue
            elif child.tag == 'fd':
                fd_name = child.get('name')
                type = module.get_type('INT32')
                type.make_fd_of(module, self, fd_name)
                continue
            else:
                # Hit this on Reply
                continue

            # Get the full type name for the field
            field_type = module.get_type_name(fkey)
            # Add the field to ourself
            type.make_member_of(module, self, field_type, field_name, visible, True, False, enum)
            # Recursively resolve the type (could be another structure, list)
            type.resolve(module)

        self.calc_size() # Figure out how big we are
        self.resolved = True

    def calc_size(self):
        self.size = 0
        for m in self.fields:
            if not m.wire:
                continue
            if m.type.fixed_size():
                self.size = self.size + (m.type.size * m.type.nmemb)
            else:
                self.size = None
                break

    def fixed_size(self):
        for m in self.fields:
            if not m.type.fixed_size():
                return False
        return True

class SwitchType(ComplexType):
    '''
    Derived class which represents a List of Items.  

    Public fields added:
    bitcases is an array of Bitcase objects describing the list items
    '''

    def __init__(self, name, elt, *parents):
        ComplexType.__init__(self, name, elt)
        self.parents = parents
        # FIXME: switch cannot store lenfields, so it should just delegate the parents
        self.lenfield_parent = list(parents) + [self]
        # self.fields contains all possible fields collected from the Bitcase objects, 
        # whereas self.items contains the Bitcase objects themselves
        self.bitcases = []

        self.is_switch = True
        elts = list(elt)
        self.expr = Expression(elts[0] if len(elts) else elt, self)

    def resolve(self, module):
        if self.resolved:
            return
#        pads = 0

        parents = list(self.parents) + [self]

        # Resolve all of our field datatypes.
        for index, child in enumerate(list(self.elt)):
            if child.tag == 'bitcase':
                field_name = child.get('name')
                if field_name is None:
                    field_type = self.name + ('bitcase%d' % index,)
                else:
                    field_type = self.name + (field_name,)

                # use self.parent to indicate anchestor, 
                # as switch does not contain named fields itself
                type = BitcaseType(index, field_type, child, *parents)
                # construct the switch type name from the parent type and the field name
                if field_name is None:
                    type.has_name = False
                    # Get the full type name for the field
                    field_type = type.name               
                visible = True

                # add the field to ourself
                type.make_member_of(module, self, field_type, field_name, visible, True, False)

                # recursively resolve the type (could be another structure, list)
                type.resolve(module)
                inserted = False
                for new_field in type.fields:
                    # We dump the _placeholder_byte if any fields are added.
                    for (idx, field) in enumerate(self.fields):
                        if field == _placeholder_byte:
                            self.fields[idx] = new_field
                            inserted = True
                            break
                    if False == inserted:
                        self.fields.append(new_field)

        self.calc_size() # Figure out how big we are
        self.resolved = True

    def make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum=None):
        if not self.fixed_size():
            # We need a length field.
            # Ask our Expression object for it's name, type, and whether it's on the wire.
            lenfid = self.expr.lenfield_type
            lenfield_name = self.expr.lenfield_name
            lenwire = self.expr.lenwire
            needlen = True

            # See if the length field is already in the structure.
            for parent in self.parents:
                for field in parent.fields:
                    if field.field_name == lenfield_name:
                        needlen = False

            # It isn't, so we need to add it to the structure ourself.
            if needlen:
                type = module.get_type(lenfid)
                lenfield_type = module.get_type_name(lenfid)
                type.make_member_of(module, complex_type, lenfield_type, lenfield_name, True, lenwire, False, enum)

        # Add ourself to the structure by calling our original method.
        Type.make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum)

    # size for switch can only be calculated at runtime
    def calc_size(self):
        pass

    # note: switch is _always_ of variable size, but we indicate here wether 
    # it contains elements that are variable-sized themselves
    def fixed_size(self):
        return False
#        for m in self.fields:
#            if not m.type.fixed_size():
#                return False
#        return True


class Struct(ComplexType):
    '''
    Derived class representing a struct data type.
    '''
    out = __main__.output['struct']


class Union(ComplexType):
    '''
    Derived class representing a union data type.
    '''
    def __init__(self, name, elt):
        ComplexType.__init__(self, name, elt)
        self.is_union = True

    out = __main__.output['union']


class BitcaseType(ComplexType):
    '''
    Derived class representing a struct data type.
    '''
    def __init__(self, index, name, elt, *parent):
        elts = list(elt)
        self.expr = []
        fields = []
        for elt in elts:
            if elt.tag == 'enumref':
                self.expr.append(Expression(elt, self))
            else:
                fields.append(elt)
        ComplexType.__init__(self, name, fields)
        self.has_name = True
        self.index = 1
        self.lenfield_parent = list(parent) + [self]
        self.parents = list(parent)
        self.is_bitcase = True

    def make_member_of(self, module, switch_type, field_type, field_name, visible, wire, auto, enum=None):
        '''
        register BitcaseType with the corresponding SwitchType

        module is the global module object.
        complex_type is the structure object.
        see Field for the meaning of the other parameters.
        '''
        new_field = Field(self, field_type, field_name, visible, wire, auto, enum)

        # We dump the _placeholder_byte if any bitcases are added.
        for (idx, field) in enumerate(switch_type.bitcases):
            if field == _placeholder_byte:
                switch_type.bitcases[idx] = new_field
                return

        switch_type.bitcases.append(new_field)

    def resolve(self, module):
        if self.resolved:
            return

        for e in self.expr:
            e.resolve(module, self.parents+[self])

        # Resolve the bitcase expression
        ComplexType.resolve(self, module)


class Reply(ComplexType):
    '''
    Derived class representing a reply.  Only found as a field of Request.
    '''
    def __init__(self, name, elt):
        ComplexType.__init__(self, name, elt)
        self.is_reply = True
        self.doc = None

        for child in list(elt):
            if child.tag == 'doc':
                self.doc = Doc(name, child)

    def resolve(self, module):
        if self.resolved:
            return
        # Add the automatic protocol fields
        self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True))
        self.fields.append(_placeholder_byte)
        self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True))
        self.fields.append(Field(tcard32, tcard32.name, 'length', False, True, True))
        ComplexType.resolve(self, module)
        

class Request(ComplexType):
    '''
    Derived class representing a request.

    Public fields added:
    reply contains the reply datatype or None for void requests.
    opcode contains the request number.
    '''
    def __init__(self, name, elt):
        ComplexType.__init__(self, name, elt)
        self.reply = None
        self.doc = None
        self.opcode = elt.get('opcode')

        for child in list(elt):
            if child.tag == 'reply':
                self.reply = Reply(name, child)
            if child.tag == 'doc':
                self.doc = Doc(name, child)

    def resolve(self, module):
        if self.resolved:
            return
        # Add the automatic protocol fields
        if module.namespace.is_ext:
            self.fields.append(Field(tcard8, tcard8.name, 'major_opcode', False, True, True))
            self.fields.append(Field(tcard8, tcard8.name, 'minor_opcode', False, True, True))
            self.fields.append(Field(tcard16, tcard16.name, 'length', False, True, True))
            ComplexType.resolve(self, module)
        else:
            self.fields.append(Field(tcard8, tcard8.name, 'major_opcode', False, True, True))
            self.fields.append(_placeholder_byte)
            self.fields.append(Field(tcard16, tcard16.name, 'length', False, True, True))
            ComplexType.resolve(self, module)

        if self.reply:
            self.reply.resolve(module)

    out = __main__.output['request']


class Event(ComplexType):
    '''
    Derived class representing an event data type.

    Public fields added:
    opcodes is a dictionary of name -> opcode number, for eventcopies.
    '''
    def __init__(self, name, elt):
        ComplexType.__init__(self, name, elt)
        self.opcodes = {}

        self.has_seq = not bool(elt.get('no-sequence-number'))

        self.is_ge_event = bool(elt.get('xge'))

        self.doc = None
        for item in list(elt):
            if item.tag == 'doc':
                self.doc = Doc(name, item)

    def add_opcode(self, opcode, name, main):
        self.opcodes[name] = opcode
        if main:
            self.name = name

    def resolve(self, module):
        def add_event_header():
            self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True))
            if self.has_seq:
                self.fields.append(_placeholder_byte)
                self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True))

        def add_ge_event_header():
            self.fields.append(Field(tcard8,  tcard8.name,  'response_type', False, True, True))
            self.fields.append(Field(tcard8,  tcard8.name,  'extension', False, True, True))
            self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True))
            self.fields.append(Field(tcard32, tcard32.name, 'length', False, True, True))
            self.fields.append(Field(tcard16, tcard16.name, 'event_type', False, True, True))

        if self.resolved:
            return

        # Add the automatic protocol fields
        if self.is_ge_event:
            add_ge_event_header()
        else:
            add_event_header()

        ComplexType.resolve(self, module)

    out = __main__.output['event']


class Error(ComplexType):
    '''
    Derived class representing an error data type.

    Public fields added:
    opcodes is a dictionary of name -> opcode number, for errorcopies.
    '''
    def __init__(self, name, elt):
        ComplexType.__init__(self, name, elt)
        self.opcodes = {}

    def add_opcode(self, opcode, name, main):
        self.opcodes[name] = opcode
        if main:
            self.name = name

    def resolve(self, module):
        if self.resolved:
            return

        # Add the automatic protocol fields
        self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True))
        self.fields.append(Field(tcard8, tcard8.name, 'error_code', False, True, True))
        self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True))
        ComplexType.resolve(self, module)

    out = __main__.output['error']


class Doc(object):
    '''
    Class representing a <doc> tag.
    '''
    def __init__(self, name, elt):
        self.name = name
        self.description = None
        self.brief = 'BRIEF DESCRIPTION MISSING'
        self.fields = {}
        self.errors = {}
        self.see = {}
        self.example = None

        for child in list(elt):
            text = child.text if child.text else ''
            if child.tag == 'description':
                self.description = text.strip()
            if child.tag == 'brief':
                self.brief = text.strip()
            if child.tag == 'field':
                self.fields[child.get('name')] = text.strip()
            if child.tag == 'error':
                self.errors[child.get('type')] = text.strip()
            if child.tag == 'see':
                self.see[child.get('name')] = child.get('type')
            if child.tag == 'example':
                self.example = text.strip()



_placeholder_byte = Field(PadType(None), tcard8.name, 'pad0', False, True, False)
