"""Utilities for makegw - Parse a header file to build an interface

 This module contains the core code for parsing a header file describing a
 COM interface, and building it into an "Interface" structure.

 Each Interface has methods, and each method has arguments.

 Each argument knows how to use Py_BuildValue or Py_ParseTuple to
 exchange itself with Python.
 
 See the @win32com.makegw@ module for information in building a COM
 interface
"""
import re
import traceback
import string

class error_not_found(Exception):
	def __init__(self, msg="The requested item could not be found"):
		super(error_not_found, self).__init__(msg)

class error_not_supported(Exception):
	def __init__(self, msg="The required functionality is not supported"):
		super(error_not_supported, self).__init__(msg)

VERBOSE=0
DEBUG=0

## NOTE : For interfaces as params to work correctly, you must
## make sure any PythonCOM extensions which expose the interface are loaded
## before generating.


class ArgFormatter:
	"""An instance for a specific type of argument.	 Knows how to convert itself"""
	def __init__(self, arg, builtinIndirection, declaredIndirection = 0):
		#print 'init:', arg.name, builtinIndirection, declaredIndirection, arg.indirectionLevel
		self.arg = arg
		self.builtinIndirection = builtinIndirection
		self.declaredIndirection = declaredIndirection
		self.gatewayMode = 0
	def _IndirectPrefix(self, indirectionFrom, indirectionTo):
		"""Given the indirection level I was declared at (0=Normal, 1=*, 2=**)
		return a string prefix so I can pass to a function with the
		required indirection (where the default is the indirection of the method's param.
		
		eg, assuming my arg has indirection level of 2, if this function was passed 1
		it would return "&", so that a variable declared with indirection of 1
		can be prefixed with this to turn it into the indirection level required of 2
		"""
		dif = indirectionFrom - indirectionTo
		if dif==0:
		  return ""
		elif dif==-1:
		  return "&"
		elif dif==1:
		  return "*"
		else:
		  return "?? (%d)" % (dif,)
		  raise error_not_supported, "Can't indirect this far - please fix me :-)"
	def GetIndirectedArgName(self, indirectFrom, indirectionTo):
		#print 'get:',self.arg.name, indirectFrom,self._GetDeclaredIndirection() + self.builtinIndirection, indirectionTo, self.arg.indirectionLevel

		if indirectFrom is None:
			### ACK! this does not account for [in][out] variables.
			### when this method is called, we need to know which
			indirectFrom = self._GetDeclaredIndirection() + self.builtinIndirection

		return self._IndirectPrefix(indirectFrom, indirectionTo) + self.arg.name
	def GetBuildValueArg(self):
		"Get the argument to be passes to Py_BuildValue"
		return self.arg.name
	def GetParseTupleArg(self):
		"Get the argument to be passed to PyArg_ParseTuple"
		if self.gatewayMode:
			# use whatever they were declared with
			return self.GetIndirectedArgName(None, 1)
		# local declarations have just their builtin indirection
		return self.GetIndirectedArgName(self.builtinIndirection, 1)
	def GetInterfaceCppObjectInfo(self):
		"""Provide information about the C++ object used.
				
		Simple variables (such as integers) can declare their type (eg an integer)
		and use it as the target of both PyArg_ParseTuple and the COM function itself.
		
		More complex types require a PyObject * declared as the target of PyArg_ParseTuple,
		then some conversion routine to the C++ object which is actually passed to COM.
		
		This method provides the name, and optionally the type of that C++ variable.  
		If the type if provided, the caller will likely generate a variable declaration.
		The name must always be returned.
		
		Result is a tuple of (variableName, [DeclareType|None|""])
		"""

		# the first return element is the variable to be passed as
		#	 an argument to an interface method. the variable was
		#	 declared with only its builtin indirection level. when
		#	 we pass it, we'll need to pass in whatever amount of
		#	 indirection was applied (plus the builtin amount)
		# the second return element is the variable declaration; it
		#	 should simply be builtin indirection
		return self.GetIndirectedArgName(self.builtinIndirection, self.arg.indirectionLevel + self.builtinIndirection), \
					   "%s %s" % (self.GetUnconstType(), self.arg.name)

	def GetInterfaceArgCleanup(self):
		"Return cleanup code for C++ args passed to the interface method."
		if DEBUG:
			return "/* GetInterfaceArgCleanup output goes here: %s */\n" % self.arg.name
		else:
			return ""

	def GetInterfaceArgCleanupGIL(self):
		"""Return cleanup code for C++ args passed to the interface
		method that must be executed with the GIL held"""
		if DEBUG:
			return "/* GetInterfaceArgCleanup (GIL held) output goes here: %s */\n" % self.arg.name
		else:
			return ""

	def GetUnconstType(self):
		return self.arg.unc_type
	
	def SetGatewayMode(self):
		self.gatewayMode = 1
	def _GetDeclaredIndirection(self):
		return self.arg.indirectionLevel
		print 'declared:', self.arg.name, self.gatewayMode
		if self.gatewayMode:
			return self.arg.indirectionLevel
		else:
			return self.declaredIndirection
	def DeclareParseArgTupleInputConverter(self):
		"Declare the variable used as the PyArg_ParseTuple param for a gateway"
		# Only declare it??
		#if self.arg.indirectionLevel==0:
		#	return "\t%s %s;\n" % (self.arg.type, self.arg.name)
		#else:
		if DEBUG:
			return "/* Declare ParseArgTupleInputConverter goes here: %s */\n" % self.arg.name
		else:
			return ""
	def GetParsePostCode(self):
		"Get a string of C++ code to be executed after (ie, to finalise) the PyArg_ParseTuple conversion"
		if DEBUG:
			return "/* GetParsePostCode code goes here: %s */\n" % self.arg.name
		else:
			return ""
	def GetBuildForInterfacePreCode(self):
		"Get a string of C++ code to be executed before (ie, to initialise) the Py_BuildValue conversion for Interfaces"
		if DEBUG:
			return "/* GetBuildForInterfacePreCode goes here: %s */\n" % self.arg.name
		else:
			return ""
	def GetBuildForGatewayPreCode(self):
		"Get a string of C++ code to be executed before (ie, to initialise) the Py_BuildValue conversion for Gateways"
		s = self.GetBuildForInterfacePreCode() # Usually the same
		if DEBUG:
			if s[:4] == "/* G":
				s = "/* GetBuildForGatewayPreCode goes here: %s */\n" % self.arg.name
		return s
	def GetBuildForInterfacePostCode(self):
		"Get a string of C++ code to be executed after (ie, to finalise) the Py_BuildValue conversion for Interfaces"
		if DEBUG:
			return "/* GetBuildForInterfacePostCode goes here: %s */\n" % self.arg.name
		return ""
	def GetBuildForGatewayPostCode(self):
		"Get a string of C++ code to be executed after (ie, to finalise) the Py_BuildValue conversion for Gateways"
		s = self.GetBuildForInterfacePostCode() # Usually the same
		if DEBUG:
			if s[:4] == "/* G":
				s = "/* GetBuildForGatewayPostCode goes here: %s */\n" % self.arg.name
		return s
	def GetAutoduckString(self):
		return '// @pyparm %s|%s||Description for %s' % (self._GetPythonTypeDesc(), self.arg.name, self.arg.name)
	def _GetPythonTypeDesc(self):
		"Returns a string with the description of the type.	 Used for doco purposes"
		return None
	def NeedUSES_CONVERSION(self):
		"Determines if this arg forces a USES_CONVERSION macro"
		return 0

