//
// outline -- support for rendering in monop
// Some code stolen from updater.cs in monodoc.
//
// Authors:
//	Ben Maurer (bmaurer@users.sourceforge.net)
//
// (C) 2004 Ben Maurer
//

//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Reflection;
using System.Collections;
using System.CodeDom.Compiler;
using System.IO;
using System.Text;

namespace ICSharpCode.NRefactory.MonoCSharp {
public class Outline {

	bool declared_only;
	bool show_private;
	bool filter_obsolete;
	
	IndentedTextWriter o;
	Type t;
	
	public Outline (Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete)
	{
		this.t = t;
		this.o = new IndentedTextWriter (output, "\t");
		this.declared_only = declared_only;
		this.show_private = show_private;
		this.filter_obsolete = filter_obsolete;
	}

	public void OutlineType ()
        {
		bool first;
		
		OutlineAttributes ();
		o.Write (GetTypeVisibility (t));
		
		if (t.IsClass && !t.IsSubclassOf (typeof (System.MulticastDelegate))) {
			if (t.IsSealed)
				o.Write (t.IsAbstract ? " static" : " sealed");
			else if (t.IsAbstract)
				o.Write (" abstract");
		}
		
		o.Write (" ");
		o.Write (GetTypeKind (t));
		o.Write (" ");
		
		Type [] interfaces = (Type []) Comparer.Sort (TypeGetInterfaces (t, declared_only));
		Type parent = t.BaseType;

		if (t.IsSubclassOf (typeof (System.MulticastDelegate))) {
			MethodInfo method;

			method = t.GetMethod ("Invoke");

			o.Write (FormatType (method.ReturnType));
			o.Write (" ");
			o.Write (GetTypeName (t));
			o.Write (" (");
			OutlineParams (method.GetParameters ());
			o.Write (")");

			WriteGenericConstraints (t.GetGenericArguments ());
	
			o.WriteLine (";"); 
			return;
		}
		
		o.Write (GetTypeName (t));
		if (((parent != null && parent != typeof (object) && parent != typeof (ValueType)) || interfaces.Length != 0) && ! t.IsEnum) {
			first = true;
			o.Write (" : ");
			
			if (parent != null && parent != typeof (object) && parent != typeof (ValueType)) {
				o.Write (FormatType (parent));
				first = false;
			}
			
			foreach (Type intf in interfaces) {
				if (!first) o.Write (", ");
				first = false;
				
				o.Write (FormatType (intf));
			}
		}

		if (t.IsEnum) {
			Type underlyingType = System.Enum.GetUnderlyingType (t);
			if (underlyingType != typeof (int))
				o.Write (" : {0}", FormatType (underlyingType));
		}
		WriteGenericConstraints (t.GetGenericArguments ());
		o.WriteLine (" {");
		o.Indent++;

		if (t.IsEnum) {
			bool is_first = true;
			foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Static)) {
				
				if (! is_first)
					o.WriteLine (",");
				is_first = false;
				o.Write (fi.Name);
			}
			o.WriteLine ();
			o.Indent--; o.WriteLine ("}");
			return;
		}
		
		first = true;
		
		foreach (ConstructorInfo ci in t.GetConstructors (DefaultFlags)) {
			if (! ShowMember (ci))
				continue;
			
			if (first)
				o.WriteLine ();
			first = false;
			
			OutlineMemberAttribute (ci);
			OutlineConstructor (ci);
			
			o.WriteLine ();
		}
		

		first = true;
		
		foreach (MethodInfo m in Comparer.Sort (t.GetMethods (DefaultFlags))) {
			
			if (! ShowMember (m))
				continue;		
			
			if ((m.Attributes & MethodAttributes.SpecialName) != 0)
				continue;
			
			if (first)
				o.WriteLine ();
			first = false;

			OutlineMemberAttribute (m);
			OutlineMethod (m);
			
			o.WriteLine ();
		}
		
		first = true;
		
