#!/usr/bin/python

import sys
import re
from string import *

class Compiler:
    def __init__( self ):
	self.inclass = 0
	self.classname = ""
	self.error = 0

    def match( self, line, exp ):
	m = re.compile( exp ).search( line )
	if m == None:
	    return m
	self.groups = m.groups()
	return m

    def parseMethod( self, mg, prop ):
	params = split( strip( mg[5] ), "," )
	plist = []
	for p in params:
	    p = strip( p )
	    if p != "":
		if self.match( p, r"(const\s)?\s*([A-Za-z0-9_]+)\s*([*&])?([A-Za-z0-9_]+)?\s*(=\s*[-+A-Za-z0-9_:]+)?" ) == None:
		    print "---------------- ERROR ------------------"
		    self.error = 1;
		    return
		print "    ", self.groups
		plist.append( self.groups )
		
	method = ( mg[0], mg[1], mg[2], mg[3], mg[4], plist, mg[6], prop )

	if not self.methods.has_key( method[4] ):
	    self.methods[ method[4] ] = []
	self.methods[ method[4] ].append( method )

    def parseSetProperty( self, func, xtype, prop ):
	if self.match( xtype, r"(const\s)?\s*([A-Za-z0-9_]+)\s*([*&])?([A-Za-z0-9_]+)?\s*(=\s*[-+A-Za-z0-9_:]+)?" ) == None:
	    print "---------------- ERROR ------------------"
	    self.error = 1;
	    return
	method = ( func, self.groups[1], self.groups[2], prop )
	self.writeProps[ prop ] = method

    def parseGetProperty( self, const, type, qualifier, func, prop ):
	method = ( func, type, qualifier, prop )
	self.readProps[ prop ] = method

    def main( self, input ):
	for line in input.readlines():
	    if self.inclass == 0:
		if self.match( line, "class Q_EXPORT ([A-Za-z0-9_]+)") != None:
		    self.classname = self.groups[0]
		    self.inclass = 1
		    self.methods = {}
		    self.readProps = {}
		    self.writeProps = {}
		    self.propFuncs = []
		    self.enums = {}
		    self.inenum = 0
	    else:
		# Check for enums
		if self.inenum == 1:
		    if self.match( line, r'};' ) != None:
			self.inenum = 0
			self.enums[ self.enumname ] = self.enum
			line = ""
		    elif self.match( line, r'([A-Za-z0-9_]+)' ) != None:
			self.enum.append( self.groups[0] )
		elif self.match( line, r'enum\s+([A-Za-z0-9_]+)\s*{' ) != None:
		    self.inenum = 1
		    self.enum = []
		    self.enumname = self.groups[0]

		prop = 0
		# Check for properties
		if self.match( line, r'(virtual\s)?\s*void\s+([A-Za-z0-9_]+)\s*\(\s*(.*)\s*\)\s+prop\s*\(([A-Za-z0-9_]+)\)' ):
		    self.parseSetProperty( self.groups[1], self.groups[2], self.groups[3] )
		    prop = 1
		elif self.match( line, r'(virtual\s)?\s*(const\s)?\s*([A-Za-z0-9_]+)\s*([*&]|\s)\s*([A-Za-z0-9_]+)\s*\(\s*\)\s*(const)?\s+prop\(([A-Za-z0-9_]+)\)' ):
		    self.parseGetProperty( self.groups[1], self.groups[2], self.groups[3], self.groups[4], self.groups[6] )
		    prop = 1

		# Check for methods
		if self.match( line, r'%s\s*\(\s*([^)]*)\s*\)' % self.classname ) != None:
		    print ( None, None, "void", None, self.classname, self.groups[0], None )
		    self.parseMethod( ( None, None, "void", None, self.classname, self.groups[0], None ), 0 )
		elif self.match( line, r'(virtual\s|static\s)?\s*(const\s)?\s*([A-Za-z0-9_]+)\s*([*&]|\s)\s*([A-Za-z0-9_]+)\s*\(\s*([^)]*)\s*\)\s*(const)?' ) != None:
		    print self.groups
		    self.parseMethod( self.groups, prop )
		elif self.match( line, "};") != None:
		    self.inclass = 0

