private void UpdateInventoryMargin(IEnumerable <ThingGroupSelector> groupSelectors)
        {
            ValidateArg.NotNull(groupSelectors, nameof(groupSelectors));

            foreach (ThingGroupSelector groupSelector in groupSelectors)
            {
                this.InventoryMargins[groupSelector] = groupSelector.AllowedStackCount * -1;
            }

            ConcurrentBag <ThingGroupSelectorPool> pools = new ConcurrentBag <ThingGroupSelectorPool>();

            Parallel.ForEach(
                Partitioner.Create(MakeListForPawnGearAndInventory(_pawn)),
                (Thing thing) =>
            {
                ThingGroupSelectorPool pool = this.FindPotentialThingGroupSelectors(thing, groupSelectors);

                if (pool.OrderedSelectorTuples.Any())
                {
                    pools.Add(pool);
                }
            });

            foreach (ThingGroupSelectorPool pool in pools)
            {
                this.Restock(pool);
            }
        }
        /// <summary>
        /// Find <see cref="ThingGroupSelector"/> that allows <paramref name="thing"/>.
        /// </summary>
        /// <param name="thing"> Thing to check if there is any selector fits. </param>
        /// <param name="stackCount"> Stack count of <paramref name="thing"/>. </param>
        /// <param name="groupSelectors"> A list of fiiting selectors. </param>
        /// <returns> A data packet that contains all information needed to find the best suited selector for <paramref name="thing"/>. </returns>
        protected virtual ThingGroupSelectorPool FindPotentialThingGroupSelectors(Thing thing, int stackCount, IEnumerable <ThingGroupSelector> groupSelectors)
        {
            ValidateArg.NotNull(groupSelectors, nameof(groupSelectors));

            ThingGroupSelectorPool pool = new ThingGroupSelectorPool()
            {
                Thing                 = thing,
                StackCount            = stackCount,
                OrderedSelectorTuples = new List <Tuple <ThingSelector, ThingGroupSelector> >(),
            };

            foreach (ThingGroupSelector groupSelector in groupSelectors)
            {
                if (groupSelector.Allows(thing, out ThingSelector thingSelector))
                {
                    pool.OrderedSelectorTuples.Add(Tuple.Create(thingSelector, groupSelector));
                }
            }

            if (pool.OrderedSelectorTuples.Count > 1)
            {
                pool.OrderedSelectorTuples = pool.OrderedSelectorTuples.OrderBy(t => t.Item1, ThingSelectorComparer.Instance).ToList();
            }

            return(pool);
        }
        /// <summary>
        /// Remove thing from <see cref="CompAwesomeInventoryLoadout.InventoryMargins"/>.
        /// </summary>
        /// <param name="thing"> Thing to remove. </param>
        /// <param name="stackCountToDelete"> Stack count to remove. </param>
        protected virtual void DeleteStock(Thing thing, int stackCountToDelete)
        {
            ThingGroupSelectorPool pool = this.FindPotentialThingGroupSelectors(thing, stackCountToDelete, this.InventoryMargins.Keys);

            foreach (var tuple in pool.OrderedSelectorTuples)
            {
                int maxNegativeMargin = tuple.Item2.AllowedStackCount * -1;
                if (this.InventoryMargins[tuple.Item2] - pool.StackCount < maxNegativeMargin)
                {
                    pool.StackCount -= this.InventoryMargins[tuple.Item2] - maxNegativeMargin;
                    this.InventoryMargins[tuple.Item2] = maxNegativeMargin;
                    this.UpdateThreshold(new[] { tuple.Item2 });
                }
                else
                {
                    this.InventoryMargins[tuple.Item2] -= pool.StackCount;
                    this.UpdateThreshold(new[] { tuple.Item2 });
                    break;
                }
            }

            // Rebalance inventory margin for cases where bottom threshold in ThingGroupSelectors are set to CanRestock in the code above.
            if (pool.OrderedSelectorTuples.Any(t => t.Item2.UseBottomThreshold && _bottomThresholdLookup[t.Item2].CanRestock))
            {
                this.UpdateInventoryMargin(pool.OrderedSelectorTuples.Select(t => t.Item2));
            }
        }
        /// <summary>
        /// Keep <see cref="CompAwesomeInventoryLoadout.InventoryMargins" /> in sync with pawn's inventory.
        /// </summary>
        /// <param name="pool"> A data packet contains all necessary information. </param>
        protected virtual void Restock(ThingGroupSelectorPool pool)
        {
            int restockCount = pool.StackCount;

            foreach (var tuple in pool.OrderedSelectorTuples)
            {
                if (!tuple.Item2.UseBottomThreshold || _bottomThresholdLookup[tuple.Item2].CanRestock)
                {
                    if (this.InventoryMargins[tuple.Item2] + restockCount <= 0)
                    {
                        this.InventoryMargins[tuple.Item2] += restockCount;
                        if (this.InventoryMargins[tuple.Item2] == 0)
                        {
                            this.UpdateThreshold(new[] { tuple.Item2 });
                        }

                        restockCount = 0;
                        break;
                    }
                    else
                    {
                        restockCount += this.InventoryMargins[tuple.Item2];
                        this.InventoryMargins[tuple.Item2] = 0;
                        this.UpdateThreshold(new[] { tuple.Item2 });
                    }
                }
            }

            if (pool.OrderedSelectorTuples.Any() && restockCount != 0)
            {
                this.InventoryMargins[pool.OrderedSelectorTuples.First().Item2] += restockCount;
            }
        }
        /// <summary>
        /// Remove thing from <see cref="CompAwesomeInventoryLoadout.InventoryMargins"/>.
        /// </summary>
        /// <param name="thing"> Thing to remove. </param>
        /// <param name="stackCountToDelete"> Stack count to remove. </param>
        protected virtual void DeleteStock(Thing thing, int stackCountToDelete)
        {
            ThingGroupSelectorPool pool = this.FindPotentialThingGroupSelectors(thing, stackCountToDelete, this.InventoryMargins.Keys);

            foreach (var tuple in pool.OrderedSelectorTuples)
            {
                int maxNegativeMargin = tuple.Item2.AllowedStackCount * -1;
                if (this.InventoryMargins[tuple.Item2] - pool.StackCount < maxNegativeMargin)
                {
                    pool.StackCount -= this.InventoryMargins[tuple.Item2] - maxNegativeMargin;
                    this.InventoryMargins[tuple.Item2] = maxNegativeMargin;
                }
                else
                {
                    this.InventoryMargins[tuple.Item2] -= pool.StackCount;
                    break;
                }
            }
        }
        /// <summary>
        /// Keep <see cref="CompAwesomeInventoryLoadout.InventoryMargins" /> in sync with pawn's inventory.
        /// </summary>
        /// <param name="pool"> A data packet contains all necessary information. </param>
        protected virtual void Restock(ThingGroupSelectorPool pool)
        {
            int restockCount = pool.StackCount;

            foreach (var tuple in pool.OrderedSelectorTuples)
            {
                if (this.InventoryMargins[tuple.Item2] + restockCount <= 0)
                {
                    this.InventoryMargins[tuple.Item2] += restockCount;
                    restockCount = 0;
                    break;
                }
                else
                {
                    restockCount += this.InventoryMargins[tuple.Item2];
                    this.InventoryMargins[tuple.Item2] = 0;
                }
            }

            if (pool.OrderedSelectorTuples.Any() && restockCount != 0)
            {
                this.InventoryMargins[pool.OrderedSelectorTuples.First().Item2] += restockCount;
            }
        }