/*
===========================================================================
Copyright (C) 2015-2019 Project Meteor Dev Team
This file is part of Project Meteor Server.
Project Meteor Server is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Project Meteor Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Project Meteor Server. If not, see .
===========================================================================
*/
using Meteor.Common;
using Meteor.Map.actors.chara.npc;
using Meteor.Map.Actors;
using Meteor.Map.DataObjects;
using Meteor.Map.packets.send.actor.inventory;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Meteor.Map.actors.chara.player
{
    class ItemPackage
    {       
        public const ushort NORMAL                  = 0; //Max 0xC8
        public const ushort UNKNOWN                 = 1; //Max 0x96
        public const ushort LOOT                    = 4; //Max 0xA
        public const ushort MELDREQUEST             = 5; //Max 0x04
        public const ushort BAZAAR                  = 7; //Max 0x0A
        public const ushort CURRENCY_CRYSTALS       = 99; //Max 0x140
        public const ushort KEYITEMS                = 100; //Max 0x500
        public const ushort EQUIPMENT               = 0x00FE; //Max 0x23
        public const ushort TRADE                   = 0x00FD; //Max 0x04
        public const ushort EQUIPMENT_OTHERPLAYER   = 0x00F9; //Max 0x23
        public const ushort MAXSIZE_NORMAL = 200;
        public const ushort MAXSIZE_CURRANCY = 320;
        public const ushort MAXSIZE_KEYITEMS = 500;
        public const ushort MAXSIZE_LOOT = 10;
        public const ushort MAXSIZE_TRADE = 4;
        public const ushort MAXSIZE_MELDREQUEST = 4;
        public const ushort MAXSIZE_BAZAAR = 10;
        public const ushort MAXSIZE_EQUIPMENT = 35;
        public const ushort MAXSIZE_EQUIPMENT_OTHERPLAYER = 0x23;
        public const int ERROR_SUCCESS = 0;
        public const int ERROR_FULL = 1;
        public const int ERROR_HAS_UNIQUE = 2;
        public const int ERROR_SYSTEM = 3;
        private Character owner;
        private ushort itemPackageCapacity;
        private ushort itemPackageCode;
        private bool isTemporary;
        private InventoryItem[] list;
        private bool[] isDirty;
        private bool holdingUpdates = false;
        private int endOfListIndex = 0;
        
        public ItemPackage(Character ownerPlayer, ushort capacity, ushort code, bool temporary = false)
        {
            owner = ownerPlayer;
            itemPackageCapacity = capacity;
            itemPackageCode = code;
            isTemporary = temporary;
            list = new InventoryItem[capacity];
            isDirty = new bool[capacity];
        }
        #region Inventory Management
        public void InitList(List itemsFromDB)
        {
            int i = 0;
            foreach (InventoryItem item in itemsFromDB)
            {
                item.SetOwner(owner, itemPackageCode, (ushort) i);
                list[i++] = item;
            }
            endOfListIndex = i;
        }
        public InventoryItem GetItemAtSlot(ushort slot)
        {
            if (slot < list.Length)
                return list[slot];
            else
                return null;
        }
        public InventoryItem GetItemByUniqueId(ulong uniqueItemId)
        {
            for (int i = 0; i < endOfListIndex; i++)
            {
                InventoryItem item = list[i];
                Debug.Assert(item != null, "Item slot was null!!!");
                if (item.uniqueId == uniqueItemId)
                    return item;
            }
            return null;
        }
        public InventoryItem GetItemByCatelogId(ulong catelogId)
        {
            for (int i = 0; i < endOfListIndex; i++)
            {
                InventoryItem item = list[i];
                Debug.Assert(item != null, "Item slot was null!!!");
                if (item.itemId == catelogId)
                    return item;
            }
            return null;
        }
        public int AddItem(uint itemId)
        {
            return AddItem(itemId, 1, 1);
        }
        public int AddItem(uint itemId, int quantity)
        {
            return AddItem(itemId, quantity, 1);
        }
        public int AddItem(uint itemId, int quantity, byte quality)
        {
            if (!IsSpaceForAdd(itemId, quantity, quality))
                return ERROR_FULL;
            ItemData gItem = Server.GetItemGamedata(itemId);
            //If it's unique, abort
            if (HasItem(itemId) && gItem.isExclusive)
                return ERROR_HAS_UNIQUE;
            if (gItem == null)
            {
                Program.Log.Error("Inventory.AddItem: unable to find item %u", itemId);
                return ERROR_SYSTEM;
            }
            //Check if item id exists 
            int quantityCount = quantity;
            for (int i = 0; i < endOfListIndex; i++)
            {
                InventoryItem item = list[i];
                Debug.Assert(item != null, "Item slot was null!!!");
                if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack)
                {
                    int oldQuantity = item.quantity;
                    item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack);
                    isDirty[i] = true;
                    quantityCount -= (gItem.maxStack - oldQuantity);
                    DoDatabaseQuantity(item.uniqueId, item.quantity);
                    if (quantityCount <= 0)
                        break;
                }
            }
            //New item that spilled over
            while (quantityCount > 0)
            {
                InventoryItem.ItemModifier modifiers = null;
                if (gItem.durability != 0)
                {
                    modifiers = new InventoryItem.ItemModifier();
                    modifiers.durability = (uint)gItem.durability;
                }
                InventoryItem addedItem = Database.CreateItem(itemId, Math.Min(quantityCount, gItem.maxStack), quality, modifiers);
                addedItem.SetOwner(owner, itemPackageCode, (ushort)endOfListIndex);
                isDirty[endOfListIndex] = true;
                list[endOfListIndex++] = addedItem;
                quantityCount -= gItem.maxStack;
                DoDatabaseAdd(addedItem);
            }
            if (owner is Player)
            {
                (owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id));
                SendUpdate();
                (owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id));
            }
            return ERROR_SUCCESS;
        }
        public int AddItems(uint[] itemIds, uint[] quantity = null, byte[] quality = null)
        {
            //Check if has space
            if (!CanAdd(itemIds, quantity, quality))
                return ERROR_FULL;
                        
            for (int i = 0; i < itemIds.Length; i++)
            {
                ItemData gItem = Server.GetItemGamedata(itemIds[i]);
                //If it's unique, abort
                if (HasItem(itemIds[i]) && gItem.isExclusive)
                    return ERROR_HAS_UNIQUE;
                if (gItem == null)
                {
                    Program.Log.Error("Inventory.AddItem: unable to find item %u", itemIds[i]);
                    return ERROR_SYSTEM;
                }
                //Check if item id exists 
                uint setQuantity = quantity != null ? quantity[i] : 1;
                int quantityCount = (int)setQuantity;
                for (int j = 0; j < endOfListIndex; j++)
                {
                    InventoryItem item = list[j];
                    Debug.Assert(item != null, "Item slot was null!!!");
                    byte setQuality = quality != null ? quality[i] : (byte)1;
                    if (item.itemId == itemIds[i] && item.quality == setQuality && item.quantity < gItem.maxStack)
                    {
                        int oldQuantity = item.quantity;
                        item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack);
                        isDirty[j] = true;
                        quantityCount -= (gItem.maxStack - oldQuantity);
                        DoDatabaseQuantity(item.uniqueId, item.quantity);
                        if (quantityCount <= 0)
                            break;
                    }
                }
                //New item that spilled over
                while (quantityCount > 0)
                {
                    InventoryItem.ItemModifier modifiers = null;
                    if (gItem.durability != 0)
                    {
                        modifiers = new InventoryItem.ItemModifier();
                        modifiers.durability = (uint)gItem.durability;
                    }
                    byte setQuality = quality != null ? quality[i] : (byte)1;
                    InventoryItem addedItem = Database.CreateItem(itemIds[i], Math.Min(quantityCount, gItem.maxStack), setQuality, modifiers);
                    addedItem.SetOwner(owner, itemPackageCode, (ushort)endOfListIndex);
                    isDirty[endOfListIndex] = true;
                    list[endOfListIndex++] = addedItem;
                    quantityCount -= gItem.maxStack;
                    DoDatabaseAdd(addedItem);
                }
            }
            if (owner is Player)
            {
                (owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id));
                SendUpdate();
                (owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id));
            }
            return ERROR_SUCCESS;
        }
        
        public int AddItem(InventoryItem itemRef)
        {
            //If it isn't a single item (ie: armor) just add like normal (not valid for BAZAAR)
            if (itemPackageCode != BAZAAR && itemRef.GetItemData().maxStack > 1)
                return AddItem(itemRef.itemId, itemRef.quantity, itemRef.quality);
            if (!IsSpaceForAdd(itemRef.itemId, itemRef.quantity, itemRef.quality))
                return ERROR_FULL;
            ItemData gItem = Server.GetItemGamedata(itemRef.itemId);
            if (gItem == null)
            {
                Program.Log.Error("Inventory.AddItem: unable to find item %u", itemRef.itemId);
                return ERROR_SYSTEM;
            }
            itemRef.SetOwner(owner, itemPackageCode, (ushort)endOfListIndex);
            isDirty[endOfListIndex] = true;
            list[endOfListIndex++] = itemRef;
            DoDatabaseAdd(itemRef);
            if (owner is Player)
            {
                (owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id));
                SendUpdate();
                (owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id));
            }
            return ERROR_SUCCESS;           
        }
        public int MoveItem(ushort position, ItemPackage destinationPackage)
        {
            InventoryItem item = GetItemAtSlot(position);
            if (destinationPackage.CanAdd(item))
            {
                RemoveItemAtSlot(position);
                destinationPackage.AddItem(item);
                return ERROR_SUCCESS;
            }
            return ERROR_FULL;
        }
        public int MoveItem(InventoryItem item, ItemPackage destinationPackage)
        {
            if (destinationPackage == null || item == null)
                return ERROR_SYSTEM;
            if (destinationPackage.CanAdd(item))
            {
                RemoveItem(item);
                destinationPackage.AddItem(item);
                return ERROR_SUCCESS;
            }
            return ERROR_FULL;
        }
        public void RemoveItem(uint itemId)
        {
            RemoveItem(itemId, 1);
        }
        
        public void RemoveItem(uint itemId, int quantity)
        {
            RemoveItem(itemId, quantity, 1);
        }
        public void RemoveItem(uint itemId, int quantity, int quality)
        {
            if (!HasItem(itemId, quantity, quality))
                return;
            List slotsToUpdate = new List();
            List itemsToRemove = new List();
            List slotsToRemove = new List();
            List AddItemPackets = new List();
            //Remove as we go along
            int quantityCount = quantity;
            ushort lowestSlot = 0;
            for (int i = endOfListIndex - 1; i >= 0; i--)
            {
                InventoryItem item = list[i];
                Debug.Assert(item != null, "Item slot was null!!!");
                if (item.itemId == itemId && item.quality == quality)
                {
                    int oldQuantity = item.quantity;
                    //Stack nomnomed
                    if (item.quantity - quantityCount <= 0)
                    {
                        DoDatabaseRemove(list[i].uniqueId);
                        list[i] = null;
                    }
                    //Stack reduced
                    else
                    {
                        item.quantity -= quantityCount;
                        DoDatabaseQuantity(list[i].uniqueId, list[i].quantity);}
                    isDirty[i] = true;
                    quantityCount -= oldQuantity;
                    lowestSlot = item.slot;
                    if (quantityCount <= 0)
                        break;
                }
            }
            DoRealign();
            if (owner is Player)
            {
                (owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id));
                SendUpdate();
                (owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id));
            }
        }
        public void RemoveItem(InventoryItem item)
        {
            if (itemPackageCode == item.itemPackage)
                RemoveItemAtSlot(item.slot);
        }
        
        public void RemoveItemByUniqueId(ulong itemDBId, int quantity)
        {
            ushort slot = 0;
            InventoryItem toDelete = null;
            for (int i = 0; i < endOfListIndex; i++)
            {
                InventoryItem item = list[i];
                Debug.Assert(item != null, "Item slot was null!!!");
                if (item.uniqueId == itemDBId)
                {
                    toDelete = item;
                    break;
                }
                slot++;
            }
            if (toDelete == null)
                return;
            if (quantity >= toDelete.quantity)
            {
                DoDatabaseRemove(toDelete.uniqueId);
                list[slot].ClearOwner();
                list[slot] = null;
            }
            else
            {
                list[slot].quantity -= quantity;
                DoDatabaseQuantity(list[slot].uniqueId, list[slot].quantity);
            }
            isDirty[slot] = true;
            DoRealign();
            if (owner is Player)
            {
                (owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id));
                SendUpdate();
                (owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id));
            }
        }
        public void RemoveItemAtSlot(ushort slot)
        {
            if (slot >= endOfListIndex)
                return;
            DoDatabaseRemove(list[slot].uniqueId);
            list[slot].ClearOwner();
            list[slot] = null;
            isDirty[slot] = true;
            DoRealign();
            if (owner is Player)
            {
                (owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id));
                SendUpdate();
                (owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id));
            }
        }
        public void RemoveItemAtSlot(ushort slot, int quantity)
        {
            if (slot >= endOfListIndex)
                return;
            if (list[slot] != null)
            {
                list[slot].quantity -= quantity;
                if (list[slot].quantity <= 0)
                {
                    DoDatabaseRemove(list[slot].uniqueId);
                    list[slot].ClearOwner();
                    list[slot] = null;
                    DoRealign();
                }
                else
                    DoDatabaseQuantity(list[slot].uniqueId, list[slot].quantity);
                isDirty[slot] = true;
                if (owner is Player)
                {
                    (owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id));
                    SendUpdate();
                    (owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id));
                }
            }                   
        }
        public void Clear()
        {
            for (int i = 0; i < endOfListIndex; i++)
            {
                list[i].ClearOwner();
                list[i] = null;
                isDirty[i] = true;
            }
            endOfListIndex = 0;
            if (owner is Player)
            {
                (owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id));
                SendUpdate();
                (owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id));
            }
        }
        public bool CanAdd(InventoryItem item)
        {
            return itemPackageCapacity - GetCount() > 0;
        }
        public bool CanAdd(uint[] itemIds, uint[] quantity, byte[] quality)
        {
            int tempInvSize = GetCount();
            for (int i = 0; i < itemIds.Length; i++)
            {
                ItemData gItem = Server.GetItemGamedata(itemIds[i]);
                //Check if item id exists and fill up til maxstack 
                int quantityCount = (int)(quantity != null ? quantity[i] : 1);
                for (int j = 0; j < endOfListIndex; j++)
                {
                    InventoryItem item = list[j];
                    Debug.Assert(item != null, "Item slot was null!!!");
                    if (item.itemId == itemIds[i] && item.quality == (quality != null ? quality[i] : 1) && item.quantity < gItem.maxStack)
                    {
                        quantityCount -= (gItem.maxStack - item.quantity);
                        if (quantityCount <= 0)
                            break;
                    }
                }
                //New items that spilled over creating new stacks
                while (quantityCount > 0)
                {
                    quantityCount -= gItem.maxStack;
                    tempInvSize++;
                }
                //If the new stacks push us over capacity, can't add these items
                if (tempInvSize > itemPackageCapacity)
                    return false;
            }
            return true;
        }
        public void MarkDirty(InventoryItem item)
        {
            if (item.itemPackage != itemPackageCode || list[item.slot] == null)
                return;
            isDirty[item.slot] = true;
        }
        public void MarkDirty(ushort slot)
        {
            isDirty[slot] = true;
        }
        public InventoryItem[] GetRawList()
        {
            return list;
        }
        public void ChangeDurability(uint slot, uint durabilityChange)
        {
            isDirty[slot] = true;
        }
        public void ChangeSpiritBind(uint slot, uint spiritBindChange)
        {
            isDirty[slot] = true;
        }
        public void ChangeMateria(uint slot, byte materiaSlot, byte materiaId)
        {
            isDirty[slot] = true;
        }
        #endregion
        #region Packet Functions
        public void SendFullPackage(Player player)
        {
            player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.Id, itemPackageCapacity, itemPackageCode));
            SendItemPackets(player, 0);
            player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.Id));            
        }
        public void SendUpdate()
        {
            if (owner is Player && !holdingUpdates)
            {
                SendUpdate((Player)owner);
            }
        }
        public void SendUpdate(Player player)
        {
            List items = new List();
            List slotsToRemove = new List();
            for (int i = 0; i < list.Length; i++)
            {
                if (i == endOfListIndex)
                    break;
                if (isDirty[i])
                    items.Add(list[i]);
            }
            for (int i = endOfListIndex; i < list.Length; i++)
            {
                if (isDirty[i])
                    slotsToRemove.Add((ushort)i);
            }
            if (!holdingUpdates)
                Array.Clear(isDirty, 0, isDirty.Length);
            
            player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.Id, itemPackageCapacity, itemPackageCode));
            //Send Updated Slots
            SendItemPackets(player, items);
            //Send Remove packets for tail end
            SendItemPackets(player, slotsToRemove);
            player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.Id));
            //If player is updating their normal inventory, we need to send
            //an equip update as well to resync the slots.
            if (player.Equals(owner) && itemPackageCode == NORMAL)
                player.GetEquipment().SendUpdate();
        }
        private void SendItemPackets(Player player, InventoryItem item)
        {
             player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.Id, item));            
        }
        private void SendItemPackets(Player player, List items)
        {
            int currentIndex = 0;
            while (true)
            {
                if (items.Count - currentIndex >= 64)
                    player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.Id, items, ref currentIndex));
                else if (items.Count - currentIndex >= 32)
                    player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.Id, items, ref currentIndex));
                else if (items.Count - currentIndex >= 16)
                    player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.Id, items, ref currentIndex));
                else if (items.Count - currentIndex > 1)
                    player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.Id, items, ref currentIndex));
                else if (items.Count - currentIndex == 1)
                {
                    player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.Id, items[currentIndex]));
                    currentIndex++;
                }
                else
                    break;
            }
        }
        private void SendItemPackets(Player player, int startOffset)
        {
            int currentIndex = startOffset;
            List lst = new List();
            for (int i = 0; i < endOfListIndex; i++)
                lst.Add(list[i]);
            while (true)
            {
                if (endOfListIndex - currentIndex >= 64)
                    player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.Id, lst, ref currentIndex));
                else if (endOfListIndex - currentIndex >= 32)
                    player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.Id, lst, ref currentIndex));
                else if (endOfListIndex - currentIndex >= 16)
                    player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.Id, lst, ref currentIndex));
                else if (endOfListIndex - currentIndex > 1)
                    player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.Id, lst, ref currentIndex));
                else if (endOfListIndex - currentIndex == 1)
                {
                    player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.Id, list[currentIndex]));
                    currentIndex++;
                }
                else
                    break;
            }
        }
        private void SendItemPackets(Player player, ushort index)
        {
            player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.Id, index));
        }
        private void SendItemPackets(Player player, List indexes)
        {
            int currentIndex = 0;
            while (true)
            {
                if (indexes.Count - currentIndex >= 64)
                    player.QueuePacket(InventoryRemoveX64Packet.BuildPacket(owner.Id, indexes, ref currentIndex));
                else if (indexes.Count - currentIndex >= 32)
                    player.QueuePacket(InventoryRemoveX32Packet.BuildPacket(owner.Id, indexes, ref currentIndex));
                else if (indexes.Count - currentIndex >= 16)
                    player.QueuePacket(InventoryRemoveX16Packet.BuildPacket(owner.Id, indexes, ref currentIndex));
                else if (indexes.Count - currentIndex > 1)
                    player.QueuePacket(InventoryRemoveX08Packet.BuildPacket(owner.Id, indexes, ref currentIndex));
                else if (indexes.Count - currentIndex == 1)
                {
                    player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.Id, indexes[currentIndex]));
                    currentIndex++;
                }
                else
                    break;
            }
        }
        #endregion
        #region Automatic Client and DB Updating
        private void DoDatabaseAdd(InventoryItem addedItem)
        {
            if (isTemporary)
                return;
            Database.AddItem(owner, addedItem, itemPackageCode, addedItem.slot);
        }
        private void DoDatabaseQuantity(ulong itemDBId, int quantity)
        {
            if (isTemporary)
                return;
            
            Database.SetQuantity(itemDBId, quantity);
        }
        private void DoDatabaseRemove(ulong itemDBId)
        {
            if (isTemporary)
                return;
            
            Database.RemoveItem(owner, itemDBId);
        }
        
        public void StartSendUpdate()
        {
            holdingUpdates = true;
        }
        public void DoneSendUpdate()
        {
            holdingUpdates = false;
            SendUpdate();
            Array.Clear(isDirty, 0, isDirty.Length);
        }
        #endregion
        #region Inventory Utils
        public bool IsFull()
        {
            return endOfListIndex >= itemPackageCapacity;
        }
        public int GetFreeSlots()
        {
            return itemPackageCapacity - endOfListIndex;
        }
        public bool IsSpaceForAdd(uint itemId, int quantity, int quality)
        {
            int quantityCount = quantity;
            for (int i = 0; i < endOfListIndex; i++)
            {
                InventoryItem item = list[i];
                ItemData gItem = Server.GetItemGamedata(item.itemId);
                if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack)
                {
                    quantityCount -= (gItem.maxStack - item.quantity);
                    if (quantityCount <= 0)
                        break;
                }
            }
            
            return quantityCount <= 0 || (quantityCount > 0 && !IsFull());
        }
        public bool HasItem(uint itemId)
        {
            return HasItem(itemId, 1);
        }
        public bool HasItem(uint itemId, int minQuantity)
        {
            return HasItem(itemId, minQuantity, 1);
        }
        public bool HasItem(uint itemId, int minQuantity, int quality)
        {
            int count = 0;
            for (int i = endOfListIndex - 1; i >= 0; i--)
            {
                InventoryItem item = list[i];
                Debug.Assert(item != null, "Item slot was null!!!");
                if (item.itemId == itemId && item.quality == quality)
                    count += item.quantity;
                if (count >= minQuantity)
                    return true;
            }
            
            return false;
        }
        public int GetNextEmptySlot()
        {
            return endOfListIndex;
        }
        private void DoRealign()
        {
            List positionUpdate = new List();
            int lastNullSlot = -1;
            for (int i = 0; i < endOfListIndex; i++)
            {
                if (list[i] == null && lastNullSlot == -1)
                {
                    lastNullSlot = i;                    
                    continue;
                }
                else if (list[i] != null && lastNullSlot != -1)
                {
                    list[lastNullSlot] = list[i];
                    if (list[lastNullSlot].GetOfferedTo() != null)
                    {
                        list[lastNullSlot].UpdateOfferedSlot((ushort)(list[lastNullSlot].slot - lastNullSlot));                        
                    }
                    list[lastNullSlot].slot = (ushort)lastNullSlot;
                    positionUpdate.Add(list[lastNullSlot]);
                    list[i] = null;
                    isDirty[lastNullSlot] = true;
                    isDirty[i] = true;
                    lastNullSlot++;
                }
            }
            if (lastNullSlot != -1)
                endOfListIndex = lastNullSlot;
            Database.UpdateItemPositions(positionUpdate);
        }
        #endregion
            
        public int GetCount()
        {
            return endOfListIndex;
        }
        public override string ToString()
        {
            string packageName;
            switch (itemPackageCode)
            {
                case NORMAL:
                    packageName = "Inventory";
                    break;
                case LOOT:
                    packageName = "Loot";
                    break;
                case MELDREQUEST:
                    packageName = "Meld Request";
                    break;
                case BAZAAR:
                    packageName = "Bazaar";
                    break;
                case CURRENCY_CRYSTALS:
                    packageName = "Currency";
                    break;
                case KEYITEMS:
                    packageName = "KeyItems";
                    break;
                case EQUIPMENT:
                    packageName = "Equipment";
                    break;
                case TRADE:
                    packageName = "Trade";
                    break;
                case EQUIPMENT_OTHERPLAYER:
                    packageName = "CheckEquip";
                    break;
                default:
                    packageName = "Unknown";
                    break;
            }
            return string.Format("{0} Package", packageName);
        }
    }
}