# Special formatter for floats since they're smaller than Python floats.
class ArgFormatterFloat(ArgFormatter):
	def GetFormatChar(self):
		return "f"
	def DeclareParseArgTupleInputConverter(self):
		# Declare a double variable
		return "\tdouble dbl%s;\n" % self.arg.name
	def GetParseTupleArg(self):
		return "&dbl" + self.arg.name
	def _GetPythonTypeDesc(self):
		return "float"
	def GetBuildValueArg(self):
		return "&dbl" + self.arg.name
	def GetBuildForInterfacePreCode(self):
		return "\tdbl" + self.arg.name + " = " + self.arg.name + ";\n"
	def GetBuildForGatewayPreCode(self):
		return "\tdbl%s = " % self.arg.name + self._IndirectPrefix( \
			self._GetDeclaredIndirection(),
			0) + self.arg.name + ";\n"
	def GetParsePostCode(self):
		s = "\t"
		if self.gatewayMode:
			s = s + self._IndirectPrefix( 
				self._GetDeclaredIndirection(),
				0)
		s = s + self.arg.name
		s = s + " = (float)dbl%s;\n" % self.arg.name
		return s

# Special formatter for Shorts because they're
# a different size than Python ints!
class ArgFormatterShort(ArgFormatter):
	def GetFormatChar(self):
		return "i"
	def DeclareParseArgTupleInputConverter(self):
		# Declare a double variable
		return "\tINT i%s;\n" % self.arg.name
	def GetParseTupleArg(self):
		return "&i" + self.arg.name
	def _GetPythonTypeDesc(self):
		return "int"
	def GetBuildValueArg(self):
		return "&i" + self.arg.name
	def GetBuildForInterfacePreCode(self):
		return "\ti" + self.arg.name + " = " + self.arg.name + ";\n"
	def GetBuildForGatewayPreCode(self):
		return "\ti%s = " % self.arg.name + self._IndirectPrefix( \
			self._GetDeclaredIndirection(),
			0) + self.arg.name + ";\n"
	def GetParsePostCode(self):
		s = "\t"
		if self.gatewayMode:
			s = s + self._IndirectPrefix( 
				self._GetDeclaredIndirection(),
				0)
		s = s + self.arg.name
		s = s + " = i%s;\n" % self.arg.name
		return s

