// internal
bool asCModule::AreInterfacesEqual(asCObjectType *a, asCObjectType *b, asCArray<sObjectTypePair> &equals)
{
	// An interface is considered equal to another if the following criterias apply:
	//
	// - The interface names are equal
	// - The number of methods is equal
	// - All the methods are equal
	// - The order of the methods is equal
	// - If a method returns or takes an interface by handle or reference, both interfaces must be equal


	// ------------
	// TODO: Study the possiblity of allowing interfaces where methods are declared in different orders to
	// be considered equal. The compiler and VM can handle this, but it complicates the comparison of interfaces
	// where multiple methods take different interfaces as parameters (or return values). Example:
	// 
	// interface A
	// {
	//    void f(B, C);
	//    void f(B);
	//    void f(C);
	// }
	//
	// If 'void f(B)' in module A is compared against 'void f(C)' in module B, then the code will assume
	// interface B in module A equals interface C in module B. Thus 'void f(B, C)' in module A won't match
	// 'void f(C, B)' in module B.
	// ------------


	// Are both interfaces?
	if( !a->IsInterface() || !b->IsInterface() )
		return false;

	// Are the names equal?
	if( a->name != b->name )
		return false;

	// Are the number of methods equal?
	if( a->methods.GetLength() != b->methods.GetLength() )
		return false;

	// Keep the number of equals in the list so we can restore it later if necessary
	int prevEquals = (int)equals.GetLength();

	// Are the methods equal to each other?
	bool match = true;
	for( unsigned int n = 0; n < a->methods.GetLength(); n++ )
	{
		match = false;

		asCScriptFunction *funcA = (asCScriptFunction*)engine->GetFunctionDescriptorById(a->methods[n]);
		asCScriptFunction *funcB = (asCScriptFunction*)engine->GetFunctionDescriptorById(b->methods[n]);

		// funcB can be null if the module that created the interface has been  
		// discarded but the type has not yet been released by the engine.
		if( funcB == 0 )
			break;

		// The methods must have the same name and the same number of parameters
		if( funcA->name != funcB->name ||
			funcA->parameterTypes.GetLength() != funcB->parameterTypes.GetLength() )
			break;
		
		// The return types must be equal. If the return type is an interface the interfaces must match.
		if( !AreTypesEqual(funcA->returnType, funcB->returnType, equals) )
			break;

		match = true;
		for( unsigned int p = 0; p < funcA->parameterTypes.GetLength(); p++ )
		{
			if( !AreTypesEqual(funcA->parameterTypes[p], funcB->parameterTypes[p], equals) ||
				funcA->inOutFlags[p] != funcB->inOutFlags[p] )
			{
				match = false;
				break;
			}
		}

		if( !match )
			break;
	}

	// For each of the new interfaces that we're assuming to be equal, we need to validate this
	if( match )
	{
		for( unsigned int n = prevEquals; n < equals.GetLength(); n++ )
		{
			if( !AreInterfacesEqual(equals[n].a, equals[n].b, equals) )
			{
				match = false;
				break;
			}
		}
	}

	if( !match )
	{
		// The interfaces doesn't match. 
		// Restore the list of previous equals before we go on, so  
		// the caller can continue comparing with another interface
		equals.SetLength(prevEquals);
	}

	return match;
}