#pragma once

#include "game_object.hpp"
#include "utils/ptr.hpp"

#include <memory>       // unique_ptr
#include <utility>      // move
#include <algorithm>    // remove_if, for_each
#include <vector>
#include <string_view>


namespace frog
{


class engine;


inline static auto all = [](const auto&){ return true; };

inline static auto by_tag(std::string_view tag)
{
    return [=](const auto& obj){ return obj.tag() == tag; };
}


class scene
{
    std::vector<ptr<game_object>> objects;

    template <typename Self, typename Func, typename Predicate>
    static void for_each_impl(Self& self, Func func, Predicate pred)
    {
        std::for_each(self.objects.begin(), self.objects.end(),
            [=](auto& ptr)
            {
                if (ptr && pred(*ptr))
                    func(*ptr);
            });
    }

public:

    void add(ptr<game_object> obj)
    {
        objects.push_back(std::move(obj));
    }

    void init(engine&);

    void stable_update(engine&);

    void frame_update(engine&);

    void cleanup(engine&);

    /*
     * Functions to iterate through objects
     */

    template<typename Func>
    void for_each_object(Func func) const { for_each_impl(*this, func, all); }

    template<typename Func>
    void for_each_object(Func func)       { for_each_impl(*this, func, all); }

    template<typename Predicate, typename Func>
    void for_each_object(Predicate pred, Func func) const
    {
        for_each_impl(*this, func, pred);
    }

    template<typename Predicate, typename Func>
    void for_each_object(Predicate pred, Func func)
    {
        for_each_impl(*this, func, pred);
    }
};


} // namespace frog