# for types which are 64bits on AMD64 - eg, HWND
class ArgFormatterLONG_PTR(ArgFormatter):
	def GetFormatChar(self):
		return "O"
	def DeclareParseArgTupleInputConverter(self):
		# Declare a PyObject variable
		return "\tPyObject *ob%s;\n" % self.arg.name
	def GetParseTupleArg(self):
		return "&ob"+self.arg.name
	def _GetPythonTypeDesc(self):
		return "int/long"
	def GetBuildValueArg(self):
		return "ob" + self.arg.name
	def GetBuildForInterfacePostCode(self):
		return "\tPy_XDECREF(ob%s);\n" % self.arg.name
	def DeclareParseArgTupleInputConverter(self):
		# Declare a PyObject variable
		return "\tPyObject *ob%s;\n" % self.arg.name

	def GetParsePostCode(self):
		return "\tif (bPythonIsHappy && !PyWinLong_AsULONG_PTR(ob%s, (ULONG_PTR *)%s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
	def GetBuildForInterfacePreCode(self):
		notdirected = self.GetIndirectedArgName(None, 1)
		return "\tob%s = PyWinObject_FromULONG_PTR(%s);\n" % \
			   (self.arg.name, notdirected)
	def GetBuildForGatewayPostCode(self):
		return "\tPy_XDECREF(ob%s);\n" % self.arg.name

class ArgFormatterPythonCOM(ArgFormatter):
	"""An arg formatter for types exposed in the PythonCOM module"""
	def GetFormatChar(self):
		return "O"
	#def GetInterfaceCppObjectInfo(self):
	#	return ArgFormatter.GetInterfaceCppObjectInfo(self)[0], \
	#		"%s %s%s" % (self.arg.unc_type, "*" * self._GetDeclaredIndirection(), self.arg.name)
	def DeclareParseArgTupleInputConverter(self):
		# Declare a PyObject variable
		return "\tPyObject *ob%s;\n" % self.arg.name
	def GetParseTupleArg(self):
		return "&ob"+self.arg.name
	def _GetPythonTypeDesc(self):
		return "<o Py%s>" % self.arg.type
	def GetBuildValueArg(self):
		return "ob" + self.arg.name
	def GetBuildForInterfacePostCode(self):
		return "\tPy_XDECREF(ob%s);\n" % self.arg.name
	def DeclareParseArgTupleInputConverter(self):
		# Declare a PyObject variable
		return "\tPyObject *ob%s;\n" % self.arg.name

class ArgFormatterBSTR(ArgFormatterPythonCOM):
	def _GetPythonTypeDesc(self):
		return "<o unicode>"
	def GetParsePostCode(self):
		return "\tif (bPythonIsHappy && !PyWinObject_AsBstr(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
	def GetBuildForInterfacePreCode(self):
		notdirected = self.GetIndirectedArgName(None, 1)
		return "\tob%s = MakeBstrToObj(%s);\n" % \
			   (self.arg.name, notdirected)
	def GetBuildForInterfacePostCode(self):
		return "\tSysFreeString(%s);\n" % (self.arg.name,) + \
		       ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self)
	def GetBuildForGatewayPostCode(self):
		return "\tPy_XDECREF(ob%s);\n" % self.arg.name

