context: //
  // CompareContext.cs
  //
  // (C) 2007 - 2008 Novell, Inc. (http://www.novell.com)
  //
  // 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.Collections.Generic;
  using System.Threading;
  
  namespace GuiCompare {
  
  	public class CompareContext
  	{
  		Func<CompAssembly> reference_loader, target_loader;
  		
  		public CompareContext (Func<CompAssembly> reference, Func<CompAssembly> target)
  		{
  			reference_loader = reference;
  			target_loader = target;
  		}
  
  		public ComparisonNode Comparison {
  			get { return comparison; }
  		}
  
  		public void Compare ()
  		{
  			if (t != null)
  				throw new InvalidOperationException ("compare already running");
  
  			t = new Thread (CompareThread);
  			t.Start ();
  		}
  
  		public void StopCompare ()
  		{
  		}
  
  		bool TryLoad (ref CompAssembly assembly, Func<CompAssembly> loader)
  		{
  			try {
  				assembly = loader ();
  				return true;
  			} catch (Exception e) {
  				OnError (e.ToString ());
  				return false;
  			}
  		}
  
  		void CompareThread ()
  		{
  			try {
  				ProgressChange (Double.NaN, "Loading reference...");
  
  				if (!TryLoad (ref reference, reference_loader))
  					return;
  
  				ProgressChange (Double.NaN, "Loading target...");
  
  				if (!TryLoad (ref target, target_loader))
  					return;
  
  				ProgressChange (0.0, "Comparing...");
  
  				comparison = target.GetComparisonNode ();
  
  				List<CompNamed> ref_namespaces = reference.GetNamespaces();
  				
  				total_comparisons = CountComparisons (ref_namespaces);
  				comparisons_performed = 0;
  				
  				CompareTypeLists (comparison, reference.GetNamespaces(), target.GetNamespaces());
  
  				CompareAttributes (comparison, reference, target);
  			} catch (Exception exc) {
  				OnError (exc.Message);
  			} finally {
  				Finish ();
  			}
  		}
  
  		int total_comparisons;
  		int comparisons_performed;
  
  		int CountComparisons (List<CompNamed> list)
  		{
  			int rv = 0;
  			foreach (CompNamed l in list) {
  				rv += CountComparisons (l);
  			}
  			return rv;
  		}
  		
  		int CountComparisons (CompNamed named)
  		{
  			int rv = 1;
  			if (named is ICompMemberContainer) {
  				ICompMemberContainer container = (ICompMemberContainer)named;
  				rv += CountComparisons (container.GetInterfaces());
  				rv += CountComparisons (container.GetConstructors());
  				rv += CountComparisons (container.GetMethods());
  				rv += CountComparisons (container.GetProperties());
  				rv += CountComparisons (container.GetFields());
  			}
  			if (named is ICompTypeContainer) {
  				ICompTypeContainer container = (ICompTypeContainer)named;
  				rv += CountComparisons (container.GetNestedInterfaces());
  				rv += CountComparisons (container.GetNestedClasses());
  				rv += CountComparisons (container.GetNestedStructs());
  				rv += CountComparisons (container.GetNestedEnums());
  				rv += CountComparisons (container.GetNestedDelegates());
  			}
  			if (named is ICompAttributeContainer) {
  				rv += CountComparisons (((ICompAttributeContainer)named).GetAttributes());
  			}
  			return rv;
  		}
  		
  		void CompareNestedTypes (ComparisonNode parent, ICompTypeContainer reference_container, ICompTypeContainer target_container)
  		{
  			CompareTypeLists (parent,
  			                  reference_container.GetNestedInterfaces(), target_container.GetNestedInterfaces());
  			CompareTypeLists (parent,
  			                  reference_container.GetNestedClasses(), target_container.GetNestedClasses());
  			CompareTypeLists (parent,
  			                  reference_container.GetNestedStructs(), target_container.GetNestedStructs());
  			CompareTypeLists (parent,
  			                  reference_container.GetNestedEnums(), target_container.GetNestedEnums());
  			CompareTypeLists (parent,
  			                  reference_container.GetNestedDelegates(), target_container.GetNestedDelegates());
  		}
  
  		void CompareBaseTypes (ComparisonNode parent, ICompHasBaseType reference_type, ICompHasBaseType target_type)
  		{
  			if (reference_type.GetBaseType() != target_type.GetBaseType()) {
  				parent.AddError (String.Format ("reference has base class of {0}, target has base class of {1}",
  								reference_type.GetBaseType(),
  								target_type.GetBaseType()));
  			}
  			
  			if (reference_type.IsAbstract != target_type.IsAbstract) {
  				string ref_mod = (reference_type.IsAbstract && reference_type.IsSealed) ? "static" : "abstract";
  				string tar_mod = (target_type.IsAbstract && target_type.IsSealed) ? "static" : "abstract";
  
  				parent.AddError (String.Format ("reference is {0} {2}, target is {1} {3}",
  								reference_type.IsAbstract ? null : "not", target_type.IsAbstract ? null : "not",
  								ref_mod, tar_mod));
  			} else if (reference_type.IsSealed != target_type.IsSealed) {
  				string ref_mod = (reference_type.IsAbstract && reference_type.IsSealed) ? "static" : "sealed";
  				string tar_mod = (target_type.IsAbstract && target_type.IsSealed) ? "static" : "sealed";
  				
  				parent.AddError (String.Format ("reference is {0} {2}, target is {1} {3}",
  								reference_type.IsSealed ? null : "not", target_type.IsSealed ? null : "not",
  								ref_mod, tar_mod));
  			}
  		}
  		
  		void CompareParameters (ComparisonNode parent, ICompParameters reference, ICompParameters target)
  		{
  			var r = reference.GetParameters ();
  			var t = target.GetParameters ();
  			
  			if (r.Count != t.Count) {
  				throw new NotImplementedException (string.Format ("Should never happen with valid data ({0} != {1})", r.Count, t.Count));
  			}
  			
  			for (int i = 0; i < r.Count; ++i) {
  				var r_i = r [i];
  				var t_i = t [i];
  				
  				if (r_i.TypeReference != t_i.TypeReference) {
  					parent.AddError (string.Format ("Parameter `{0}' type mismatch", t_i.Name));
  				}
  				
  				if (r_i.Name != t_i.Name) {
  					parent.AddError (string.Format ("Parameter name `{0}' should be `{1}'", t_i.Name, r_i.Name));
  				}
  				
  				if (r_i.IsOptional != t_i.IsOptional) {
  					if (r_i.IsOptional)
  						parent.AddError (string.Format ("Parameter `{0}' is missing a default value", t_i.Name));
  					else
  						parent.AddError (string.Format ("Parameter `{0}' should not have a default value", t_i.Name));
  				}
  				
  				CompareAttributes (parent, r_i, t_i);
  			}
  		}
  		
  		void CompareTypeParameters (ComparisonNode parent, ICompGenericParameter reference, ICompGenericParameter target)
  		{
  			var r = reference.GetTypeParameters ();
  			var t = target.GetTypeParameters ();
  			if (r == null && t == null)
  				return;
  
  			if (r.Count != t.Count) {
  				throw new NotImplementedException (string.Format ("Should never happen with valid data ({0} != {1})", r.Count, t.Count));
  			}
  
  			for (int i = 0; i < r.Count; ++i) {
  				var r_i = r [i];
  				var t_i = t [i];
  				
  				if (r_i.GenericAttributes != t_i.GenericAttributes) {
  					parent.AddError (string.Format ("reference type parameter {2} has {0} generic attributes, target type parameter {3} has {1} generic attributes",
  						CompGenericParameter.GetGenericAttributeDesc (r_i.GenericAttributes),
  						CompGenericParameter.GetGenericAttributeDesc (t_i.GenericAttributes),
  						r_i.Name,
  						t_i.Name));
  				}
  
  				// TODO: Compare constraints properly			
  				if (r_i.HasConstraints != t_i.HasConstraints) {
  					parent.AddError (string.Format ("Type parameter `{0}' constraints mismatch", r_i.Name));
  				}
  
  				CompareAttributes (parent, r_i, t_i);
  			}
  		}
  
  		void CompareTypeLists (ComparisonNode parent,
  		                       List<CompNamed> reference_list,
  		                       List<CompNamed> target_list)
  		{
  			int m = 0, a = 0;
  
  			reference_list.Sort (CompNamed.Compare);
  			target_list.Sort (CompNamed.Compare);
  
  			while (m < reference_list.Count || a < target_list.Count) {
  				if (m == reference_list.Count) {
  					AddExtra (parent, target_list[a]);
  					a++;
  					continue;
  				}
  				else if (a == target_list.Count) {
  					AddMissing (parent, reference_list[m]);
  					m++;
  					continue;
  				}
  
  				int c = String.Compare (reference_list[m].Name, target_list[a].Name);
  				comparisons_performed ++;
  				
  				if (c == 0) {
  					ProgressChange ((double)comparisons_performed / total_comparisons * 100.0, String.Format ("Comparing {0} {1}", reference_list[m].Type, reference_list[m].Name));
  
  					/* the names match, further investigation is required */
  					ComparisonNode comparison = target_list[a].GetComparisonNode();
  					parent.AddChild (comparison);
  
  					// compare base types
  					if (reference_list[m] is ICompHasBaseType && target_list[a] is ICompHasBaseType) {
  						CompareBaseTypes (comparison,
  								  (ICompHasBaseType)reference_list[m],
  								  (ICompHasBaseType)target_list[a]);
  					}
  					
  					// compares generic type parameters
  					if (reference_list[m] is ICompGenericParameter && target_list[a] is ICompGenericParameter) {
  						CompareTypeParameters (comparison,
  								(ICompGenericParameter)reference_list[m],
  								(ICompGenericParameter)target_list[a]);
  					}
  					
  					// compare nested types
  					if (reference_list[m] is ICompTypeContainer && target_list[a] is ICompTypeContainer) {
  						CompareNestedTypes (comparison,
  						                    (ICompTypeContainer)reference_list[m],
  						                    (ICompTypeContainer)target_list[a]);
  					}
  					if (reference_list[m] is ICompMemberContainer && target_list[a] is ICompMemberContainer) {
  						CompareMembers (comparison,
  						                (ICompMemberContainer)reference_list[m],
  						                (ICompMemberContainer)target_list[a]);
  					}
  					if (reference_list[m] is ICompAttributeContainer && target_list[a] is ICompAttributeContainer) {
  						CompareAttributes (comparison,
  								   (ICompAttributeContainer)reference_list[m],
  								   (ICompAttributeContainer)target_list[a]);
  					}
  
  					m++;
  					a++;
  				}
  				else if (c < 0) {
  					/* reference name is before target name, reference name is missing from target */
  					AddMissing (parent, reference_list[m]);
  					m++;
  				}
  				else {
  					/* reference name is after target name, target name is extra */
  					AddExtra (parent, target_list[a]);
  					a++;
  				}
  			}
  		}
  
  		void CompareAttributes (ComparisonNode parent,
  		                        ICompAttributeContainer reference_container, ICompAttributeContainer target_container)
  		{
  			int m = 0, a = 0;
  			
  			List<CompNamed> reference_attrs = reference_container.GetAttributes ();
  			List<CompNamed> target_attrs = target_container.GetAttributes ();
  			
  			reference_attrs.Sort (CompNamed.Compare);
  			target_attrs.Sort (CompNamed.Compare);
  			
  			while (m < reference_attrs.Count || a < target_attrs.Count) {
  				if (m == reference_attrs.Count) {
  					AddExtra (parent, target_attrs[a]);
  					a++;
  					continue;
  				}
  				else if (a == target_attrs.Count) {
  					AddMissing (parent, reference_attrs[m]);
  					m++;
  					continue;
  				}
  
  				int c = String.Compare (reference_attrs[m].Name, target_attrs[a].Name);
  				comparisons_performed ++;
  
  				if (c == 0) {
  					/* the names match, further investigation is required */
  // 					Console.WriteLine ("method {0} is in both, doing more comparisons", reference_list[m].Name);
  					ComparisonNode comparison = target_attrs[a].GetComparisonNode();
  					parent.AddChild (comparison);
  					//CompareParameters (comparison, reference_list[m], target_namespace [target_list[a]]);
  					m++;
  					a++;
  				}
  				else if (c < 0) {
  					/* reference name is before target name, reference name is missing from target */
  					AddMissing (parent, reference_attrs[m]);
  					m++;
  				}
  				else {
  					/* reference name is after target name, target name is extra */
  					AddExtra (parent, target_attrs[a]);
  					a++;
  				}
  			}
  		}
  		
  		void CompareMembers (ComparisonNode parent,
  		                     ICompMemberContainer reference_container, ICompMemberContainer target_container)
  		{
  			CompareMemberLists (parent,
  			                    reference_container.GetInterfaces(), target_container.GetInterfaces());
  			CompareMemberLists (parent,
  			                    reference_container.GetConstructors(), target_container.GetConstructors());
  			CompareMemberLists (parent,
  			                    reference_container.GetMethods(), target_container.GetMethods());
  			CompareMemberLists (parent,
  			                    reference_container.GetProperties(), target_container.GetProperties());
  			CompareMemberLists (parent,
  			                    reference_container.GetFields(), target_container.GetFields());
  			CompareMemberLists (parent,
  			                    reference_container.GetEvents(), target_container.GetEvents());
  		}
  
  		void CompareMemberLists (ComparisonNode parent,
  		                         List<CompNamed> reference_list,
  		                         List<CompNamed> target_list)
  		{
  			int m = 0, a = 0;
  
  			reference_list.Sort (CompNamed.Compare);
  			target_list.Sort (CompNamed.Compare);
  
  			while (m < reference_list.Count || a < target_list.Count) {
  				if (m == reference_list.Count) {
  					AddExtra (parent, target_list[a]);
  					a++;
  					continue;
  				}
  				else if (a == target_list.Count) {
  					AddMissing (parent, reference_list[m]);
  					m++;
  					continue;
  				}
  
  				int c = CompNamed.Compare (reference_list[m], target_list[a]);
  				comparisons_performed ++;
  
  				if (c == 0) {
  					/* the names match, further investigation is required */
  // 					Console.WriteLine ("method {0} is in both, doing more comparisons", reference_list[m].Name);
  
gt: 					ComparisonNode comparison = target_list[a].GetComparisonNode();