#ifndef CAPSULE_HPP_
#define CAPSULE_HPP_

#include <Item/IEquipment.hpp>
#include <Item/StatefulItem/StatefulItem.hpp>
#include <Serialize/Serialize.hpp>
#include <Material/Material.hpp>
#include <World/IWorldActor.hpp>
#include <World/LocalWorld/LocalWorld.hpp>
#include <Item/IFluidContainingItem.hpp>

#include <string>

struct CapsuleState
{
    std::string fluidName;
    int amount;

    inline bool operator==(const CapsuleState &other) const
    {
        return fluidName == other.fluidName && other.amount == amount;
    }
};

/**
 * @brief Represents the tier of capsule (tin, lead, etc).
 */
enum class CapsuleTier
{
    Tin,
    Lead,
};

/**
 * @brief Represents the size of a capsule.
 */
enum class CapsuleSize
{
    Small,
    Large,
};

/**
 * @brief Represents a type of capsule.
 */
class Capsule final :
    public StatefulItem<CapsuleState>,
    public IFluidContainingItem,
    public IEquipment
{
private:
    CapsuleSize m_size;
    CapsuleTier m_tier;
    Material &m_icon;
    
    int getCapacity();
    bool canStoreFluid(std::string name);
    void consumeFromBlock(LocalWorld &world, IWorldActor &actor, ItemStack &stack, glm::ivec3 pos);
    void spillOntoBlock(LocalWorld &world, ItemStack &stack, glm::ivec3 pos);

public:
    static const int smallCapsuleCapacity = 5000;
    static const int largeCapsuleCapacity = 20000;

    Capsule(std::string name, CapsuleSize size, CapsuleTier tier, Material &icon);

    virtual int getFluidAmount(const ItemStack &stack) override;
    virtual void draw(GraphicsContext &context, int x, int y, const ItemStack &stack) override;
    virtual std::string getDisplayName(const ItemStack &stack) override;
    virtual void draw3D(GraphicsContext &context, WorldDisplay &worldDisplay, glm::vec3 pos, ItemStack &stack) override;
    virtual void drawFlat(GraphicsContext &context, WorldDisplay &worldDisplay, glm::mat4 matrix, ItemStack &stack) override;
    virtual void getExtraToolTipLines(const ItemStack &stack, ToolTip &tip) override;
    virtual void onUse(LocalWorld &world, IWorldActor &actor, ItemStack &stack, glm::ivec3 pos, Side side) override;
    virtual bool canMerge(const ItemStack &a, const ItemStack &b) override;
    virtual std::string getFluidName(const ItemStack &stack) override;
    virtual int pullFluid(ItemStack &stack, std::string fluidName, int maxToPull) override;
    virtual int pushFluid(ItemStack &stack, std::string fluidName, int amount) override;
    virtual int pullFluidForEquipment(ItemStack &stack, std::string fluidName, int maxAmount) override;
};

template<>
struct SerialCodec<CapsuleState>
{
    static void encode(std::vector<uint8_t> &outBytes, const CapsuleState &state);
    static size_t decode(const uint8_t *data, size_t size, CapsuleState &state);
};

#endif