#ifndef STATEFUL_ITEM_HPP_
#define STATEFUL_ITEM_HPP_

#include <Item/Item.hpp>
#include <ItemStack/ItemStack.hpp>
#include <Serialize/Serialize.hpp>

/**
 * @brief Represents an item with state.
 * 
 * @tparam TState The state type.
 */
template<typename TState>
class StatefulItem : public Item
{
private:
    TState m_dummyState;

protected:
    /**
     * @brief Get a reference to the state on the item stack.
     * 
     * @param stack The item stack.
     * @return Reference to the state.
     */
    TState& state(ItemStack &stack)
    {
        TState *stackState = reinterpret_cast<TState*>(stack.getMutableStatePtr());
        if (stackState == nullptr)
        {
            stackState = new TState();
            stack.setStatePtr(reinterpret_cast<void*>(stackState));
        }

        return *stackState;
    }

    const TState& state(const ItemStack &stack) const
    {
        const TState *stackState = reinterpret_cast<const TState*>(stack.getConstStatePtr());
        if (stackState == nullptr)
        {
            return m_dummyState;
        }
        else
        {
            return *stackState;
        }
    }

public:
    StatefulItem(std::string name) : Item(name), m_dummyState()
    {
        setMaxStackSize(1);
    }

    virtual void serializeState(std::vector<uint8_t> &outBytes, const ItemStack &stack) override
    {
        serializeInto(outBytes, state(stack));
    }

    virtual size_t deserializeState(const uint8_t *data, size_t size, ItemStack &stack) override
    {
        return deserialize(data, size, state(stack));
    }

    virtual void copyState(ItemStack &dest, const ItemStack &src) override
    {
        const TState *srcState = reinterpret_cast<const TState*>(src.getConstStatePtr());
        delete reinterpret_cast<TState*>(dest.getMutableStatePtr());

        dest.setStatePtr(srcState == nullptr ? nullptr : new TState(*srcState));
    }

    virtual void destroyState(void *state) override
    {
        delete reinterpret_cast<TState*>(state);
    }

    virtual bool isStateEqual(const ItemStack &a, const ItemStack &b) override
    {
        return state(a) == state(b);
    }
};

#endif