context: #region References
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  
  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 bool ResValidator(Item item);
  	public delegate void ContainerSnoopHandler(Container cont, Mobile from);
  
  	public class Container : Item
  	{
  		#region Enhanced Client Support
  		public virtual void ValidateGridLocation(Item item)
  		{
  			var pos = item.GridLocation;
  
  			if (!IsFreePosition(pos))
  			{
  				item.GridLocation = GetNewPosition(pos);
  			}
  		}
  
  		public virtual bool IsFreePosition(byte pos)
  		{
  			if (pos < 0 || pos > 0x7C)
  			{
  				return false;
  			}
  
  			return Items.All(i => i.GridLocation != pos);
  		}
  
  		public virtual byte GetNewPosition(byte current)
  		{
  			var index = 0;
  			var next = (byte)(current + 1);
  
  			while (++index < 0x7D)
  			{
  				if (!IsFreePosition(next))
  				{
  					if (next == 0x7C)
  					{
  						next = 0;
  
  						if (IsFreePosition(next))
  						{
  							return next;
  						}
  					}
  				}
  				else
  				{
  					return next;
  				}
  
  				next++;
  			}
  
  			return 0;
  		}
  
  		public virtual void ValidatePositions()
  		{
  			foreach (var item in Items)
  			{
  				if (IsFreePosition(item.GridLocation))
  				{
  					item.GridLocation = GetNewPosition(item.GridLocation);
  				}
  			}
  		}
  		#endregion
  
  		private static ContainerSnoopHandler m_SnoopHandler;
  
  		public static ContainerSnoopHandler SnoopHandler { get => 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;
  
  		public ContainerData ContainerData
  		{
  			get
  			{
  				if (m_ContainerData == null)
  				{
  					UpdateContainerData();
  				}
  
  				return m_ContainerData;
  			}
  			set => m_ContainerData = value;
  		}
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public override int ItemID
  		{
  			get => base.ItemID;
  			set
  			{
  				var oldID = ItemID;
  
  				base.ItemID = value;
  
  				if (ItemID != oldID)
  				{
  					UpdateContainerData();
  				}
  			}
  		}
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public int GumpID { get => m_GumpID == -1 ? DefaultGumpID : m_GumpID; set => m_GumpID = value; }
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public int DropSound { get => m_DropSound == -1 ? DefaultDropSound : m_DropSound; set => m_DropSound = value; }
  
  		[CommandProperty(AccessLevel.GameMaster)]
  		public int MaxItems
  		{
  			get => 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 => m_LiftOverride; set => m_LiftOverride = value; }
  
  		public virtual void UpdateContainerData()
  		{
  			ContainerData = ContainerData.GetData(ItemID);
  		}
  
  		public virtual Rectangle2D Bounds => ContainerData.Bounds;
  		public virtual int DefaultGumpID => ContainerData.GumpID;
  		public virtual int DefaultDropSound => ContainerData.DropSound;
  
  		public virtual int DefaultMaxItems => GlobalMaxItems;
  		public virtual int DefaultMaxWeight => GlobalMaxWeight;
  
  		public virtual bool IsDecoContainer => !Movable && !IsLockedDown && !IsSecure && Parent == null && !m_LiftOverride;
  
  		public virtual int GetDroppedSound(Item item)
  		{
  			var 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;
  				}
  
  				var maxItems = MaxItems;
  
  				if (checkItems && maxItems != 0 &&
  					(TotalItems + plusItems + item.TotalItems + (item.IsVirtualItem ? 0 : 1)) > maxItems)
  				{
  					if (message)
  					{
  						SendFullItemsMessage(m, item);
  					}
  
  					return false;
  				}
  				else
  				{
  					var maxWeight = MaxWeight;
  
  					if (maxWeight != 0 && (TotalWeight + plusWeight + item.TotalWeight + item.PileWeight) > maxWeight)
  					{
  						if (message)
  						{
  							SendFullWeightMessage(m, item);
  						}
  
  						return false;
  					}
  				}
  			}
  
  			var 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 bool CheckStack(Mobile from, Item item)
  		{
  			if (item == null || item.Deleted || !item.Stackable)
  			{
  				return false;
  			}
  
  			foreach (var i in Items)
  			{
  				if (i.WillStack(from, item))
  				{
  					return true;
  				}
  			}
  
  			return false;
  		}
  
  		public virtual void SendFullItemsMessage(Mobile to, Item item)
  		{
  			to.SendLocalizedMessage(1080017); // That container cannot hold more items.
  		}
  
  		public virtual void SendFullWeightMessage(Mobile to, Item item)
  		{
  			to.SendLocalizedMessage(1080016); // 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)
  		{
  			if (!CheckHold(from, item, true, true))
  			{
  				return false;
  			}
  
  			item.Location = new Point3D(p.m_X, p.m_Y, 0);
  			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)
  		{
  			return ConsumeTotalGrouped(type, amount, recurse, null, callback, grouper);
  		}
  
  		public bool ConsumeTotalGrouped(Type type, int amount, bool recurse, ResValidator validator, OnItemConsumed callback, CheckItemGroup grouper)
  		{
  			if (grouper == null)
  			{
  				throw new ArgumentNullException();
  			}
  
  			var typedItems = FindItemsByType(type, recurse);
  
  			var groups = new List<List<Item>>();
  			var idx = 0;
  
  			while (idx < typedItems.Length)
  			{
  				var a = typedItems[idx++];
  
  				if (validator != null && !validator(a))
  					continue;
  
  				var group = new List<Item>
  				{
  					a
  				};
  
  				while (idx < typedItems.Length)
  				{
  					var b = typedItems[idx];
  
  					if (validator != null && !validator(b))
  						continue;
  
  					var 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];
  
  			var hasEnough = false;
  
  			for (var i = 0; i < groups.Count; ++i)
  			{
  				items[i] = groups[i].ToArray();
  				//items[i] = (Item[])(((ArrayList)groups[i]).ToArray( typeof( Item ) ));
  
  				for (var j = 0; j < items[i].Length; ++j)
  				{
  					totals[i] += items[i][j].Amount;
  				}
  
  				if (totals[i] >= amount)
  				{
  					hasEnough = true;
  				}
  			}
  
  			if (!hasEnough)
  			{
  				return false;
  			}
  
  			for (var i = 0; i < items.Length; ++i)
  			{
  				if (totals[i] >= amount)
  				{
  					var need = amount;
  
  					for (var j = 0; j < items[i].Length; ++j)
  					{
  						var item = items[i][j];
  
  						var 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)
  		{
  			return ConsumeTotalGrouped(types, amounts, recurse, null, callback, grouper);
  		}
  
  		public int ConsumeTotalGrouped(
  			Type[] types, int[] amounts, bool recurse, ResValidator validator, 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 (var i = 0; i < types.Length; ++i)
  			{
  				var typedItems = FindItemsByType(types[i], recurse);
  
  				var groups = new List<List<Item>>();
  				var idx = 0;
  
  				while (idx < typedItems.Length)
  				{
  					var a = typedItems[idx++];
  
  					if (validator != null && !validator(a))
  						continue;
  
  					var group = new List<Item>
  					{
  						a
  					};
  
  					while (idx < typedItems.Length)
  					{
  						var b = typedItems[idx];
  
  						if (validator != null && !validator(b))
  							continue;
  
  						var 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];
  
  				var hasEnough = false;
  
  				for (var j = 0; j < groups.Count; ++j)
  				{
  					items[i][j] = groups[j].ToArray();
  					//items[i][j] = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) ));
  
  					for (var 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 (var i = 0; i < items.Length; ++i)
  			{
  				for (var j = 0; j < items[i].Length; ++j)
  				{
  					if (totals[i][j] >= amounts[i])
  					{
  						var need = amounts[i];
  
  						for (var k = 0; k < items[i][j].Length; ++k)
  						{
  							var item = items[i][j][k];
  
  							var 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)
  		{
  			return ConsumeTotalGrouped(types, amounts, recurse, null, callback, grouper);
  		}
  
  		public int ConsumeTotalGrouped(
  			Type[][] types, int[] amounts, bool recurse, ResValidator validator, 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 (var i = 0; i < types.Length; ++i)
  			{
  				var typedItems = FindItemsByType(types[i], recurse);
  
  				var groups = new List<List<Item>>();
  				var idx = 0;
  
  				while (idx < typedItems.Length)
  				{
  					var a = typedItems[idx++];
  
  					if (validator != null && !validator(a))
  						continue;
  
  					var group = new List<Item>
  					{
  						a
  					};
  
  					while (idx < typedItems.Length)
  					{
  						var b = typedItems[idx];
  
  
gt: 						if (validator != null && !validator(b))