class ArgFormatterOLECHAR(ArgFormatterPythonCOM):
	def _GetPythonTypeDesc(self):
		return "<o unicode>"
	def GetUnconstType(self):
		if self.arg.type[:3]=="LPC":
			return self.arg.type[:2] + self.arg.type[3:]
		else:
			return self.arg.unc_type
	def GetParsePostCode(self):
		return "\tif (bPythonIsHappy && !PyWinObject_AsBstr(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
	def GetInterfaceArgCleanup(self):
		return "\tSysFreeString(%s);\n" % self.GetIndirectedArgName(None, 1)
	def GetBuildForInterfacePreCode(self):
		# the variable was declared with just its builtin indirection
		notdirected = self.GetIndirectedArgName(self.builtinIndirection, 1)
		return "\tob%s = MakeOLECHARToObj(%s);\n" % \
			   (self.arg.name, notdirected)
	def GetBuildForInterfacePostCode(self):
		# memory returned into an OLECHAR should be freed
		return "\tCoTaskMemFree(%s);\n" % (self.arg.name,) + \
		       ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self)
	def GetBuildForGatewayPostCode(self):
		return "\tPy_XDECREF(ob%s);\n" % self.arg.name

class ArgFormatterTCHAR(ArgFormatterPythonCOM):
	def _GetPythonTypeDesc(self):
		return "string/<o unicode>"
	def GetUnconstType(self):
		if self.arg.type[:3]=="LPC":
			return self.arg.type[:2] + self.arg.type[3:]
		else:
			return self.arg.unc_type
	def GetParsePostCode(self):
		return "\tif (bPythonIsHappy && !PyWinObject_AsTCHAR(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
	def GetInterfaceArgCleanup(self):
		return "\tPyWinObject_FreeTCHAR(%s);\n" % self.GetIndirectedArgName(None, 1)
	def GetBuildForInterfacePreCode(self):
		# the variable was declared with just its builtin indirection
		notdirected = self.GetIndirectedArgName(self.builtinIndirection, 1)
		return "\tob%s = PyWinObject_FromTCHAR(%s);\n" % \
			   (self.arg.name, notdirected)
	def GetBuildForInterfacePostCode(self):
		return "// ??? - TCHAR post code\n"
	def GetBuildForGatewayPostCode(self):
		return "\tPy_XDECREF(ob%s);\n" % self.arg.name

class ArgFormatterIID(ArgFormatterPythonCOM):
	def _GetPythonTypeDesc(self):
		return "<o PyIID>"
	def GetParsePostCode(self):
		return "\tif (!PyWinObject_AsIID(ob%s, &%s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.arg.name)
	def GetBuildForInterfacePreCode(self):
#		notdirected = self.GetIndirectedArgName(self.arg.indirectionLevel, 0)
		notdirected = self.GetIndirectedArgName(None, 0)
		return "\tob%s = PyWinObject_FromIID(%s);\n" % (self.arg.name, notdirected)
	def GetInterfaceCppObjectInfo(self):
		return self.arg.name, "IID %s" % (self.arg.name)

class ArgFormatterTime(ArgFormatterPythonCOM):
	def __init__(self, arg, builtinIndirection, declaredIndirection = 0):
		# we don't want to declare LPSYSTEMTIME / LPFILETIME objects
		if arg.indirectionLevel == 0 and arg.unc_type[:2] == "LP":
			arg.unc_type = arg.unc_type[2:]
			# reduce the builtin and increment the declaration
			arg.indirectionLevel = arg.indirectionLevel + 1
			builtinIndirection = 0
		ArgFormatterPythonCOM.__init__(self, arg, builtinIndirection, declaredIndirection)

	def _GetPythonTypeDesc(self):
		return "<o PyTime>"
	def GetParsePostCode(self):
		# variable was declared with only the builtinIndirection
		### NOTE: this is an [in] ... so use only builtin
		return '\tif (!PyTime_Check(ob%s)) {\n\t\tPyErr_SetString(PyExc_TypeError, "The argument must be a PyTime object");\n\t\tbPythonIsHappy = FALSE;\n\t}\n\tif (!((PyTime *)ob%s)->GetTime(%s)) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.arg.name, self.GetIndirectedArgName(self.builtinIndirection, 1))
	def GetBuildForInterfacePreCode(self):
		### use just the builtinIndirection again...
		notdirected = self.GetIndirectedArgName(self.builtinIndirection,0)
		return "\tob%s = new PyTime(%s);\n" % (self.arg.name, notdirected)
	def GetBuildForInterfacePostCode(self):
		### hack to determine if we need to free stuff
		ret = ''
		if self.builtinIndirection + self.arg.indirectionLevel > 1:
			# memory returned into an OLECHAR should be freed
			ret = "\tCoTaskMemFree(%s);\n" % self.arg.name
		return ret + ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self)

