context: #region References
  using System;
  using System.Reflection;
  
  using CustomsFramework;
  
  using Server.Commands;
  using Server.Commands.Generic;
  using Server.Gumps;
  using Server.Targeting;
  using Server.Mobiles;
  
  using CPA = Server.CommandPropertyAttribute;
  #endregion
  
  namespace Server.Commands
  {
  	[Flags]
  	public enum PropertyAccess
  	{
  		Read = 0x01,
  		Write = 0x02,
  		ReadWrite = Read | Write
  	}
  
  	public class Properties
  	{
  		private static readonly Type _TypeOfCPA = typeof(CPA);
  		private static readonly Type _TypeOfSerial = typeof(Serial);
  		private static readonly Type _TypeOfCustomSerial = typeof(CustomSerial);
  		private static readonly Type _TypeOfType = typeof(Type);
  		private static readonly Type _TypeOfChar = typeof(Char);
  		private static readonly Type _TypeOfString = typeof(String);
  		private static readonly Type _TypeOfIDynamicEnum = typeof(IDynamicEnum);
  		private static readonly Type _TypeOfText = typeof(TextDefinition);
  		private static readonly Type _TypeOfTimeSpan = typeof(TimeSpan);
          private static readonly Type _TypeOfParsable = typeof(ParsableAttribute);
  
  		private static readonly Type[] _ParseTypes = new[] {typeof(string)};
  		private static readonly object[] _ParseParams = new object[1];
  
  		private static readonly Type[] _NumericTypes = new[]
  		{
  			typeof(Byte), typeof(SByte), typeof(Int16), typeof(UInt16), typeof(Int32), typeof(UInt32), typeof(Int64),
  			typeof(UInt64)
  		};
  
  		public static void Initialize()
  		{
  			CommandSystem.Register("Props", AccessLevel.Counselor, Props_OnCommand);
  		}
  
  		public static CPA GetCPA(PropertyInfo p)
  		{
  			var attrs = p.GetCustomAttributes(_TypeOfCPA, false);
  
  			return attrs.Length == 0 ? null : attrs[0] as CPA;
  		}
  
  		public static PropertyInfo[] GetPropertyInfoChain(
  			Mobile m, Type type, string propertyString, PropertyAccess endAccess, ref string failReason)
  		{
  			var split = propertyString.Split('.');
  
  			if (split.Length == 0)
  			{
  				return null;
  			}
  
  			var info = new PropertyInfo[split.Length];
  
  			for (int i = 0; i < info.Length; ++i)
  			{
  				string propertyName = split[i];
  
  				if (CIEqual(propertyName, "current"))
  				{
  					continue;
  				}
  
  				var props = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);
  
  				bool isFinal = i == info.Length - 1;
  
  				PropertyAccess access = endAccess;
  
  				if (!isFinal)
  				{
  					access |= PropertyAccess.Read;
  				}
  
  				foreach (PropertyInfo p in props)
  				{
  					if (!CIEqual(p.Name, propertyName))
  					{
  						continue;
  					}
  
  					CPA attr = GetCPA(p);
  
  					if (attr == null)
  					{
  						failReason = String.Format("Property '{0}' not found.", propertyName);
  						return null;
  					}
  
  					if ((access & PropertyAccess.Read) != 0 && m.AccessLevel < attr.ReadLevel)
  					{
  						failReason = String.Format(
  							"You must be at least {0} to get the property '{1}'.", Mobile.GetAccessLevelName(attr.ReadLevel), propertyName);
  
  						return null;
  					}
  
  					if ((access & PropertyAccess.Write) != 0 && m.AccessLevel < attr.WriteLevel)
  					{
  						failReason = String.Format(
  							"You must be at least {0} to set the property '{1}'.", Mobile.GetAccessLevelName(attr.WriteLevel), propertyName);
  
  						return null;
  					}
  
  					if ((access & PropertyAccess.Read) != 0 && !p.CanRead)
  					{
  						failReason = String.Format("Property '{0}' is write only.", propertyName);
  						return null;
  					}
  
  					if ((access & PropertyAccess.Write) != 0 && (!p.CanWrite || attr.ReadOnly) && isFinal)
  					{
  						failReason = String.Format("Property '{0}' is read only.", propertyName);
  						return null;
  					}
  
  					info[i] = p;
  					type = p.PropertyType;
  					break;
  				}
  
  				if (info[i] != null)
  				{
  					continue;
  				}
  
  				failReason = String.Format("Property '{0}' not found.", propertyName);
  				return null;
  			}
  
  			return info;
  		}
  
  		public static PropertyInfo GetPropertyInfo(
  			Mobile m, ref object obj, string propertyName, PropertyAccess access, ref string failReason)
  		{
  			var chain = GetPropertyInfoChain(m, obj.GetType(), propertyName, access, ref failReason);
  
  			return chain == null ? null : GetPropertyInfo(ref obj, chain, ref failReason);
  		}
  
  		public static PropertyInfo GetPropertyInfo(ref object obj, PropertyInfo[] chain, ref string failReason)
  		{
  			if (chain == null || chain.Length == 0)
  			{
  				failReason = "Property chain is empty.";
  				return null;
  			}
  
  			for (int i = 0; i < chain.Length - 1; ++i)
  			{
  				if (chain[i] == null)
  				{
  					continue;
  				}
  
  				obj = chain[i].GetValue(obj, null);
  
  				if (obj != null)
  				{
  					continue;
  				}
  
  				failReason = String.Format("Property '{0}' is null.", chain[i]);
  				return null;
  			}
  
  			return chain[chain.Length - 1];
  		}
  
  		public static string GetValue(Mobile from, object o, string name)
  		{
  			string failReason = "";
  
  			var chain = GetPropertyInfoChain(from, o.GetType(), name, PropertyAccess.Read, ref failReason);
  
  			if (chain == null || chain.Length == 0)
  			{
  				return failReason;
  			}
  
  			PropertyInfo p = GetPropertyInfo(ref o, chain, ref failReason);
  
  			return p == null ? failReason : InternalGetValue(o, p, chain);
  		}
  
  		public static string IncreaseValue(Mobile m, object o, string[] args)
  		{
  			int len = args.Length / 2;
  
  			var realObjs = new object[len];
  			var realProps = new PropertyInfo[len];
  			var realValues = new int[len];
  
  			bool positive = false, negative = false;
  
  			for (int i = 0; i < realProps.Length; ++i)
  			{
  				int idx = i * 2;
  				string name = args[idx];
  
  				try
  				{
  					string valueString = args[1 + idx];
  
  					if (valueString.StartsWith("0x"))
  					{
  						realValues[i] = Convert.ToInt32(valueString.Substring(2), 16);
  					}
  					else
  					{
  						realValues[i] = Convert.ToInt32(valueString);
  					}
  				}
  				catch
  				{
  					return "Offset value could not be parsed.";
  				}
  
  				if (realValues[i] > 0)
  				{
  					positive = true;
  				}
  				else if (realValues[i] < 0)
  				{
  					negative = true;
  				}
  				else
  				{
  					return "Zero is not a valid value to offset.";
  				}
  
  				string failReason = null;
  				
  				realObjs[i] = o;
  				realProps[i] = GetPropertyInfo(m, ref realObjs[i], name, PropertyAccess.ReadWrite, ref failReason);
  
  				if (failReason != null)
  				{
  					return failReason;
  				}
  
  				if (realProps[i] == null)
  				{
  					return "Property not found.";
  				}
  			}
  
  			for (int i = 0; i < realProps.Length; ++i)
  			{
  				object obj = realProps[i].GetValue(realObjs[i], null);
  
  				if (!(obj is IConvertible))
  				{
  					return "Property is not IConvertable.";
  				}
  
  				try
  				{
  					long v = Convert.ToInt64(obj) + realValues[i];
  					object toSet = Convert.ChangeType(v, realProps[i].PropertyType);
  					realProps[i].SetValue(realObjs[i], toSet, null);
  
  					EventSink.InvokeOnPropertyChanged(new OnPropertyChangedEventArgs(m, realObjs[i], realProps[i], obj, toSet));
  				}
  				catch
  				{
  					return "Value could not be converted";
  				}
  			}
  
  
gt: 			if (realProps.Length == 1)