		foreach (MethodInfo m in t.GetMethods (DefaultFlags)) {
			
			if (! ShowMember (m))
				continue;
			
			if ((m.Attributes & MethodAttributes.SpecialName) == 0)
				continue;
			if (!(m.Name.StartsWith ("op_")))
				continue;

			if (first)
				o.WriteLine ();
			first = false;
			
			OutlineMemberAttribute (m);
			OutlineOperator (m);
			
			o.WriteLine ();
		}

		first = true;
		
		foreach (PropertyInfo pi in Comparer.Sort (t.GetProperties (DefaultFlags))) {
			
			if (! ((pi.CanRead  && ShowMember (pi.GetGetMethod (true))) ||
			       (pi.CanWrite && ShowMember (pi.GetSetMethod (true)))))
				continue;
			
			if (first)
				o.WriteLine ();
			first = false;
			
			OutlineMemberAttribute (pi);
			OutlineProperty (pi);
			
			o.WriteLine ();
		}
		
		first = true;

		foreach (FieldInfo fi in t.GetFields (DefaultFlags)) {
			
			if (! ShowMember (fi))
				continue;
			
			if (first)
				o.WriteLine ();
			first = false;
			
			OutlineMemberAttribute (fi);
			OutlineField (fi);
			
			o.WriteLine ();
		}

		first = true;
		
		foreach (EventInfo ei in Comparer.Sort (t.GetEvents (DefaultFlags))) {
			
			if (! ShowMember (ei.GetAddMethod (true)))
				continue;
			
			if (first)
				o.WriteLine ();
			first = false;
			
			OutlineMemberAttribute (ei);
			OutlineEvent (ei);
			
			o.WriteLine ();
		}

		first = true;

		foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (DefaultFlags))) {
			
			if (! ShowMember (ntype))
				continue;
			
			if (first)
				o.WriteLine ();
			first = false;
			
			new Outline (ntype, o, declared_only, show_private, filter_obsolete).OutlineType ();
		}
		
		o.Indent--; o.WriteLine ("}");
	}
	
	BindingFlags DefaultFlags {
		get {
			BindingFlags f = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
			
			if (declared_only)
				f |= BindingFlags.DeclaredOnly;
			
			return f;
		}
	}

	// FIXME: add other interesting attributes?
	void OutlineAttributes ()
	{
		if (t.IsSerializable)
			o.WriteLine ("[Serializable]");

		if (t.IsDefined (typeof (System.FlagsAttribute), true))
			o.WriteLine ("[Flags]");

		if (t.IsDefined (typeof (System.ObsoleteAttribute), true))
			o.WriteLine ("[Obsolete]");
	}

	void OutlineMemberAttribute (MemberInfo mi)
	{
		if (!mi.IsDefined (typeof (System.ObsoleteAttribute), false))
			return;
		var oa = mi.GetCustomAttributes (typeof (System.ObsoleteAttribute), false) [0] as ObsoleteAttribute;
		var msg = oa.Message;
		o.WriteLine ("[Obsolete{0}]", msg == null || msg == "" ? "" : string.Format ("(\"{0}\")", msg));
	}
	
	void OutlineEvent (EventInfo ei)
	{
		MethodBase accessor = ei.GetAddMethod (true);
		
		o.Write (GetMethodVisibility (accessor));
		o.Write ("event ");
		o.Write (FormatType (ei.EventHandlerType));
		o.Write (" ");
		o.Write (ei.Name);
		o.Write (";");
	}
	
	void OutlineConstructor (ConstructorInfo ci)
	{
		o.Write (GetMethodVisibility (ci));
		o.Write (RemoveGenericArity (t.Name));
		o.Write (" (");
		OutlineParams (ci.GetParameters ());
		o.Write (");");
	}
	
	
	void OutlineProperty (PropertyInfo pi)
	{
		ParameterInfo [] idxp = pi.GetIndexParameters ();
		MethodBase g = pi.GetGetMethod (true);
		MethodBase s = pi.GetSetMethod (true);
		MethodBase accessor = g != null ? g : s;
		
		if (pi.CanRead && pi.CanWrite) {

			
			// Get the more accessible accessor
			if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
			    (s.Attributes & MethodAttributes.MemberAccessMask)) {
				
				if (g.IsPublic) accessor = g;
				else if (s.IsPublic) accessor = s;
				else if (g.IsFamilyOrAssembly) accessor = g;
				else if (s.IsFamilyOrAssembly) accessor = s;
				else if (g.IsAssembly || g.IsFamily) accessor = g;
				else if (s.IsAssembly || s.IsFamily) accessor = s;
			}
		}
		
		o.Write (GetMethodVisibility (accessor));
		o.Write (GetMethodModifiers  (accessor));
		o.Write (FormatType (pi.PropertyType));
		o.Write (" ");
		
		if (idxp.Length == 0)
			o.Write (pi.Name);
		else {
			o.Write ("this [");
			OutlineParams (idxp);
			o.Write ("]");
		}
		
		o.WriteLine (" {");
		o.Indent ++;
		
		if (g != null && ShowMember (g)) {
			if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
			    (accessor.Attributes & MethodAttributes.MemberAccessMask))
				o.Write (GetMethodVisibility (g));
			o.WriteLine ("get;");
		}
		
		if (s != null && ShowMember (s)) {
			if ((s.Attributes & MethodAttributes.MemberAccessMask) !=
			    (accessor.Attributes & MethodAttributes.MemberAccessMask))
				o.Write (GetMethodVisibility (s));
			o.WriteLine ("set;");
		}
		
		o.Indent --;
		o.Write ("}");
	}
	
	void OutlineMethod (MethodInfo mi)
	{
		if (MethodIsExplicitIfaceImpl (mi)) {
			o.Write (FormatType (mi.ReturnType));
			o.Write (" ");
			// MSFT has no way to get the method that we are overriding
			// from the interface. this would allow us to pretty print
			// the type name (and be more correct if there compiler
			// were to do some strange naming thing).
		} else {
			o.Write (GetMethodVisibility (mi));
			o.Write (GetMethodModifiers  (mi));
			o.Write (FormatType (mi.ReturnType));
			o.Write (" ");
		}

		o.Write (mi.Name);
		o.Write (FormatGenericParams (mi.GetGenericArguments ()));
		o.Write (" (");
		OutlineParams (mi.GetParameters ());
		o.Write (")");
		WriteGenericConstraints (mi.GetGenericArguments ());
		o.Write (";");
	}
	
	void OutlineOperator (MethodInfo mi)
	{
		o.Write (GetMethodVisibility (mi));
		o.Write (GetMethodModifiers  (mi));
		if (mi.Name == "op_Explicit" || mi.Name == "op_Implicit") {
			o.Write (mi.Name.Substring (3).ToLower ());
			o.Write (" operator ");
			o.Write (FormatType (mi.ReturnType));
		} else {
			o.Write (FormatType (mi.ReturnType));
			o.Write (" operator ");
			o.Write (OperatorFromName (mi.Name));
		}
		o.Write (" (");
		OutlineParams (mi.GetParameters ());
		o.Write (");");
	}
	
	void OutlineParams (ParameterInfo [] pi)
	{
		int i = 0;
		foreach (ParameterInfo p in pi) {
			if (p.ParameterType.IsByRef) {
				o.Write (p.IsOut ? "out " : "ref ");
				o.Write (FormatType (p.ParameterType.GetElementType ()));
			} else if (p.IsDefined (typeof (ParamArrayAttribute), false)) {
				o.Write ("params ");
				o.Write (FormatType (p.ParameterType));
			} else {
				o.Write (FormatType (p.ParameterType));
			}
			
			o.Write (" ");
			o.Write (p.Name);
			if (i + 1 < pi.Length)
				o.Write (", ");
			i++;
		}
	}

	void OutlineField (FieldInfo fi)
	{
		if (fi.IsPublic)   o.Write ("public ");
		if (fi.IsFamily)   o.Write ("protected ");
		if (fi.IsPrivate)  o.Write ("private ");
		if (fi.IsAssembly) o.Write ("internal ");
		if (fi.IsLiteral)  o.Write ("const ");
		else if (fi.IsStatic) o.Write ("static ");
		if (fi.IsInitOnly) o.Write ("readonly ");

		o.Write (FormatType (fi.FieldType));
		o.Write (" ");
		o.Write (fi.Name);
		if (fi.IsLiteral) { 
			object v = fi.GetValue (this);

			// TODO: Escape values here
			o.Write (" = ");
			if (v is char)
				o.Write ("'{0}'", v);
			else if (v is string)
				o.Write ("\"{0}\"", v);
			else
				o.Write (fi.GetValue (this));
		}
		o.Write (";");
	}

	static string GetMethodVisibility (MethodBase m)
	{
		// itnerfaces have no modifiers here
		if (m.DeclaringType.IsInterface)
			return "";
		
		if (m.IsPublic)   return "public ";
		if (m.IsFamily)   return "protected ";
		if (m.IsPrivate)  return "private ";
		if (m.IsAssembly) return "internal ";
			
		return null;
	}
	
	static string GetMethodModifiers (MethodBase method)
	{
		if (method.IsStatic)
			return "static ";

		if (method.IsFinal) {
			// This will happen if you have
			// class X : IA {
			//   public void A () {}
			//   static void Main () {}
			// }
			// interface IA {
			//   void A ();
			// }
			//
			// A needs to be virtual (the CLR requires
			// methods implementing an iface be virtual),
			// but can not be inherited. It also can not
			// be inherited. In C# this is represented
			// with no special modifiers

			if (method.IsVirtual)
				return null;
			return "sealed ";
		}
		
		// all interface methods are "virtual" but we don't say that in c#
		if (method.IsVirtual && !method.DeclaringType.IsInterface) {
			if (method.IsAbstract)
				return "abstract ";

			return ((method.Attributes & MethodAttributes.NewSlot) != 0) ?
				"virtual " :
				"override ";	
		}
				
		return null;
	}

	static string GetTypeKind (Type t)
	{
		if (t.IsEnum)
			return "enum";
		if (t.IsClass) {
			if (t.IsSubclassOf (typeof (System.MulticastDelegate)))
				return "delegate";
			else
				return "class";
		}
		if (t.IsInterface)
			return "interface";
		if (t.IsValueType)
			return "struct";
		return "class";
	}
	
	static string GetTypeVisibility (Type t)
	{
                switch (t.Attributes & TypeAttributes.VisibilityMask){
                case TypeAttributes.Public:
                case TypeAttributes.NestedPublic:
                        return "public";

                case TypeAttributes.NestedFamily:
                case TypeAttributes.NestedFamANDAssem:
                case TypeAttributes.NestedFamORAssem:
                        return "protected";

                default:
                        return "internal";
                }
	}

	string FormatGenericParams (Type [] args)
	{
		StringBuilder sb = new StringBuilder ();
		if (args.Length == 0)
			return "";
		
		sb.Append ("<");
		for (int i = 0; i < args.Length; i++) {
			if (i > 0)
				sb.Append (",");
			sb.Append (FormatType (args [i]));
		}
		sb.Append (">");
		return sb.ToString ();
	}

	// TODO: fine tune this so that our output is less verbose. We need to figure
	// out a way to do this while not making things confusing.
	string FormatType (Type t)
	{
		if (t == null)
			return "";

		string type = GetFullName (t);
		if (type == null)
			return t.ToString ();
		
		if (!type.StartsWith ("System.")) {
			if (t.Namespace == this.t.Namespace)
				return t.Name;
			return type;
		}
		
		if (t.HasElementType) {
			Type et = t.GetElementType ();
			if (t.IsArray)
				return FormatType (et) + " []";
			if (t.IsPointer)
				return FormatType (et) + " *";
			if (t.IsByRef)
				return "ref " + FormatType (et);
		}
	
		switch (type) {
		case "System.Byte": return "byte";
		case "System.SByte": return "sbyte";
		case "System.Int16": return "short";
		case "System.Int32": return "int";
		case "System.Int64": return "long";
			
		case "System.UInt16": return "ushort";
		case "System.UInt32": return "uint";
		case "System.UInt64": return "ulong";
			
		case "System.Single":  return "float";
		case "System.Double":  return "double";
		case "System.Decimal": return "decimal";
		case "System.Boolean": return "bool";
		case "System.Char":    return "char";
		case "System.String":  return "string";
			
		case "System.Object":  return "object";
		case "System.Void":  return "void";
		}
	
		if (type.LastIndexOf(".") == 6)
			return type.Substring(7);

		//
		// If the namespace of the type is the namespace of what
		// we are printing (or is a member of one if its children
		// don't print it. This basically means that in C# we would
		// automatically get the namespace imported by virtue of the
		// namespace {} block.
		//	
		if (this.t.Namespace.StartsWith (t.Namespace + ".") || t.Namespace == this.t.Namespace)
			return type.Substring (t.Namespace.Length + 1);
	
		return type;
	}

	public static string RemoveGenericArity (string name)
	{
		int start = 0;
		StringBuilder sb = new StringBuilder ();
		while (start < name.Length) {
			int pos = name.IndexOf ('`', start);
			if (pos < 0) {
				sb.Append (name.Substring (start));
				break;
			}
			sb.Append (name.Substring (start, pos-start));

			pos++;

			while ((pos < name.Length) && Char.IsNumber (name [pos]))
				pos++;

			start = pos;
		}

		return sb.ToString ();
	}

	string GetTypeName (Type t)
	{
		StringBuilder sb = new StringBuilder ();
		GetTypeName (sb, t);
		return sb.ToString ();
	}

	void GetTypeName (StringBuilder sb, Type t)
	{
		sb.Append (RemoveGenericArity (t.Name));
		sb.Append (FormatGenericParams (t.GetGenericArguments ()));
	}

	string GetFullName (Type t)
	{
		StringBuilder sb = new StringBuilder ();
		GetFullName_recursed (sb, t, false);
		return sb.ToString ();
	}

	void GetFullName_recursed (StringBuilder sb, Type t, bool recursed)
	{
		if (t.IsGenericParameter) {
			sb.Append (t.Name);
			return;
		}

		if (t.DeclaringType != null) {
			GetFullName_recursed (sb, t.DeclaringType, true);
			sb.Append (".");
		}

		if (!recursed) {
			string ns = t.Namespace;
			if ((ns != null) && (ns != "")) {
				sb.Append (ns);
				sb.Append (".");
			}
		}

		GetTypeName (sb, t);
	}

	void WriteGenericConstraints (Type [] args)
	{

		foreach (Type t in args) {
			bool first = true;
			Type[] ifaces = TypeGetInterfaces (t, true);
			
			GenericParameterAttributes attrs = t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
			GenericParameterAttributes [] interesting = {
				GenericParameterAttributes.ReferenceTypeConstraint,
				GenericParameterAttributes.NotNullableValueTypeConstraint,
				GenericParameterAttributes.DefaultConstructorConstraint
			};
			
			if (t.BaseType != typeof (object) || ifaces.Length != 0 || attrs != 0) {
				o.Write (" where ");
				o.Write (FormatType (t));
				o.Write (" : ");
			}

			if (t.BaseType != typeof (object)) {
				o.Write (FormatType (t.BaseType));
				first = false;
			}

			foreach (Type iface in ifaces) {
				if (!first)
					o.Write (", ");
				first = false;
				
				o.Write (FormatType (iface));
			}

			foreach (GenericParameterAttributes a in interesting) {
				if ((attrs & a) == 0)
					continue;
				
				if (!first)
					o.Write (", ");
				first = false;
				
				switch (a) {
				case GenericParameterAttributes.ReferenceTypeConstraint:
					o.Write ("class");
					break;
				case GenericParameterAttributes.NotNullableValueTypeConstraint:
					o.Write ("struct");
					break;
				case GenericParameterAttributes.DefaultConstructorConstraint:
					o.Write ("new ()");
					break;
				}
			}
		}
	}
 
	string OperatorFromName (string name)
	{
		switch (name) {
		case "op_UnaryPlus": return "+";
		case "op_UnaryNegation": return "-";
		case "op_LogicalNot": return "!";
		case "op_OnesComplement": return "~";
		case "op_Increment": return "++";
		case "op_Decrement": return "--";
		case "op_True": return "true";
		case "op_False": return "false";
		case "op_Addition": return "+";
		case "op_Subtraction": return "-";
		case "op_Multiply": return "*";
		case "op_Division": return "/";
		case "op_Modulus": return "%";
		case "op_BitwiseAnd": return "&";
		case "op_BitwiseOr": return "|";
		case "op_ExclusiveOr": return "^";
		case "op_LeftShift": return "<<";
		case "op_RightShift": return ">>";
		case "op_Equality": return "==";
		case "op_Inequality": return "!=";
		case "op_GreaterThan": return ">";
		case "op_LessThan": return "<";
		case "op_GreaterThanOrEqual": return ">=";
		case "op_LessThanOrEqual": return "<=";
		default: return name;
		}
	}

	bool MethodIsExplicitIfaceImpl (MethodBase mb)
	{
		if (!(mb.IsFinal && mb.IsVirtual && mb.IsPrivate))
			return false;
		
		// UGH msft has no way to get the info about what method is
		// getting overriden. Another reason to use cecil :-)
		//
		//MethodInfo mi = mb as MethodInfo;
		//if (mi == null)
		//	return false;
		//
		//Console.WriteLine (mi.GetBaseDefinition ().DeclaringType);
		//return mi.GetBaseDefinition ().DeclaringType.IsInterface;
		
		// So, we guess that virtual final private methods only come
		// from ifaces :-)
		return true;
	}
	
	bool ShowMember (MemberInfo mi)
	{
		if (mi.MemberType == MemberTypes.Constructor && ((MethodBase) mi).IsStatic)
			return false;
		
		if (show_private)
			return true;

		if (filter_obsolete && mi.IsDefined (typeof (ObsoleteAttribute), false))
			return false;
		
		switch (mi.MemberType) {
		case MemberTypes.Constructor:
		case MemberTypes.Method:
			MethodBase mb = mi as MethodBase;
		
			if (mb.IsFamily || mb.IsPublic || mb.IsFamilyOrAssembly)
				return true;
			
			if (MethodIsExplicitIfaceImpl (mb))
				return true;
					
			return false;
		
		
		case MemberTypes.Field:
			FieldInfo fi = mi as FieldInfo;
		
			if (fi.IsFamily || fi.IsPublic || fi.IsFamilyOrAssembly)
				return true;
			
			return false;
		
		
		case MemberTypes.NestedType:
		case MemberTypes.TypeInfo:
			Type t = mi as Type;
		
			switch (t.Attributes & TypeAttributes.VisibilityMask){
			case TypeAttributes.Public:
			case TypeAttributes.NestedPublic:
			case TypeAttributes.NestedFamily:
			case TypeAttributes.NestedFamORAssem:
				return true;
			}
			
			return false;
		}
		
		// What am I !!!
		return true;
	}

	static Type [] TypeGetInterfaces (Type t, bool declonly)
	{
		if (t.IsGenericParameter)
			return new Type [0];

		Type [] ifaces = t.GetInterfaces ();
		if (! declonly)
			return ifaces;

		// Handle Object. Also, optimize for no interfaces
		if (t.BaseType == null || ifaces.Length == 0)
			return ifaces;

		ArrayList ar = new ArrayList ();

		foreach (Type i in ifaces)
			if (! i.IsAssignableFrom (t.BaseType))
				ar.Add (i);

		return (Type []) ar.ToArray (typeof (Type));
	}
}