class ArgFormatterSTATSTG(ArgFormatterPythonCOM):
	def _GetPythonTypeDesc(self):
		return "<o STATSTG>"
	def GetParsePostCode(self):
		return '\tif (!PyCom_PyObjectAsSTATSTG(ob%s, %s, 0/*flags*/)) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.GetIndirectedArgName(None, 1))
	def GetBuildForInterfacePreCode(self):
		notdirected = self.GetIndirectedArgName(None, 1)
		return "\tob%s = PyCom_PyObjectFromSTATSTG(%s);\n\t// STATSTG doco says our responsibility to free\n\tif ((%s).pwcsName) CoTaskMemFree((%s).pwcsName);\n" % (self.arg.name, self.GetIndirectedArgName(None, 1),notdirected,notdirected)

class ArgFormatterGeneric(ArgFormatterPythonCOM):
	def _GetPythonTypeDesc(self):
		return "<o %s>" % self.arg.type
	def GetParsePostCode(self):
		return '\tif (!PyObject_As%s(ob%s, &%s) bPythonIsHappy = FALSE;\n' % (self.arg.type, self.arg.name, self.GetIndirectedArgName(None, 1))
	def GetInterfaceArgCleanup(self):
		return '\tPyObject_Free%s(%s);\n' % (self.arg.type, self.arg.name)
	def GetBuildForInterfacePreCode(self):
		notdirected = self.GetIndirectedArgName(None, 1)
		return "\tob%s = PyObject_From%s(%s);\n" % (self.arg.name, self.arg.type, self.GetIndirectedArgName(None, 1))

class ArgFormatterIDLIST(ArgFormatterPythonCOM):
	def _GetPythonTypeDesc(self):
		return "<o PyIDL>"
	def GetParsePostCode(self):
		return '\tif (bPythonIsHappy && !PyObject_AsPIDL(ob%s, &%s)) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.GetIndirectedArgName(None, 1))
	def GetInterfaceArgCleanup(self):
		return '\tPyObject_FreePIDL(%s);\n' % (self.arg.name,)
	def GetBuildForInterfacePreCode(self):
		notdirected = self.GetIndirectedArgName(None, 1)
		return "\tob%s = PyObject_FromPIDL(%s);\n" % (self.arg.name, self.GetIndirectedArgName(None, 1))

class ArgFormatterHANDLE(ArgFormatterPythonCOM):
	def _GetPythonTypeDesc(self):
		return "<o PyHANDLE>"
	def GetParsePostCode(self):
		return '\tif (!PyWinObject_AsHANDLE(ob%s, &%s, FALSE) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.GetIndirectedArgName(None, 1))
	def GetBuildForInterfacePreCode(self):
		notdirected = self.GetIndirectedArgName(None, 1)
		return "\tob%s = PyWinObject_FromHANDLE(%s);\n" % (self.arg.name, self.GetIndirectedArgName(None, 0))

class ArgFormatterLARGE_INTEGER(ArgFormatterPythonCOM):
	def GetKeyName(self):
		return "LARGE_INTEGER"
	def _GetPythonTypeDesc(self):
		return "<o %s>" % self.GetKeyName()
	def GetParsePostCode(self):
		return '\tif (!PyWinObject_As%s(ob%s, %s)) bPythonIsHappy = FALSE;\n' % (self.GetKeyName(), self.arg.name, self.GetIndirectedArgName(None, 1))
	def GetBuildForInterfacePreCode(self):
		notdirected = self.GetIndirectedArgName(None, 0)
		return "\tob%s = PyWinObject_From%s(%s);\n" % (self.arg.name, self.GetKeyName(), notdirected)

class ArgFormatterULARGE_INTEGER(ArgFormatterLARGE_INTEGER):
	def GetKeyName(self):
		return "ULARGE_INTEGER"

class ArgFormatterInterface(ArgFormatterPythonCOM):
	def GetInterfaceCppObjectInfo(self):
		return self.GetIndirectedArgName(1, self.arg.indirectionLevel), \
			   "%s * %s" % (self.GetUnconstType(), self.arg.name)

	def GetParsePostCode(self):
		# This gets called for out params in gateway mode
		if self.gatewayMode:
			sArg = self.GetIndirectedArgName(None, 2)
		else:
		# vs. in params for interface mode.
			sArg = self.GetIndirectedArgName(1, 2)
		return "\tif (bPythonIsHappy && !PyCom_InterfaceFromPyInstanceOrObject(ob%s, IID_%s, (void **)%s, TRUE /* bNoneOK */))\n\t\t bPythonIsHappy = FALSE;\n" % (self.arg.name, self.arg.type, sArg)
	
	def GetBuildForInterfacePreCode(self):
		return "\tob%s = PyCom_PyObjectFromIUnknown(%s, IID_%s, FALSE);\n" % (self.arg.name, self.arg.name, self.arg.type)
	
	def GetBuildForGatewayPreCode(self):
		sPrefix = self._IndirectPrefix(self._GetDeclaredIndirection(), 1)
		return "\tob%s = PyCom_PyObjectFromIUnknown(%s%s, IID_%s, TRUE);\n" % (self.arg.name, sPrefix, self.arg.name, self.arg.type)

	def GetInterfaceArgCleanup(self):
		return "\tif (%s) %s->Release();\n" % (self.arg.name, self.arg.name)