#	print r'static PyQt_ClassInfo* pyqt_%s_class = 0;' % lower( self.classname );

        for e in self.enums.keys():
	    print r'static PyQt_Enum pyqt_%s_%s[] = {' % ( lower( self.classname ), e )
	
	    for k in self.enums[ e ]:
		print r'    { (int)%s::%s, "%s" },' % ( self.classname, k, k )
	    print r'    { 0, 0 }'
	    print r'};'
	    print

	print
	print r'static void pyqt_%s_free( void* ptr )' % lower( self.classname );
	print r'{'
	print r'    delete (%s*)ptr;' % self.classname;
	print r'}'
	print

	print r'static %s* pyqt_%s_toCpp( PyObject* obj )' % ( self.classname, lower( self.classname ) )
	print r'{'
	print r'    return (%s*)getPointer( obj, %s );' % ( self.classname, self.classname[1:] )
	print r'}'
	print

	print r'static PyObject* pyqt_%s_toPython( const %s& ptr )' % ( lower( self.classname ), self.classname )
	print r'{'
	print r'    return qobjectToPython( new %s( ptr ), %s );' % ( self.classname, self.classname[1:] )
	print r'}'
	print

	for name in self.methods.keys():

	    # Constructor
	    if name == self.classname:
		print r'static PyObject* pyqt_%s_constructor( void* /* ptr */, PyObject* obj, PyObject* arg_tuple )' % ( lower( self.classname ) )
		print r'{'
		print r'    if ( !obj )'
		print r'        return NULL;'
	    # Static method
	    elif self.methods[ name ][0][0] == "static ":
		print r'static PyObject* pyqt_%s_%s( void* /* ptr */, PyObject* /* obj */, PyObject* arg_tuple )' % ( lower( self.classname ), name )
		print r'{'
	    # Usual method
	    else:
		# Iterate over all methods to see wether one returns "Self"
		has_self = 0
		for method in self.methods[ name ]:
		    if method[2] == "Self":
			has_self = 1
		if has_self == 1:
		    print r'static PyObject* pyqt_%s_%s( void* ptr, PyObject* obj, PyObject* arg_tuple )' % ( lower( self.classname ), name )
		else:
		    print r'static PyObject* pyqt_%s_%s( void* ptr, PyObject* /* obj */, PyObject* arg_tuple )' % ( lower( self.classname ), name )
		print r'{'
		print r'    if ( !ptr )'
		print r'        return NULL;'

	    pcount = 0
	    mnumber = 0

	    # Iterate over all methods with the same name
	    for method in self.methods[ name ]:

		params = ''
		post = ""
		# No parameters ?
		if len( method[5] ) == 0:
		    print r'    if ( PyTuple_Size( arg_tuple ) == 0 )'
		    mnumber = mnumber + 1
		# One or more parameters
		else:
		    format = ""
		    args = ""
		    cond = ""
		    has_default = 0
		    # Iterate over all parameters
		    for param in method[5]:
			arg = "&_%iparam" % pcount
			p = "_%iparam" % pcount			
			
			# The default parameter
			default = param[4]
			if default == None:
			    default = ""
			elif has_default == 0:
			    has_default = 1
			    format = format + "|"

			# switch( type )
			if param[1] == "int" or param[1] == "uint":
			    format = format + "i"
			    print r'    %s _%iparam%s;' % ( param[1], pcount, default )
			elif param[1] == "double":
			    format = format + "d"
			    print r'    %s _%iparam%s;' % ( param[1], pcount, default )
			elif param[1] == "float":
			    format = format + "d"
			    print r'    double _%iparam%s;' % ( pcount, default )
			elif param[1] == "bool":
			    format = format + "b"
			    print r'    %s _%iparam%s;' % ( param[1], pcount, default )
			elif param[1] == "char" and param[2] == '*':
			    format = format + "s"
			    # default parameter is always the empty string
			    print r'    char* _%iparam = 0;' % pcount
			elif param[1] == "char":
			    format = format + "c"
			    print r'    %s _%iparam%s;' % ( param[1], pcount, default )
			elif param[1] == "QString":
			    format = format + "s"
			    # default parameter is always the empty string
			    print r'    char* _%iparam = 0;' % pcount
			elif param[1] == "QCString":
			    format = format + "s"
			    # default parameter is always the empty string
			    print r'    char* _%iparam = 0;' % pcount
			elif param[1] == "QByteArray":
			    format = format + "s#"
			    print r'    char* _%idata;' % pcount
			    print r'    int   _%ilen;' % pcount
			    post = post + '        PyQt_ByteArray _%iparam( _%idata, _%ilen );\n' % ( pcount, pcount, pcount )
			    arg = "&_%idata, &_%ilen" % ( pcount, pcount )
			elif param[1] == "QStringList":
			    format = format + "O"
			    print r'    PyObject* _%iobj;' % pcount
			    print r'    QStringList _%iparam%s;' % ( pcount, default )
			    cond = cond + " && ( !_%iobj || stringListToCpp( _%iobj, &_%iparam ) )" % ( pcount, pcount, pcount )
			    arg = "&_%iobj" % pcount
			elif param[1] == "QPointArray":
			    format = format + "O"
			    print r'    PyObject* _%iobj;' % pcount
			    print r'    QPointArray _%iparam%s;' % ( pcount, default )
			    cond = cond + " && ( !_%iobj || pointArrayToCpp( _%iobj, &_%iparam ) )" % ( pcount, pcount, pcount )
			    arg = "&_%iobj" % pcount
			# Enums
			elif self.enums.has_key( param[1] ):
			    format = format + "s"
			    print r'    char* _%ienum = 0;' % pcount
			    print r'    int   _%iparam%s;' % ( pcount, default )
			    cond = cond + " && ( !_%ienum || enumToCpp( pyqt_%s_%s, _%ienum, &_%iparam ) )" % ( pcount, lower( self.classname ), param[1], pcount, pcount )
			    p = r'(%s::%s)%s' % ( self.classname, param[1], p )
			    arg = "&_%ienum" % pcount
			# Objects
			else:
			    format = format + "O"
			    # default is always the null object
			    print r'    PyObject* _%iparam = 0;' % pcount
			    cond = cond + " && inherits( _%iparam, %s )" % ( pcount, param[1][1:] )
			    p = r'*pyqt_%s_toCpp( %s )' % ( lower( param[1] ), p )

			# Construct argument list for PyArg_ParseTuple
			if args != "":
			    args = args + ", " + arg
			else:
			    args = arg

			# Construct argument list for method call
			if params != "":
			    params = params + ", " + p
			else:
			    params = p
			pcount = pcount + 1
		    print r'    if ( PyArg_ParseTuple( arg_tuple, "%s", %s ) %s )' % ( format, args, cond )
		    mnumber = mnumber + 1
		print r'    {'
		print post,

		if mnumber > 1:
		    print r'        PyErr_Clear();'

		call = ''
		# Constructor
		if name == self.classname:
		    if len( method[5] ) == 0:
			call = r'void* ptr = new %s' % self.classname
		    else:
			call = r'void* ptr = new %s( %s )' % ( self.classname, params )
		# Static method
		elif method[0] == "static ":
		    call = r'%s::%s(%s)' % ( self.classname, name, params )
		# Method
		else:
		    call = r'((%s*)ptr)->%s(%s)' % ( self.classname, name, params )

		if method[2] == "void":
		    print r'        %s;' % call
		    if name == self.classname:
			print r'        mapObject( obj, ptr, pyqt_%s_class, Python );' % lower( self.classname )
		    print r'        Py_INCREF( Py_None );'
		    print r'        return Py_None;'
		elif method[2] == "int":
		    print r'        return PyInt_FromLong( %s );' % call
		elif method[2] == "uint":
		    print r'        return PyLong_FromUnsignedLong( %s );' % call
		elif method[2] == "bool":
		    print r'        return Py_BuildValue("b", (char)%s );' % call
		elif method[2] == "double":
		    print r'        return PyFloat_FromDouble( %s );' % call
		elif method[2] == "float":
		    print r'        return PyFloat_FromDouble( %s );' % call
		elif method[2] == "char" and method[3] == "*":
		    print r'        return Py_BuildValue("s", %s );' % call
		elif method[2] == "char":
		    print r'        return Py_BuildValue("b", (char)%s );' % call
		elif method[2] == "QString":
		    print r'        return PyString_FromString( %s.latin1() );' % call
		elif method[2] == "QCString":
		    print r'        return PyString_FromString( %s.data() );' % call
		elif method[2] == "Self":
		    print r'        %s' % call
		    print r'        Py_INCREF( obj );'
		    print r'        return obj;'
		# Enums
		elif self.enums.has_key( method[2] ):
		    print r'        return enumToPython( pyqt_%s_%s, (int)%s );' % ( lower( self.classname ), method[2], call )
		# Objects
		else:
		    print r'        return pyqt_%s_toPython( %s );' % ( lower( method[2] ), call )
		print r'    }'
	    print r'    return NULL;'
	    print r'}'
	    print

	print r'static PyQt_Method pyqt_%s_methods[] = {' % lower( self.classname )
	for name in self.methods.keys():
	    n = name
	    if n == self.classname:
		n = "constructor"
	    print r'    pyqt_%s_%s,' % ( lower( self.classname ), n )
	print r'    0'
	print r'};'
	print

	print r'static PyQt_SignalMapping pyqt_%s_signals[] = {' % lower( self.classname )
	print r'    { 0, 0, 0 }'
	print r'};'
	print

	print r'static void pyqt_%s_init()' % lower( self.classname )
	print r'{'
	print r'    if ( pyqt_%s_class )' % lower( self.classname )
	print r'        return;'
	print
	print r'    pyqt_%s_class = new PyQt_ClassInfo;' % lower( self.classname )
	print r'    pyqt_%s_class->freeQt = pyqt_%s_free;' % ( lower( self.classname ), lower( self.classname ) )
	print r'    pyqt_%s_class->methods = pyqt_%s_methods;' % ( lower( self.classname ), lower( self.classname) )
	print r'    pyqt_%s_class->methodCount = methodCount( pyqt_%s_methods );' % ( lower( self.classname ), lower( self.classname) )
	print r'    pyqt_%s_class->next = 0;' % lower( self.classname )
	print r'    pyqt_%s_class->classid = %s;' % ( lower( self.classname ), self.classname[1:] )
	print r'    pyqt_%s_class->sigs = pyqt_%s_signals;' % ( lower( self.classname ), lower( self.classname) )
	print r'    pyqt_%s_class->classObject = PyDict_GetItemString( pyqt_qt_dict, "%s" );' % ( lower( self.classname ), self.classname )
	print r'    pyqt_%s_class->className = "%s";' % ( lower( self.classname ), self.classname )
	print r'    ASSERT( pyqt_%s_class->classObject );' % lower( self.classname )
	print r'}'
	print

	print "------------------------------------------------------------------------"

	mcount = 0
	print r'class %s:' % self.classname

	numbers = {}

	# Since the static methods are not in class space we have to save the
	# stuff in this variable and print it later
	static = ""

	# Print out methods
	for name in self.methods.keys():

	    # Remember the number for the properties (see below)
	    numbers[ name ] = mcount

	    # Is it only a property access method ?
	    # We need to check all methods of this name to be shure
	    prop = 1
	    for m in self.methods[ name ]:
		if m[7] == 0:
		    prop = 0

	    method = self.methods[ name ][0]

	    if name == self.classname:
		print r'    def __init__( self, *args ):'
		print r'        if len( args ) != 1 or type( args[0] ) != CObjectType:'
		print r'            pyqt.constructor_gate( self, 2, %i, args )' % mcount
	        print r'        else:'
		print r'            self.__dict__[ "_ptr_" ] = args[0]'
		print
	    elif prop == 0:
		params = ""
		# Multiple methods of the same name ?
		if len( self.methods[ name ] ) > 1:
		    if method[0] == "static ":
			static = static + 'def %s_%s( *args ):\n' % ( self.classname, name )
		    else:
			print r'    def %s( self, *args ):' % name
		    params = "args"
		# Only one method of this name
		else:
		    # No parameters ?
		    if len( method[5] ) == 0:
			if method[0] == "static ":
			    static = static + 'def %s_%s():\n' % ( self.classname, name )
			else:
			    print r'    def %s( self ):' % name
			params = "()"
		    # Has parameters
		    else:
			count = 0
			plist = ""
			for param in method[5]:
			    if count > 0:
				plist = plist + ", "
			    plist = plist + ( "arg%i" % count );
			    count = count + 1
			if method[0] == "static ":
			    static = static + 'def %s_%s( %s ):\n' % ( self.classname, name, plist )
			else:
			    print r'    def %s( self, %s ):' % ( name, plist )
			params = "(%s,)" % plist
		
		# We assume that methods with the same name but different signature
		# either all return a value or none of them, so we just look at the
		# first one
		if method[0] == "static ":
		    # Does it return something ?
		    if method[2] == "void":
			static = static + '    pyqt.static_gate( 2, %i, %s )\n' % ( mcount, params )
		    else:
			static = static + '    return pyqt.static_gate( 2, %i, %s )\n' % ( mcount, params )
		else:
		    # Does it return something ?
		    if method[2] == "void":
			print r'        pyqt.gate( self, 2, %i, %s )' % ( mcount, params )
		    else:
			print r'        return pyqt.gate( self, 2, %i, %s )' % ( mcount, params )
			print
	    mcount = mcount + 1

	# Print out readable properties
	if len( self.readProps.keys() ) > 0:
	    print r'    def __getattr__( self, name ):'
	    print r'        if %s_readattr.has_key( name ):' % self.classname
	    print r'            return pyqt.gate( self, 2, %s_readattr[ name ], () )' % self.classname
	    print r'        else:'
	    print r'            return self.__dict__[ name ]'
	    print

	# Print out writeable properties
	if len( self.writeProps.keys() ) > 0:
	    print r'    def __setattr__( self, name, value ):'
	    print r'        if name == "_ptr_":'
	    print r'            self.__dict__[ name ] = value'
	    print r'            return'
	    print r'        if %s_writeattr.has_key( name ):' % self.classname
	    print r'            pyqt.gate( self, 2, %s_writeattr[ name ], (value,) )' % self.classname
	    print r'        else:'
	    print r'            self.__dict__[ name ] = value'
	    print

	# Register properties
	if len( self.readProps.keys() ) > 0:
	    print r'%s_readattr = { ' % ( self.classname ),
	    for prop in self.readProps.keys():
		print r'"%s":%i, ' % ( prop, numbers[ self.readProps[ prop ][0] ] ),
	    print r'}'
	if len( self.writeProps.keys() ) > 0:
	    print r'%s_writeattr = { ' % ( self.classname ),
	    for prop in self.writeProps.keys():
		print r'"%s":%i, ' % ( prop, numbers[ self.writeProps[ prop ][0] ] ),
	    print r'}'
	    print

	# Print the static stuff
	print static
	
c = Compiler()
c.main( sys.stdin )