public class Comparer : IComparer  {
	delegate int ComparerFunc (object a, object b);
	
	ComparerFunc cmp;
	
	Comparer (ComparerFunc f)
	{
		this.cmp = f;
	}
	
	public int Compare (object a, object b)
	{
		return cmp (a, b);
	}

	static int CompareType (object a, object b)
	{
		Type type1 = (Type) a;
		Type type2 = (Type) b;

		if (type1.IsSubclassOf (typeof (System.MulticastDelegate)) != type2.IsSubclassOf (typeof (System.MulticastDelegate)))
				return (type1.IsSubclassOf (typeof (System.MulticastDelegate)))? -1:1;
		return string.Compare (type1.Name, type2.Name);
			
	}

//	static Comparer TypeComparer = new Comparer (new ComparerFunc (CompareType));

//	static Type [] Sort (Type [] types)
//	{
//		Array.Sort (types, TypeComparer);
//		return types;
//	}
	
	static int CompareMemberInfo (object a, object b)
	{
		return string.Compare (((MemberInfo) a).Name, ((MemberInfo) b).Name);
	}
	
	static Comparer MemberInfoComparer = new Comparer (new ComparerFunc (CompareMemberInfo));
	
	public static MemberInfo [] Sort (MemberInfo [] inf)
	{
		Array.Sort (inf, MemberInfoComparer);
		return inf;
	}
	