class ArgFormatterVARIANT(ArgFormatterPythonCOM):
	def GetParsePostCode(self):
		return "\tif ( !PyCom_VariantFromPyObject(ob%s, %s) )\n\t\tbPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 1))

	def GetBuildForGatewayPreCode(self):
		notdirected = self.GetIndirectedArgName(None, 1)
		return "\tob%s = PyCom_PyObjectFromVariant(%s);\n" % (self.arg.name, notdirected)
	def GetBuildForGatewayPostCode(self):
		return "\tPy_XDECREF(ob%s);\n" % self.arg.name

					  # Key :		, Python Type Description, ParseTuple format char
ConvertSimpleTypes = {"BOOL":("BOOL", "int", "i"),
					  "UINT":("UINT", "int", "i"),
					  "BYTE": ("BYTE", "int", "i"),
					  "INT": ("INT", "int", "i"),
					  "DWORD": ("DWORD", "int", "l"),
					  "HRESULT":("HRESULT", "int", "l"),
					  "ULONG": ("ULONG", "int", "l"),
					  "LONG": ("LONG", "int", "l"),
					  "int": ("int", "int", "i"),
					  "long": ("long", "int", "l"),
					  "DISPID": ("DISPID", "long", "l"),
					  "APPBREAKFLAGS": ("int", "int", "i"),
					  "BREAKRESUMEACTION": ("int", "int", "i"),
					  "ERRORRESUMEACTION": ("int", "int", "i"),
					  "BREAKREASON": ("int", "int", "i"),
					  "BREAKPOINT_STATE": ("int", "int", "i"),
					  "BREAKRESUME_ACTION": ("int", "int", "i"),
					  "SOURCE_TEXT_ATTR": ("int", "int", "i"),
					  "TEXT_DOC_ATTR": ("int", "int", "i"),
					  "QUERYOPTION": ("int", "int", "i"),
					  "PARSEACTION": ("int", "int", "i"),
}

class ArgFormatterSimple(ArgFormatter):
	"""An arg formatter for simple integer etc types"""
	def GetFormatChar(self):
		return ConvertSimpleTypes[self.arg.type][2]
	def _GetPythonTypeDesc(self):
		return ConvertSimpleTypes[self.arg.type][1]

