#pragma once

#include <vector>
#include <memory>
#include <functional>
#include <utils.hpp>
#include <object/entity.hpp>
#include <mutex>

namespace Game::State {
    class GameState {
        public:
            static GameState& getInstance() { static GameState instance; return instance; }

            // Execute work while holding the GameState mutex to keep access thread-safe.
            template<typename Fn>
            void withEntitiesLocked(Fn&& fn) {
                std::scoped_lock lock(mMutex);
                fn(mEntities);
            }

            void sort(); // Sort entities by zIndex for correct rendering order
            void wipe();
            Object::Entity* getEntityByName(const std::string& name); // Get an entity by name, returns nullptr if no entity with the name exists
            std::vector<Object::Entity*> getEntitiesSnapshot(bool sortByZIndex = false); // Get a stable snapshot of entity pointers for iteration outside the lock

            // Update entity at index, by REFERENCE.
            Object::Entity* getAtIndex(size_t at);
            // Add an entity to the gamestate.
            void addEntity(std::unique_ptr<Object::Entity> entity);
            bool removeEntity(const std::string& name);

        private:
            mutable std::mutex mMutex; // Shared mutex for thread safety
            std::vector<std::unique_ptr<Object::Entity>> mEntities;
    };
}