context: using System;
  using System.IO;
  using System.Xml;
  using System.Collections;
  using System.Reflection;
  using Server;
  using Server.Items;
  
  namespace Server.Custom
  {
  	public class Categorization
  	{
  		private static CategoryEntry m_RootItems, m_RootMobiles;
  
  		public static CategoryEntry Items
  		{
  			get
  			{
  				if ( m_RootItems == null )
  					Load();
  
  				return m_RootItems;
  			}
  		}
  
  		public static CategoryEntry Mobiles
  		{
  			get
  			{
  				if ( m_RootMobiles == null )
  					Load();
  
  				return m_RootMobiles;
  			}
  		}
  
  		public static void Initialize()
  		{
  			Commands.Register( "RebuildCategorization", AccessLevel.Administrator, new CommandEventHandler( RebuildCategorization_OnCommand ) );
  		}
  
  		[Usage( "RebuildCategorization" )]
  		[Description( "Rebuilds the categorization data file used by the Add command." )]
  		public static void RebuildCategorization_OnCommand( CommandEventArgs e )
  		{
  			CategoryEntry root = new CategoryEntry( null, "Add Menu", new CategoryEntry[]{ Items, Mobiles } );
  
  			Export( root, Path.Combine(Core.Config.ConfigDirectory, "objects.xml"), "Objects" );
  
  			e.Mobile.SendMessage( "Categorization menu rebuilt." );
  		}
  
  		public static void RecurseFindCategories( CategoryEntry ce, ArrayList list )
  		{
  			list.Add( ce );
  
  			for ( int i = 0; i < ce.SubCategories.Length; ++i )
  				RecurseFindCategories( ce.SubCategories[i], list );
  		}
  
  		public static void Export( CategoryEntry ce, string fileName, string title )
  		{
  			XmlTextWriter xml = new XmlTextWriter( fileName, System.Text.Encoding.UTF8 );
  
  			xml.Indentation = 1;
  			xml.IndentChar = '\t';
  			xml.Formatting = Formatting.Indented;
  
  			xml.WriteStartDocument( true );
  
  			RecurseExport( xml, ce );
  
  			xml.Flush();
  			xml.Close();
  		}
  
  		public static void RecurseExport( XmlTextWriter xml, CategoryEntry ce )
  		{
  			xml.WriteStartElement( "category" );
  
  			xml.WriteAttributeString( "title", ce.Title );
  
  			ArrayList subCats = new ArrayList( ce.SubCategories );
  
  			subCats.Sort( new CategorySorter() );
  
  			for ( int i = 0; i < subCats.Count; ++i )
  				RecurseExport( xml, (CategoryEntry)subCats[i] );
  
  			ce.Matched.Sort( new CategorySorter() );
  
  			for ( int i = 0; i < ce.Matched.Count; ++i )
  			{
  				CategoryTypeEntry cte = (CategoryTypeEntry)ce.Matched[i];
  
  				xml.WriteStartElement( "object" );
  
  				xml.WriteAttributeString( "type", cte.Type.ToString() );
  
  				object obj = cte.Object;
  
  				if ( obj is Item )
  				{
  					Item item = (Item)obj;
  
  					int itemID = item.ItemID;
  
  					if ( item is BaseAddon && ((BaseAddon)item).Components.Count == 1 )
  						itemID = ((AddonComponent)(((BaseAddon)item).Components[0])).ItemID;
  
  					if ( itemID >= 0x4000 )
  						itemID = 1;
  
  					xml.WriteAttributeString( "gfx", XmlConvert.ToString( itemID ) );
  
  					int hue = item.Hue & 0x7FFF;
  
  					if ( (hue & 0x4000) != 0 )
  						hue = 0;
  
  					if ( hue != 0 )
  						xml.WriteAttributeString( "hue", XmlConvert.ToString( hue ) );
  
  					item.Delete();
  				}
  				else if ( obj is Mobile )
  				{
  					Mobile mob = (Mobile)obj;
  
  					int itemID = ShrinkTable.Lookup( mob, 1 );
  
  					xml.WriteAttributeString( "gfx", XmlConvert.ToString( itemID ) );
  
  					int hue = mob.Hue & 0x7FFF;
  
  					if ( (hue & 0x4000) != 0 )
  						hue = 0;
  
  					if ( hue != 0 )
  						xml.WriteAttributeString( "hue", XmlConvert.ToString( hue ) );
  
  					mob.Delete();
  				}
  
  				xml.WriteEndElement();
  			}
  
  			xml.WriteEndElement();
  		}
  
  		public static void Load()
  		{
  			ArrayList types = new ArrayList();
  
  			Library[] libs = ScriptCompiler.Libraries;
  			for (int i = 0; i < libs.Length; ++i)
  				AddTypes(libs[i], types);
  
  			m_RootItems = Load( types, Path.Combine(Core.Config.ConfigDirectory, "items.cfg") );
  			m_RootMobiles = Load( types, Path.Combine(Core.Config.ConfigDirectory, "mobiles.cfg") );
  		}
  
  		private static CategoryEntry Load( ArrayList types, string config )
  		{
  			CategoryLine[] lines = CategoryLine.Load( config );
  
  			if ( lines.Length > 0 )
  			{
  				int index = 0;
  				CategoryEntry root = new CategoryEntry( null, lines, ref index );
  
  				Fill( root, types );
  
  				return root;
  			}
  
  			return new CategoryEntry();
  		}
  
  		private static Type typeofItem = typeof( Item );
  		private static Type typeofMobile = typeof( Mobile );
  		private static Type typeofConstructable = typeof( ConstructableAttribute );
  
  		private static bool IsConstructable( Type type )
  		{
  			if ( !type.IsSubclassOf( typeofItem ) && !type.IsSubclassOf( typeofMobile ) )
  				return false;
  
  			ConstructorInfo ctor = type.GetConstructor( Type.EmptyTypes );
  
  			return ( ctor != null && ctor.IsDefined( typeofConstructable, false ) );
  		}
  
  		private static void AddTypes(Library lib, ArrayList types)
  		{
  			Type[] allTypes = lib.Types;
  
  			for ( int i = 0; i < allTypes.Length; ++i )
  			{
  				Type type = allTypes[i];
  
  				if ( type.IsAbstract )
  					continue;
  
  				if ( IsConstructable( type ) )
  					types.Add( type );
  			}
  		}
  
  		private static void Fill( CategoryEntry root, ArrayList list )
  		{
  			for ( int i = 0; i < list.Count; ++i )
  			{
  				Type type = (Type)list[i];
  
gt: 				CategoryEntry match = GetDeepestMatch( root, type );