#pragma once

#include <memory>
#include <stack>
#include <map>

template<typename Type, class SpriteFactory, class Sprite,
         typename = std::enable_if_t<std::is_enum<Type>::value>>
class SpriteContainer
{
public:
    explicit SpriteContainer(std::initializer_list<Type>&& types,
                             std::unique_ptr<SpriteFactory>&& factory,
                             std::size_t reserve_size = 20) :
        _sprite_factory(std::move(factory)),
        _poll_reserve_size(reserve_size)
    {
        for (const Type& type : types)
            reallocatePoll(type);
    }

    inline std::shared_ptr<Sprite> getSprite(Type type)
    {
        SpritePoll& poll = _sprite_dispatcher.at(type);

        if (poll.empty())
            reallocatePoll(type);

        std::shared_ptr<Sprite> sprite = poll.top();
        poll.pop();

        return sprite;
    }

    inline void resetSprite(const std::shared_ptr<Sprite> &sprite, Type action) noexcept
    {
        _sprite_dispatcher[action].push(sprite);
    }

    ~SpriteContainer(){}

private:
    inline void reallocatePoll(Type sprite_type)
    {
        SpritePoll &poll = _sprite_dispatcher[sprite_type];
        for (std::size_t i = 0; i < _poll_reserve_size; ++i)
        {
            poll.push(_sprite_factory->create(sprite_type));
        }
    }

    using SpritePoll = std::stack<std::shared_ptr<Sprite>>;

    std::map<Type, SpritePoll> _sprite_dispatcher;
    std::unique_ptr<SpriteFactory> _sprite_factory;

    std::size_t _poll_reserve_size;
};