AllConverters = {"const OLECHAR":	(ArgFormatterOLECHAR, 0, 1),
				 "WCHAR":			(ArgFormatterOLECHAR, 0, 1),
				 "OLECHAR":			(ArgFormatterOLECHAR, 0, 1),
				 "LPCOLESTR":		(ArgFormatterOLECHAR, 1, 1),
				 "LPOLESTR":		(ArgFormatterOLECHAR, 1, 1),
				 "LPCWSTR":			(ArgFormatterOLECHAR, 1, 1),
				 "LPWSTR":			(ArgFormatterOLECHAR, 1, 1),
				 "LPCSTR":			(ArgFormatterOLECHAR, 1, 1),
				 "LPTSTR":			(ArgFormatterTCHAR, 1, 1),
				 "LPCTSTR":         (ArgFormatterTCHAR, 1, 1),
				 "HANDLE":			(ArgFormatterHANDLE, 0),
				 "BSTR":			(ArgFormatterBSTR, 1, 0),
				 "const IID":		(ArgFormatterIID, 0),
				 "CLSID":			(ArgFormatterIID, 0),
				 "IID":				(ArgFormatterIID, 0),
				 "GUID":			(ArgFormatterIID, 0),
				 "const GUID":		(ArgFormatterIID, 0),
				 "const IID":		(ArgFormatterIID, 0),
				 "REFCLSID":		(ArgFormatterIID, 0),
				 "REFIID":			(ArgFormatterIID, 0),
				 "REFGUID":			(ArgFormatterIID, 0),
				 "const FILETIME":	(ArgFormatterTime, 0),
				 "const SYSTEMTIME":(ArgFormatterTime, 0),
				 "const LPSYSTEMTIME":(ArgFormatterTime, 1, 1),
				 "LPSYSTEMTIME":	(ArgFormatterTime, 1, 1),
				 "FILETIME":		(ArgFormatterTime, 0),
				 "SYSTEMTIME":		(ArgFormatterTime, 0),
				 "STATSTG":			(ArgFormatterSTATSTG, 0),
				 "LARGE_INTEGER":	(ArgFormatterLARGE_INTEGER, 0),
				 "ULARGE_INTEGER":	(ArgFormatterULARGE_INTEGER, 0),
				 "VARIANT":			(ArgFormatterVARIANT, 0),
				 "float":			(ArgFormatterFloat, 0),
				 "single":			(ArgFormatterFloat, 0),
				 "short":			(ArgFormatterShort, 0),
				 "WORD":			(ArgFormatterShort, 0),
				 "VARIANT_BOOL":	(ArgFormatterShort, 0),
				 "HWND":            (ArgFormatterLONG_PTR, 1),
				 "HMENU":			(ArgFormatterLONG_PTR, 1),
				 "HOLEMENU":		(ArgFormatterLONG_PTR, 1),
				 "HICON":			(ArgFormatterLONG_PTR, 1),
				 "HDC":				(ArgFormatterLONG_PTR, 1),
				 "LPARAM":			(ArgFormatterLONG_PTR, 1),
				 "WPARAM":			(ArgFormatterLONG_PTR, 1),
				 "LRESULT":			(ArgFormatterLONG_PTR, 1),
				 "UINT":			(ArgFormatterShort, 0),
				 "SVSIF":			(ArgFormatterShort, 0),
				 "Control":			(ArgFormatterInterface, 0, 1),
				 "DataObject":		(ArgFormatterInterface, 0, 1),
				 "_PropertyBag":	(ArgFormatterInterface, 0, 1),
				 "AsyncProp":		(ArgFormatterInterface, 0, 1),
				 "DataSource":		(ArgFormatterInterface, 0, 1),
				 "DataFormat":		(ArgFormatterInterface, 0, 1),
				 "void **":			(ArgFormatterInterface, 2, 2),
				 "ITEMIDLIST":		(ArgFormatterIDLIST, 0, 0),
				 "LPITEMIDLIST":		(ArgFormatterIDLIST, 0, 1),
				 "LPCITEMIDLIST":		(ArgFormatterIDLIST, 0, 1),
				 "const ITEMIDLIST":		(ArgFormatterIDLIST, 0, 1),
}

# Auto-add all the simple types
for key in ConvertSimpleTypes.keys():
	AllConverters[key] = ArgFormatterSimple, 0

def make_arg_converter(arg):
	try:
		clz = AllConverters[arg.type][0]
		bin = AllConverters[arg.type][1]
		decl = 0
		if len(AllConverters[arg.type])>2:
			decl = AllConverters[arg.type][2]
		return clz(arg,bin, decl)
	except KeyError:
		if arg.type[0]=="I":
			return ArgFormatterInterface(arg, 0, 1)

		raise error_not_supported, "The type '%s' (%s) is unknown." % (arg.type, arg.name)


#############################################################
#
# The instances that represent the args, methods and interface
class Argument:
	"""A representation of an argument to a COM method
	
	This class contains information about a specific argument to a method.
	In addition, methods exist so that an argument knows how to convert itself
	to/from Python arguments.
	"""
#									  in,out					  type			  name			 [	]
#								   --------------				--------	  ------------		------
	regex = re.compile(r'/\* \[([^\]]*.*?)] \*/[ \t](.*[* ]+)(\w+)(\[ *])?[\),]')
	def __init__(self, good_interface_names):
		self.good_interface_names = good_interface_names
		self.inout = self.name = self.type = None
		self.const = 0
		self.arrayDecl = 0
	def BuildFromFile(self, file):
		"""Parse and build my data from a file
		
		Reads the next line in the file, and matches it as an argument
		description.  If not a valid argument line, an error_not_found exception
		is raised.
		"""
		line = file.readline()
		mo = self.regex.search(line)
		if not mo:
			raise error_not_found
		self.name = mo.group(3)
		self.inout = mo.group(1).split('][')
		typ = mo.group(2).strip()
		self.raw_type = typ
		self.indirectionLevel = 0
		if mo.group(4): # Has "[ ]" decl
			self.arrayDecl = 1
			try:
				pos = typ.rindex("__RPC_FAR")
				self.indirectionLevel = self.indirectionLevel + 1
				typ = typ[:pos].strip()
			except ValueError:
				pass

		typ = typ.replace("__RPC_FAR", "")
		while 1:
			try:
				pos = typ.rindex("*")
				self.indirectionLevel = self.indirectionLevel + 1
				typ = typ[:pos].strip()
			except ValueError:
				break
		self.type = typ
		if self.type[:6]=="const ":
			self.unc_type = self.type[6:]
		else:
			self.unc_type = self.type
		
		if VERBOSE:
			print "	   Arg %s of type %s%s (%s)" % (self.name, self.type, "*" * self.indirectionLevel, self.inout)

	def HasAttribute(self, typ):
		"""Determines if the argument has the specific attribute.
		
		Argument attributes are specified in the header file, such as
		"[in][out][retval]" etc.  You can pass a specific string (eg "out")
		to find if this attribute was specified for the argument
		"""
		return typ in self.inout

	def GetRawDeclaration(self):
		ret = "%s %s" % (self.raw_type, self.name)
		if self.arrayDecl:
			ret = ret + "[]"
		return ret