	static int CompareMethodBase (object a, object b)
	{
		MethodBase aa = (MethodBase) a, bb = (MethodBase) b;
		
		if (aa.IsStatic == bb.IsStatic) {
			int c = CompareMemberInfo (a, b);
			if (c != 0)
				return c;
			ParameterInfo [] ap, bp;

			//
			// Sort overloads by the names of their types
			// put methods with fewer params first.
			//
			
			ap = aa.GetParameters ();
			bp = bb.GetParameters ();
			int n = System.Math.Min (ap.Length, bp.Length);

			for (int i = 0; i < n; i ++)
				if ((c = CompareType (ap [i].ParameterType, bp [i].ParameterType)) != 0)
					return c;

			return ap.Length.CompareTo (bp.Length);
		}
		if (aa.IsStatic)
			return -1;
		
		return 1;
	}
	
	static Comparer MethodBaseComparer = new Comparer (new ComparerFunc (CompareMethodBase));
	
	public static MethodBase [] Sort (MethodBase [] inf)
	{
		Array.Sort (inf, MethodBaseComparer);
		return inf;
	}
	
	static int ComparePropertyInfo (object a, object b)
	{
		PropertyInfo aa = (PropertyInfo) a, bb = (PropertyInfo) b;
		
		bool astatic = (aa.CanRead ? aa.GetGetMethod (true) : aa.GetSetMethod (true)).IsStatic;
		bool bstatic = (bb.CanRead ? bb.GetGetMethod (true) : bb.GetSetMethod (true)).IsStatic;
		
		if (astatic == bstatic)
			return CompareMemberInfo (a, b);
		
		if (astatic)
			return -1;
		
		return 1;
	}
	
	static Comparer PropertyInfoComparer = new Comparer (new ComparerFunc (ComparePropertyInfo));
	
	public static PropertyInfo [] Sort (PropertyInfo [] inf)
	{
		Array.Sort (inf, PropertyInfoComparer);
		return inf;
	}
	
	static int CompareEventInfo (object a, object b)
	{
		EventInfo aa = (EventInfo) a, bb = (EventInfo) b;
		
		bool astatic = aa.GetAddMethod (true).IsStatic;
		bool bstatic = bb.GetAddMethod (true).IsStatic;
		
		if (astatic == bstatic)
			return CompareMemberInfo (a, b);
		
		if (astatic)
			return -1;
		
		return 1;
	}
	
	static Comparer EventInfoComparer = new Comparer (new ComparerFunc (CompareEventInfo));
	
	public static EventInfo [] Sort (EventInfo [] inf)
	{
		Array.Sort (inf, EventInfoComparer);
		return inf;
	}
}
}
