#ifndef MWGUI_ITEM_MODEL_H
#define MWGUI_ITEM_MODEL_H

#include <memory>

#include "../mwworld/ptr.hpp"

namespace MWGui
{

    class ItemModel;

    /// @brief A single item stack managed by an item model
    struct ItemStack
    {
        ItemStack(const MWWorld::Ptr& base, ItemModel* creator, size_t count);
        ItemStack();
        ///< like operator==, only without checking mType

        enum Type
        {
            Type_Barter,
            Type_Equipped,
            Type_Normal
        };
        Type mType;

        enum Flags
        {
            Flag_Enchanted = (1 << 0),
            Flag_Bound = (1 << 1)
        };
        int mFlags;

        ItemModel* mCreator;
        size_t mCount;
        MWWorld::Ptr mBase;
    };

    bool operator==(const ItemStack& left, const ItemStack& right);

    /// @brief The base class that all item models should derive from.
    class ItemModel
    {
    public:
        ItemModel();
        virtual ~ItemModel() {}

        typedef int ModelIndex; // -1 means invalid index

        /// Throws for invalid index or out of range index
        virtual ItemStack getItem(ModelIndex index) = 0;

        /// The number of items in the model, this specifies the range of indices you can pass to
        /// the getItem function (but this range is only valid until the next call to update())
        virtual size_t getItemCount() = 0;

        /// Returns an invalid index if the item was not found
        virtual ModelIndex getIndex(const ItemStack& item) = 0;

        /// Rebuild the item model, this will invalidate existing model indices
        virtual void update() = 0;

        /// Move items from this model to \a otherModel.
        /// @note Derived implementations may return an empty Ptr if the move was unsuccessful.
        virtual MWWorld::Ptr moveItem(
            const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip = true);

        virtual MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0;
        virtual MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0;
        virtual void removeItem(const ItemStack& item, size_t count) = 0;

        /// Is the player allowed to use items from this item model? (default true)
        virtual bool allowedToUseItems() const;
        virtual void onClose() {}
        virtual bool onDropItem(const MWWorld::Ptr& item, int count);
        virtual bool onTakeItem(const MWWorld::Ptr& item, int count);

        virtual bool usesContainer(const MWWorld::Ptr& container) = 0;

    private:
        ItemModel(const ItemModel&);
        ItemModel& operator=(const ItemModel&);
    };

    /// @brief A proxy item model can be used to filter or rearrange items from a source model (or even add new items to
    /// it). The neat thing is that this does not actually alter the source model.
    class ProxyItemModel : public ItemModel
    {
    public:
        ProxyItemModel() = default;
        virtual ~ProxyItemModel() = default;

        bool allowedToUseItems() const override;

        void onClose() override;
        bool onDropItem(const MWWorld::Ptr& item, int count) override;
        bool onTakeItem(const MWWorld::Ptr& item, int count) override;

        MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override;
        MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override;
        void removeItem(const ItemStack& item, size_t count) override;
        ModelIndex getIndex(const ItemStack& item) override;

        /// @note Takes ownership of the passed pointer.
        void setSourceModel(std::unique_ptr<ItemModel> sourceModel);

        ModelIndex mapToSource(ModelIndex index);
        ModelIndex mapFromSource(ModelIndex index);

        bool usesContainer(const MWWorld::Ptr& container) override;

    protected:
        std::unique_ptr<ItemModel> mSourceModel;
    };

}

#endif