class Method:
	"""A representation of a C++ method on a COM interface
	
	This class contains information about a specific method, as well as 
	a list of all @Argument@s
	"""
#										 options	 ret type callconv	 name
#								   ----------------- -------- -------- --------
	regex = re.compile(r'virtual (/\*.*?\*/ )?(.*?) (.*?) (.*?)\(\w?')
	def __init__(self, good_interface_names):
		self.good_interface_names = good_interface_names
		self.name = self.result = self.callconv = None
		self.args = []
	def BuildFromFile(self, file):
		"""Parse and build my data from a file
		
		Reads the next line in the file, and matches it as a method
		description.  If not a valid method line, an error_not_found exception
		is raised.
		"""
		line = file.readline()
		mo = self.regex.search(line)
		if not mo:
			raise error_not_found
		self.name = mo.group(4)
		self.result = mo.group(2)
		if self.result != "HRESULT":
			if self.result=="DWORD": # DWORD is for old old stuff?
				print "Warning: Old style interface detected - compilation errors likely!"
			else:
				print "Method %s - Only HRESULT return types are supported." % self.name
#				raise error_not_supported,		if VERBOSE:
			print "	 Method %s %s(" % (self.result, self.name)
		while 1:
			arg = Argument(self.good_interface_names)
			try:
				arg.BuildFromFile(file)
				self.args.append(arg)
			except error_not_found:
				break

class Interface:
	"""A representation of a C++ COM Interface
	
	This class contains information about a specific interface, as well as 
	a list of all @Method@s
	"""
#									  name				 base
#									 --------		   --------
	regex = re.compile("(interface|) ([^ ]*) : public (.*)$")
	def __init__(self, mo):
		self.methods = []
		self.name = mo.group(2)
		self.base = mo.group(3)
		if VERBOSE:
			print "Interface %s : public %s" % (self.name, self.base)

	def BuildMethods(self, file):
		"""Build all sub-methods for this interface"""
		# skip the next 2 lines.
		file.readline();file.readline();
		while 1:
			try:
				method = Method([self.name])
				method.BuildFromFile(file)
				self.methods.append(method)
			except error_not_found:
				break

def find_interface(interfaceName, file):
	"""Find and return an interface in a file
	
	Given an interface name and file, search for the specified interface.
	
	Upon return, the interface itself has been built, 
	but not the methods.
	"""
	interface = None
	line = file.readline()
	while line:
		mo = Interface.regex.search(line)
		if mo:
			name = mo.group(2)
			print name
			AllConverters[name] = (ArgFormatterInterface, 0, 1)
			if name==interfaceName:
				interface = Interface(mo)
				interface.BuildMethods(file)
		line = file.readline()
	if interface:
		return interface
	raise error_not_found


def parse_interface_info(interfaceName, file):
	"""Find, parse and return an interface in a file
	
	Given an interface name and file, search for the specified interface.
	
	Upon return, the interface itself is fully built,
	"""
	try:
		return find_interface(interfaceName, file)
	except re.error:
		traceback.print_exc()
		print "The interface could not be built, as the regular expression failed!"
def test():
	f=open("d:\\msdev\\include\\objidl.h")
	try:
		parse_interface_info("IPersistStream", f)
	finally:
		f.close()

def test_regex(r,text):
	res=r.search(text,0)
	if res==-1:
		print "** Not found"
	else:
		print "%d\n%s\n%s\n%s\n%s" % (res, r.group(1), r.group(2), r.group(3), r.group(4))
