context: #region References
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using Server.ContextMenus;
  using Server.Network;
  #endregion
  
  namespace Server.Items
  {
  	public delegate void OnItemConsumed(Item item, int amount);
  
  	public delegate int CheckItemGroup(Item a, Item b);
  
  	public delegate void ContainerSnoopHandler(Container cont, Mobile from);
  
  	public class Container : Item
  	{
          private List<byte> m_FreePositions = new List<byte>();
  
          public void FreePosition(byte pos)
          {
              int maxpos = -1;
  
              foreach (Item item in Items)
              {
                  if (item.GridLocation > maxpos && item.GridLocation != pos)
                      maxpos = item.GridLocation;
              }
  
              maxpos++;
  
              if (pos > maxpos)
              {
                  pos = (byte)maxpos;
  
                  for (int i = 0; i < m_FreePositions.Count; i++)
                  {
                      byte b = m_FreePositions[i];
  
                      if (b > (pos))
                      {
                          m_FreePositions.RemoveAt(i);
                          i--;
                      }
                  }
              }
  
              if (!m_FreePositions.Contains(pos))
                  m_FreePositions.Add(pos);
          }
  
          public bool IsFreePosition(byte pos)
          {
              if (m_FreePositions.Contains(pos))
              {
                  m_FreePositions.Remove(pos);
                  return true;
              }
  
              foreach (Item item in this.Items)
              {
                  if (item.GridLocation == pos)
                      return false;
              }
  
              return true;
          }
  
          public byte GetNewPosition()
          {
              if (m_FreePositions.Count > 0)
              {
                  byte newpos = m_FreePositions[m_FreePositions.Count - 1];
                  m_FreePositions.RemoveAt(m_FreePositions.Count - 1);
                  return newpos;
              }
  
              int pos = -1;
  
              foreach (Item item in Items)
              {
                  if (item.GridLocation > pos)
                      pos = item.GridLocation;
              }
  
              return (byte)(pos + 1);
          }
  
          public override bool CanInsureItem { get { return false; } }
  
          private string m_EngravedText;
  
  		private static ContainerSnoopHandler m_SnoopHandler;
  
  		public static ContainerSnoopHandler SnoopHandler { get { return m_SnoopHandler; } set { m_SnoopHandler = value; } }
  
  		private ContainerData m_ContainerData;
  
  		private int m_DropSound;
  		private int m_GumpID;
  		private int m_MaxItems;
  
  		private int m_TotalItems;
  		private int m_TotalWeight;
  		private int m_TotalGold;
  
  		private bool m_LiftOverride;
  
  		internal List<Item> m_Items;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public string EngravedText
          {
              get { return m_EngravedText; }
              set
              {
                  m_EngravedText = value;
                  InvalidateProperties();
              }
          }
  
  		public ContainerData ContainerData
  		{
  			get
  			{
  				if (m_ContainerData == null)
  				{
  					UpdateContainerData();
  				}
  
  				return m_ContainerData;
  			}
  			set { m_ContainerData = value; }
  		}
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public override int ItemID
  		{
  			get { return base.ItemID; }
  			set
  			{
  				int oldID = ItemID;
  
  				base.ItemID = value;
  
  				if (ItemID != oldID)
  				{
  					UpdateContainerData();
  				}
  			}
  		}
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public int GumpID { get { return (m_GumpID == -1 ? DefaultGumpID : m_GumpID); } set { m_GumpID = value; } }
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public int DropSound { get { return (m_DropSound == -1 ? DefaultDropSound : m_DropSound); } set { m_DropSound = value; } }
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public int MaxItems
  		{
  			get { return (m_MaxItems == -1 ? DefaultMaxItems : m_MaxItems); }
  			set
  			{
  				m_MaxItems = value;
  				InvalidateProperties();
  			}
  		}
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public virtual int MaxWeight
  		{
  			get
  			{
  				if (Parent is Container && ((Container)Parent).MaxWeight == 0)
  				{
  					return 0;
  				}
  				else
  				{
  					return DefaultMaxWeight;
  				}
  			}
  		}
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public bool LiftOverride { get { return m_LiftOverride; } set { m_LiftOverride = value; } }
  
  		public virtual void UpdateContainerData()
  		{
  			ContainerData = ContainerData.GetData(ItemID);
  		}
  
          public virtual bool UseLockedRestriction
          {
              get { return false; }
          }
  
          private bool m_IsLockedContainer;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool IsLockedContainer
          {
              get { return m_IsLockedContainer; }
              set { m_IsLockedContainer = value; }
          }
  
  		public virtual Rectangle2D Bounds { get { return ContainerData.Bounds; } }
  		public virtual int DefaultGumpID { get { return ContainerData.GumpID; } }
  		public virtual int DefaultDropSound { get { return ContainerData.DropSound; } }
  
  		public virtual int DefaultMaxItems { get { return m_GlobalMaxItems; } }
  		public virtual int DefaultMaxWeight { get { return m_GlobalMaxWeight; } }
  
  		public virtual bool IsDecoContainer { get { return !Movable && !IsLockedDown && !IsSecure && Parent == null && !m_LiftOverride; } }
  
  		public virtual int GetDroppedSound(Item item)
  		{
  			int dropSound = item.GetDropSound();
  
  			return dropSound != -1 ? dropSound : DropSound;
  		}
  
  		public override void OnSnoop(Mobile from)
  		{
  			if (m_SnoopHandler != null)
  			{
  				m_SnoopHandler(this, from);
  			}
  		}
  
  		public override bool CheckLift(Mobile from, Item item, ref LRReason reject)
  		{
  			if (!from.IsStaff() && IsDecoContainer)
  			{
  				reject = LRReason.CannotLift;
  				return false;
  			}
  
  			return base.CheckLift(from, item, ref reject);
  		}
  
  		public override bool CheckItemUse(Mobile from, Item item)
  		{
  			if (item != this && from.AccessLevel < AccessLevel.GameMaster && IsDecoContainer)
  			{
  				from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
  				return false;
  			}
  
  			return base.CheckItemUse(from, item);
  		}
  
  		public bool CheckHold(Mobile m, Item item, bool message)
  		{
  			return CheckHold(m, item, message, true, 0, 0);
  		}
  
  		public bool CheckHold(Mobile m, Item item, bool message, bool checkItems)
  		{
  			return CheckHold(m, item, message, checkItems, 0, 0);
  		}
  
  		public virtual bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight)
  		{
  			if (!m.IsStaff())
  			{
  				if (IsDecoContainer)
  				{
  					if (message)
  					{
  						SendCantStoreMessage(m, item);
  					}
  
  					return false;
  				}
  
  				int maxItems = MaxItems;
  
  				if (checkItems && maxItems != 0 &&
  					(TotalItems + plusItems + item.TotalItems + (item.IsVirtualItem ? 0 : 1)) > maxItems)
  				{
  					if (message)
  					{
  						SendFullItemsMessage(m, item);
  					}
  
  					return false;
  				}
  				else
  				{
  					int maxWeight = MaxWeight;
  
  					if (maxWeight != 0 && (TotalWeight + plusWeight + item.TotalWeight + item.PileWeight) > maxWeight)
  					{
  						if (message)
  						{
  							SendFullWeightMessage(m, item);
  						}
  
  						return false;
  					}
  				}
  			}
  
  			object parent = Parent;
  
  			while (parent != null)
  			{
  				if (parent is Container)
  				{
  					return ((Container)parent).CheckHold(m, item, message, checkItems, plusItems, plusWeight);
  				}
  				else if (parent is Item)
  				{
  					parent = ((Item)parent).Parent;
  				}
  				else
  				{
  					break;
  				}
  			}
  
  			return true;
  		}
  
  		public virtual void SendFullItemsMessage(Mobile to, Item item)
  		{
  			to.SendMessage("That container cannot hold more items.");
  		}
  
  		public virtual void SendFullWeightMessage(Mobile to, Item item)
  		{
  			to.SendMessage("That container cannot hold more weight.");
  		}
  
  		public virtual void SendCantStoreMessage(Mobile to, Item item)
  		{
  			to.SendLocalizedMessage(500176); // That is not your container, you can't store things here.
  		}
  
          public virtual bool OnDragDropInto(Mobile from, Item item, Point3D p, byte gridloc)
  		{
  			if (!CheckHold(from, item, true, true))
  			{
  				return false;
  			}
  
  			item.Location = new Point3D(p.m_X, p.m_Y, 0);
              item.SetGridLocation(gridloc, this);
  			AddItem(item);
  
  			from.SendSound(GetDroppedSound(item), GetWorldLocation());
  
  			return true;
  		}
  
  		private class GroupComparer : IComparer
  		{
  			private readonly CheckItemGroup m_Grouper;
  
  			public GroupComparer(CheckItemGroup grouper)
  			{
  				m_Grouper = grouper;
  			}
  
  			public int Compare(object x, object y)
  			{
  				var a = (Item)x;
  				var b = (Item)y;
  
  				return m_Grouper(a, b);
  			}
  		}
  
  		#region Consume[...]
  		public bool ConsumeTotalGrouped(Type type, int amount, bool recurse, OnItemConsumed callback, CheckItemGroup grouper)
  		{
  			if (grouper == null)
  			{
  				throw new ArgumentNullException();
  			}
  
  			Item[] typedItems = FindItemsByType(type, recurse);
  
  			var groups = new List<List<Item>>();
  			int idx = 0;
  
  			while (idx < typedItems.Length)
  			{
  				Item a = typedItems[idx++];
  				var group = new List<Item>();
  
  				group.Add(a);
  
  				while (idx < typedItems.Length)
  				{
  					Item b = typedItems[idx];
  					int v = grouper(a, b);
  
  					if (v == 0)
  					{
  						group.Add(b);
  					}
  					else
  					{
  						break;
  					}
  
  					++idx;
  				}
  
  				groups.Add(group);
  			}
  
  			var items = new Item[groups.Count][];
  			var totals = new int[groups.Count];
  
  			bool hasEnough = false;
  
  			for (int i = 0; i < groups.Count; ++i)
  			{
  				items[i] = groups[i].ToArray();
  				//items[i] = (Item[])(((ArrayList)groups[i]).ToArray( typeof( Item ) ));
  
  				for (int j = 0; j < items[i].Length; ++j)
  				{
  					totals[i] += items[i][j].Amount;
  				}
  
  				if (totals[i] >= amount)
  				{
  					hasEnough = true;
  				}
  			}
  
  			if (!hasEnough)
  			{
  				return false;
  			}
  
  			for (int i = 0; i < items.Length; ++i)
  			{
  				if (totals[i] >= amount)
  				{
  					int need = amount;
  
  					for (int j = 0; j < items[i].Length; ++j)
  					{
  						Item item = items[i][j];
  
  						int theirAmount = item.Amount;
  
  						if (theirAmount < need)
  						{
  							if (callback != null)
  							{
  								callback(item, theirAmount);
  							}
  
  							item.Delete();
  							need -= theirAmount;
  						}
  						else
  						{
  							if (callback != null)
  							{
  								callback(item, need);
  							}
  
  							item.Consume(need);
  							break;
  						}
  					}
  
  					break;
  				}
  			}
  
  			return true;
  		}
  
  		public int ConsumeTotalGrouped(
  			Type[] types, int[] amounts, bool recurse, OnItemConsumed callback, CheckItemGroup grouper)
  		{
  			if (types.Length != amounts.Length)
  			{
  				throw new ArgumentException();
  			}
  			else if (grouper == null)
  			{
  				throw new ArgumentNullException();
  			}
  
  			var items = new Item[types.Length][][];
  			var totals = new int[types.Length][];
  
  			for (int i = 0; i < types.Length; ++i)
  			{
  				Item[] typedItems = FindItemsByType(types[i], recurse);
  
  				var groups = new List<List<Item>>();
  				int idx = 0;
  
  				while (idx < typedItems.Length)
  				{
  					Item a = typedItems[idx++];
  					var group = new List<Item>();
  
  					group.Add(a);
  
  					while (idx < typedItems.Length)
  					{
  						Item b = typedItems[idx];
  						int v = grouper(a, b);
  
  						if (v == 0)
  						{
  							group.Add(b);
  						}
  						else
  						{
  							break;
  						}
  
  						++idx;
  					}
  
  					groups.Add(group);
  				}
  
  				items[i] = new Item[groups.Count][];
  				totals[i] = new int[groups.Count];
  
  				bool hasEnough = false;
  
  				for (int j = 0; j < groups.Count; ++j)
  				{
  					items[i][j] = groups[j].ToArray();
  					//items[i][j] = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) ));
  
  					for (int k = 0; k < items[i][j].Length; ++k)
  					{
  						totals[i][j] += items[i][j][k].Amount;
  					}
  
  					if (totals[i][j] >= amounts[i])
  					{
  						hasEnough = true;
  					}
  				}
  
  				if (!hasEnough)
  				{
  					return i;
  				}
  			}
  
  			for (int i = 0; i < items.Length; ++i)
  			{
  				for (int j = 0; j < items[i].Length; ++j)
  				{
  					if (totals[i][j] >= amounts[i])
  					{
  						int need = amounts[i];
  
  						for (int k = 0; k < items[i][j].Length; ++k)
  						{
  							Item item = items[i][j][k];
  
  							int theirAmount = item.Amount;
  
  							if (theirAmount < need)
  							{
  								if (callback != null)
  								{
  									callback(item, theirAmount);
  								}
  
  								item.Delete();
  								need -= theirAmount;
  							}
  							else
  							{
  								if (callback != null)
  								{
  									callback(item, need);
  								}
  
  								item.Consume(need);
  								break;
  							}
  						}
  
  						break;
  					}
  				}
  			}
  
  			return -1;
  		}
  
  		public int ConsumeTotalGrouped(
  			Type[][] types, int[] amounts, bool recurse, OnItemConsumed callback, CheckItemGroup grouper)
  		{
  			if (types.Length != amounts.Length)
  			{
  				throw new ArgumentException();
  			}
  			else if (grouper == null)
  			{
  				throw new ArgumentNullException();
  			}
  
  			var items = new Item[types.Length][][];
  			var totals = new int[types.Length][];
  
  			for (int i = 0; i < types.Length; ++i)
  			{
  				Item[] typedItems = FindItemsByType(types[i], recurse);
  
  				var groups = new List<List<Item>>();
  				int idx = 0;
  
  				while (idx < typedItems.Length)
  				{
  					Item a = typedItems[idx++];
  					var group = new List<Item>();
  
  					group.Add(a);
  
  					while (idx < typedItems.Length)
  					{
  						Item b = typedItems[idx];
  						int v = grouper(a, b);
  
  						if (v == 0)
  						{
  							group.Add(b);
  						}
  						else
  						{
  							break;
  						}
  
  						++idx;
  					}
  
  					groups.Add(group);
  				}
  
  				items[i] = new Item[groups.Count][];
  				totals[i] = new int[groups.Count];
  
  				bool hasEnough = false;
  
  				for (int j = 0; j < groups.Count; ++j)
  				{
  					items[i][j] = groups[j].ToArray();
  
  					for (int k = 0; k < items[i][j].Length; ++k)
  					{
  						totals[i][j] += items[i][j][k].Amount;
  					}
  
  					if (totals[i][j] >= amounts[i])
  					{
  						hasEnough = true;
  					}
  				}
  
  				if (!hasEnough)
  				{
  					return i;
  				}
  			}
  
  			for (int i = 0; i < items.Length; ++i)
  			{
  				for (int j = 0; j < items[i].Length; ++j)
  				{
  					if (totals[i][j] >= amounts[i])
  					{
  						int need = amounts[i];
  
  						for (int k = 0; k < items[i][j].Length; ++k)
  						{
  							Item item = items[i][j][k];
  
  							int theirAmount = item.Amount;
  
  							if (theirAmount < need)
  							{
  								if (callback != null)
  								{
  									callback(item, theirAmount);
  								}
  
  								item.Delete();
  								need -= theirAmount;
  							}
  							else
  							{
  								if (callback != null)
  								{
  									callback(item, need);
  								}
  
  								item.Consume(need);
  								break;
  							}
  						}
  
  						break;
  					}
  				}
  			}
  
  			return -1;
  		}
  
  		public int ConsumeTotal(Type[][] types, int[] amounts)
  		{
  			return ConsumeTotal(types, amounts, true, null);
  		}
  
  		public int ConsumeTotal(Type[][] types, int[] amounts, bool recurse)
  		{
  			return ConsumeTotal(types, amounts, recurse, null);
  		}
  
  		public int ConsumeTotal(Type[][] types, int[] amounts, bool recurse, OnItemConsumed callback)
  		{
  			if (types.Length != amounts.Length)
  			{
  				throw new ArgumentException();
  			}
  
  			var items = new Item[types.Length][];
  			var totals = new int[types.Length];
  
  			for (int i = 0; i < types.Length; ++i)
  			{
  				items[i] = FindItemsByType(types[i], recurse);
  
  				for (int j = 0; j < items[i].Length; ++j)
  				{
  					totals[i] += items[i][j].Amount;
  				}
  
  				if (totals[i] < amounts[i])
  				{
  					return i;
  				}
  			}
  
  			for (int i = 0; i < types.Length; ++i)
  			{
  				int need = amounts[i];
  
  				for (int j = 0; j < items[i].Length; ++j)
  				{
  					Item item = items[i][j];
  
  					int theirAmount = item.Amount;
  
  					if (theirAmount < need)
  					{
  						if (callback != null)
  						{
  							callback(item, theirAmount);
  						}
  
  						item.Delete();
  						need -= theirAmount;
  					}
  					else
  					{
  						if (callback != null)
  						{
  							callback(item, need);
  						}
  
  						item.Consume(need);
  						break;
  					}
  				}
  			}
  
  			return -1;
  		}
  
  		public int ConsumeTotal(Type[] types, int[] amounts)
  		{
  			return ConsumeTotal(types, amounts, true, null);
  		}
  
  		public int ConsumeTotal(Type[] types, int[] amounts, bool recurse)
  		{
  			return ConsumeTotal(types, amounts, recurse, null);
  		}
  
  		public int ConsumeTotal(Type[] types, int[] amounts, bool recurse, OnItemConsumed callback)
  		{
  			if (types.Length != amounts.Length)
  			{
  				throw new ArgumentException();
  			}
  
  			var items = new Item[types.Length][];
  			var totals = new int[types.Length];
  
  			for (int i = 0; i < types.Length; ++i)
  			{
  				items[i] = FindItemsByType(types[i], recurse);
  
  				for (int j = 0; j < items[i].Length; ++j)
  				{
  					totals[i] += items[i][j].Amount;
  				}
  
  				if (totals[i] < amounts[i])
  				{
  					return i;
  				}
  			}
  
  			for (int i = 0; i < types.Length; ++i)
  			{
  				int need = amounts[i];
  
  				for (int j = 0; j < items[i].Length; ++j)
  				{
  					Item item = items[i][j];
  
  					int theirAmount = item.Amount;
  
  					if (theirAmount < need)
  					{
  						if (callback != null)
  						{
  							callback(item, theirAmount);
  						}
  
  						item.Delete();
  						need -= theirAmount;
  					}
  					else
  					{
  						if (callback != null)
  						{
  							callback(item, need);
  						}
  
  						item.Consume(need);
  						break;
  					}
  				}
  			}
  
  			return -1;
  		}
  
  		public bool ConsumeTotal(Type type, int amount)
  		{
  			return ConsumeTotal(type, amount, true, null);
  		}
  
  		public bool ConsumeTotal(Type type, int amount, bool recurse)
  		{
  			return ConsumeTotal(type, amount, recurse, null);
  		}
  
  		public bool ConsumeTotal(Type type, int amount, bool recurse, OnItemConsumed callback)
  		{
  			Item[] items = FindItemsByType(type, recurse);
  
  			// First pass, compute total
  			int total = 0;
  
  			for (int i = 0; i < items.Length; ++i)
  			{
  				total += items[i].Amount;
  			}
  
  			if (total >= amount)
  			{
  				// We've enough, so consume it
  
  				int need = amount;
  
  				for (int i = 0; i < items.Length; ++i)
  				{
  					Item item = items[i];
  
  					int theirAmount = item.Amount;
  
  					if (theirAmount < need)
  					{
  						if (callback != null)
  						{
  							callback(item, theirAmount);
  						}
  
  						item.Delete();
  						need -= theirAmount;
  					}
  					else
  					{
  						if (callback != null)
  						{
  							callback(item, need);
  						}
  
  						item.Consume(need);
  
  						return true;
  					}
  				}
  			}
  
  			return false;
  		}
  
  		public int ConsumeUpTo(Type type, int amount)
  		{
  			return ConsumeUpTo(type, amount, true);
  		}
  
  		public int ConsumeUpTo(Type type, int amount, bool recurse)
  		{
  			int consumed = 0;
  
  			var toDelete = new Queue<Item>();
  
  			RecurseConsumeUpTo(this, type, amount, recurse, ref consumed, toDelete);
  
  			while (toDelete.Count > 0)
  			{
  				toDelete.Dequeue().Delete();
  			}
  
  			return consumed;
  		}
  
  		private static void RecurseConsumeUpTo(Item current, Type type, int amount, bool recurse, ref int consumed, Queue<Item> toDelete)
  		{
  			if (current != null && current.Items.Count > 0)
  			{
  				List<Item> list = current.Items;
  
  				for (int i = 0; i < list.Count; ++i)
  				{
  					Item item = list[i];
  
  					if (type.IsAssignableFrom(item.GetType()))
  					{
  						int need = amount - consumed;
  						int theirAmount = item.Amount;
  
  						if (theirAmount <= need)
  						{
  							toDelete.Enqueue(item);
  							consumed += theirAmount;
  						}
  						else
  						{
  							item.Amount -= need;
  							consumed += need;
  
  							return;
  						}
  					}
                      else if (recurse && item is Container && !(((Container)item).UseLockedRestriction && ((Container)item).IsLockedContainer))
                      {
                          RecurseConsumeUpTo(item, type, amount, recurse, ref consumed, toDelete);
                      }
  				}
  			}
  		}
  		#endregion
  
  		#region Get[BestGroup]Amount
  		public int GetBestGroupAmount(Type type, bool recurse, CheckItemGroup grouper)
  		{
  			if (grouper == null)
  			{
  				throw new ArgumentNullException();
  			}
  
  			int best = 0;
  
  			Item[] typedItems = FindItemsByType(type, recurse);
  
  			var groups = new List<List<Item>>();
  			int idx = 0;
  
  			while (idx < typedItems.Length)
  			{
  				Item a = typedItems[idx++];
  				var group = new List<Item>();
  
  				group.Add(a);
  
  				while (idx < typedItems.Length)
  				{
  					Item b = typedItems[idx];
  					int v = grouper(a, b);
  
  					if (v == 0)
  					{
  						group.Add(b);
  					}
  					else
  					{
  						break;
  					}
  
  					++idx;
  				}
  
  				groups.Add(group);
  			}
  
  			for (int i = 0; i < groups.Count; ++i)
  			{
  				Item[] items = groups[i].ToArray();
  
  				//Item[] items = (Item[])(((ArrayList)groups[i]).ToArray( typeof( Item ) ));
  				int total = 0;
  
  				for (int j = 0; j < items.Length; ++j)
  				{
  					total += items[j].Amount;
  				}
  
  				if (total >= best)
  				{
  					best = total;
  				}
  			}
  
  			return best;
  		}
  
  		public int GetBestGroupAmount(Type[] types, bool recurse, CheckItemGroup grouper)
  		{
  			if (grouper == null)
  			{
  				throw new ArgumentNullException();
  			}
  
  			int best = 0;
  
  			Item[] typedItems = FindItemsByType(types, recurse);
  
  			var groups = new List<List<Item>>();
  			int idx = 0;
  
  			while (idx < typedItems.Length)
  			{
  				Item a = typedItems[idx++];
  				var group = new List<Item>();
  
  				group.Add(a);
  
  				while (idx < typedItems.Length)
  				{
  					Item b = typedItems[idx];
  					int v = grouper(a, b);
  
  					if (v == 0)
  					{
  						group.Add(b);
  					}
  					else
  					{
  						break;
  					}
  
  					++idx;
  				}
  
  				groups.Add(group);
  			}
  
  			for (int j = 0; j < groups.Count; ++j)
  			{
  				Item[] items = groups[j].ToArray();
  				//Item[] items = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) ));
  				int total = 0;
  
  				for (int k = 0; k < items.Length; ++k)
  				{
  					total += items[k].Amount;
  				}
  
  				if (total >= best)
  				{
  					best = total;
  				}
  			}
  
  			return best;
  		}
  
  		public int GetBestGroupAmount(Type[][] types, bool recurse, CheckItemGroup grouper)
  		{
  			if (grouper == null)
  			{
  				throw new ArgumentNullException();
  			}
  
  			int best = 0;
  
  			for (int i = 0; i < types.Length; ++i)
  			{
  				Item[] typedItems = FindItemsByType(types[i], recurse);
  
  				var groups = new List<List<Item>>();
  				int idx = 0;
  
  				while (idx < typedItems.Length)
  				{
  					Item a = typedItems[idx++];
  					var group = new List<Item>();
  
  					group.Add(a);
  
  					while (idx < typedItems.Length)
  					{
  						Item b = typedItems[idx];
  						int v = grouper(a, b);
  
  						if (v == 0)
  						{
  							group.Add(b);
  						}
  						else
  						{
  							break;
  						}
  
  						++idx;
  					}
  
  					groups.Add(group);
  				}
  
  				for (int j = 0; j < groups.Count; ++j)
  				{
  					Item[] items = groups[j].ToArray();
  					//Item[] items = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) ));
  					int total = 0;
  
  					for (int k = 0; k < items.Length; ++k)
  					{
  						total += items[k].Amount;
  					}
  
  					if (total >= best)
  					{
  						best = total;
  					}
  				}
  			}
  
  			return best;
  		}
  
  		public int GetAmount(Type type)
  		{
  			return GetAmount(type, true);
  		}
  
  		public int GetAmount(Type type, bool recurse)
  		{
  			Item[] items = FindItemsByType(type, recurse);
  
  			int amount = 0;
  
  			for (int i = 0; i < items.Length; ++i)
  			{
  				amount += items[i].Amount;
  			}
  
  			return amount;
  		}
  
  		public int GetAmount(Type[] types)
  		{
  			return GetAmount(types, true);
  		}
  
  		public int GetAmount(Type[] types, bool recurse)
  		{
  			Item[] items = FindItemsByType(types, recurse);
  
  			int amount = 0;
  
  			for (int i = 0; i < items.Length; ++i)
  			{
  				amount += items[i].Amount;
  			}
  
  			return amount;
  		}
  		#endregion
  
  		private static readonly List<Item> m_FindItemsList = new List<Item>();
  
  		#region Non-Generic FindItem[s] by Type
  		public Item[] FindItemsByType(Type type)
  		{
  			return FindItemsByType(type, true);
  		}
  
  		public Item[] FindItemsByType(Type type, bool recurse)
  		{
  			if (m_FindItemsList.Count > 0)
  			{
  				m_FindItemsList.Clear();
  			}
  
  			RecurseFindItemsByType(this, type, recurse, m_FindItemsList);
  
  			return m_FindItemsList.ToArray();
  		}
  
  		private static void RecurseFindItemsByType(Item current, Type type, bool recurse, List<Item> list)
  		{
  			if (current != null && current.Items.Count > 0)
  			{
  				List<Item> items = current.Items;
  
  				for (int i = 0; i < items.Count; ++i)
  				{
  					Item item = items[i];
  
  					if (type.IsAssignableFrom(item.GetType())) // item.GetType().IsAssignableFrom( type ) )
  					{
  						list.Add(item);
  					}
  
                      if (recurse && item is Container && !(((Container)item).UseLockedRestriction && ((Container)item).IsLockedContainer))
                      {
                          RecurseFindItemsByType(item, type, recurse, list);
                      }
  				}
  			}
  		}
  
  		public Item[] FindItemsByType(Type[] types)
  		{
  			return FindItemsByType(types, true);
  		}
  
  		public Item[] FindItemsByType(Type[] types, bool recurse)
  		{
  			if (m_FindItemsList.Count > 0)
  			{
  				m_FindItemsList.Clear();
  			}
  
  			RecurseFindItemsByType(this, types, recurse, m_FindItemsList);
  
  			return m_FindItemsList.ToArray();
  		}
  
  		private static void RecurseFindItemsByType(Item current, Type[] types, bool recurse, List<Item> list)
  		{
  			if (current != null && current.Items.Count > 0)
  			{
  				List<Item> items = current.Items;
  
  				for (int i = 0; i < items.Count; ++i)
  				{
  					Item item = items[i];
  
  					if (InTypeList(item, types))
  					{
  						list.Add(item);
  					}
  
                      if (recurse && item is Container && !(((Container)item).UseLockedRestriction && ((Container)item).IsLockedContainer))
                      {
                          RecurseFindItemsByType(item, types, recurse, list);
                      }
  				}
  			}
  		}
  
  		public Item FindItemByType(Type type)
  		{
  			return FindItemByType(type, true);
  		}
  
  		public Item FindItemByType(Type type, bool recurse)
  		{
  			return RecurseFindItemByType(this, type, recurse);
  		}
  
  		private static Item RecurseFindItemByType(Item current, Type type, bool recurse)
  		{
  			if (current != null && current.Items.Count > 0)
  			{
  				List<Item> list = current.Items;
  
  				for (int i = 0; i < list.Count; ++i)
  				{
  					Item item = list[i];
  
  					if (type.IsAssignableFrom(item.GetType()))
  					{
  						return item;
  					}
                      else if (recurse && item is Container && !(((Container)item).UseLockedRestriction && ((Container)item).IsLockedContainer))
                      {
                          Item check = RecurseFindItemByType(item, type, recurse);
  
                          if (check != null)
                              return check;
                      }
  				}
  			}
  
  			return null;
  		}
  
  		public Item FindItemByType(Type[] types)
  		{
  			return FindItemByType(types, true);
  		}
  
  		public Item FindItemByType(Type[] types, bool recurse)
  		{
  			return RecurseFindItemByType(this, types, recurse);
  		}
  
  		private static Item RecurseFindItemByType(Item current, Type[] types, bool recurse)
  		{
  			if (current != null && current.Items.Count > 0)
  			{
  				List<Item> list = current.Items;
  
  				for (int i = 0; i < list.Count; ++i)
  				{
  					Item item = list[i];
  
  					if (InTypeList(item, types))
  					{
  						return item;
  					}
                      else if (recurse && item is Container && !((Container)item).IsLockedContainer)
                      {
                          Item check = RecurseFindItemByType(item, types, recurse);
  
                          if (check != null)
                              return check;
                      }
  				}
  			}
  
  			return null;
  		}
  		#endregion
  
  		#region Generic FindItem[s] by Type
  		public List<T> FindItemsByType<T>() where T : Item
  		{
  			return FindItemsByType<T>(true, null);
  		}
  
  		public List<T> FindItemsByType<T>(bool recurse) where T : Item
  		{
  			return FindItemsByType<T>(recurse, null);
  		}
  
  		public List<T> FindItemsByType<T>(Predicate<T> predicate) where T : Item
  		{
  			return FindItemsByType(true, predicate);
  		}
  
  		public List<T> FindItemsByType<T>(bool recurse, Predicate<T> predicate) where T : Item
  		{
  			if (m_FindItemsList.Count > 0)
  			{
  				m_FindItemsList.Clear();
  			}
  
  			var list = new List<T>();
  
  			RecurseFindItemsByType(this, recurse, list, predicate);
  
  			return list;
  		}
  
  		private static void RecurseFindItemsByType<T>(Item current, bool recurse, List<T> list, Predicate<T> predicate)
  			where T : Item
  		{
  			if (current != null && current.Items.Count > 0)
  			{
  				List<Item> items = current.Items;
  
  				for (int i = 0; i < items.Count; ++i)
  				{
  					Item item = items[i];
  
  					if (typeof(T).IsAssignableFrom(item.GetType()))
  					{
  						var typedItem = (T)item;
  
  						if (predicate == null || predicate(typedItem))
  						{
  							list.Add(typedItem);
  						}
  					}
  
  					if (recurse && item is Container)
  					{
  						RecurseFindItemsByType(item, recurse, list, predicate);
  					}
  				}
  			}
  		}
  
  		public T FindItemByType<T>() where T : Item
  		{
  			return FindItemByType<T>(true);
  		}
  
  		public T FindItemByType<T>(Predicate<T> predicate) where T : Item
  		{
  			return FindItemByType(true, predicate);
  		}
  
  		public T FindItemByType<T>(bool recurse) where T : Item
  		{
  			return FindItemByType<T>(recurse, null);
  		}
  
  		public T FindItemByType<T>(bool recurse, Predicate<T> predicate) where T : Item
  		{
  			return RecurseFindItemByType(this, recurse, predicate);
  		}
  
  		private static T RecurseFindItemByType<T>(Item current, bool recurse, Predicate<T> predicate) where T : Item
  		{
  			if (current != null && current.Items.Count > 0)
  			{
  				List<Item> list = current.Items;
  
  				for (int i = 0; i < list.Count; ++i)
  				{
  					Item item = list[i];
  
  					if (typeof(T).IsAssignableFrom(item.GetType()))
  					{
  						var typedItem = (T)item;
  
  						if (predicate == null || predicate(typedItem))
  						{
  							return typedItem;
  						}
  					}
  					else if (recurse && item is Container)
  					{
  						T check = RecurseFindItemByType(item, recurse, predicate);
  
  						if (check != null)
  						{
  							return check;
  						}
  					}
  				}
  			}
  
  			return null;
  		}
  		#endregion
  
          public void DeleteItemsByType(Type type)
          {
              for (int i = 0; i < Items.Count; i++)
              {
                  Item item = Items[i] as Item;
  
                  if (item != null && item.GetType() == type)
                      item.Delete();
                  else if (item is Container)
                      ((Container)item).DeleteItemsByType(type);
              }
          }
  
          public void DeleteItemsByType<T>() where T : Item
          {
              for (int i = 0; i < Items.Count; i++)
              {
                  Item item = Items[i] as Item;
  
                  if (item is T)
                      item.Delete();
                  else if (item is Container)
                      ((Container)item).DeleteItemsByType<T>();
              }
          }
  
  		private static bool InTypeList(Item item, Type[] types)
  		{
  			Type t = item.GetType();
  
  			for (int i = 0; i < types.Length; ++i)
  			{
  				if (types[i].IsAssignableFrom(t))
  				{
  					return true;
  				}
  			}
  
  			return false;
  		}
  
  		private static void SetSaveFlag(ref SaveFlag flags, SaveFlag toSet, bool setIf)
  		{
  			if (setIf)
  			{
  				flags |= toSet;
  			}
  		}
  
  		private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet)
  		{
  			return ((flags & toGet) != 0);
  		}
  
  		[Flags]
  		private enum SaveFlag : byte
  		{
  			None = 0x00000000,
  			MaxItems = 0x00000001,
  			GumpID = 0x00000002,
  			DropSound = 0x00000004,
  			LiftOverride = 0x00000008,
              EngravedText = 0x00000010
  		}
  
          public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
          {
              base.GetContextMenuEntries(from, list);
          }
  
  		public override void Serialize(GenericWriter writer)
  		{
  			base.Serialize(writer);
  
  			writer.Write(3); // version
  
              //Version 3
              writer.Write(m_EngravedText);
  
  			var flags = SaveFlag.None;
  
  			SetSaveFlag(ref flags, SaveFlag.MaxItems, m_MaxItems != -1);
  			SetSaveFlag(ref flags, SaveFlag.GumpID, m_GumpID != -1);
  			SetSaveFlag(ref flags, SaveFlag.DropSound, m_DropSound != -1);
  			SetSaveFlag(ref flags, SaveFlag.LiftOverride, m_LiftOverride);
              SetSaveFlag(ref flags, SaveFlag.EngravedText, !String.IsNullOrEmpty(m_EngravedText));
  
  			writer.Write((byte)flags);
  
  			if (GetSaveFlag(flags, SaveFlag.MaxItems))
  			{
  				writer.WriteEncodedInt(m_MaxItems);
  			}
  
  			if (GetSaveFlag(flags, SaveFlag.GumpID))
  			{
  				writer.WriteEncodedInt(m_GumpID);
  			}
  
  			if (GetSaveFlag(flags, SaveFlag.DropSound))
  			{
  				writer.WriteEncodedInt(m_DropSound);
  			}
  
              if (GetSaveFlag(flags, SaveFlag.EngravedText))
              {
                  writer.Write(m_EngravedText);
              }
  		}
  
  		public override void Deserialize(GenericReader reader)
  		{
  			base.Deserialize(reader);
  
  			int version = reader.ReadInt();
  
  			switch (version)
  			{
                  case 3:
                      {
                          m_EngravedText = reader.ReadString();
                          goto case 2;
                      }
  				case 2:
  					{
  						var flags = (SaveFlag)reader.ReadByte();
  
  						if (GetSaveFlag(flags, SaveFlag.MaxItems))
  						{
  							m_MaxItems = reader.ReadEncodedInt();
  						}
  						else
  						{
  							m_MaxItems = -1;
  						}
  
  						if (GetSaveFlag(flags, SaveFlag.GumpID))
  						{
  							m_GumpID = reader.ReadEncodedInt();
  						}
  						else
  						{
  							m_GumpID = -1;
  						}
  
  						if (GetSaveFlag(flags, SaveFlag.DropSound))
  						{
  							m_DropSound = reader.ReadEncodedInt();
  						}
  						else
  						{
  							m_DropSound = -1;
  						}
  
  						m_LiftOverride = GetSaveFlag(flags, SaveFlag.LiftOverride);
  
                          if (GetSaveFlag(flags, SaveFlag.EngravedText))
                          {
                              m_EngravedText = reader.ReadString();
                          }
  						break;
  					}
  				case 1:
  					{
  						m_MaxItems = reader.ReadInt();
  						goto case 0;
  					}
  				case 0:
  					{
  						if (version < 1)
  						{
  							m_MaxItems = m_GlobalMaxItems;
  						}
  
  						m_GumpID = reader.ReadInt();
  
gt: 						m_DropSound = reader.ReadInt();