#include <state/gamestate.hpp>
#include <algorithm>
#include <iostream>

namespace Game::State {
    Object::Entity* GameState::addEntity(std::unique_ptr<Object::Entity> entity) {
        Object::Entity* addedEntity = nullptr;
        {
            std::lock_guard<std::mutex> lock(mMutex); // Lock the mutex for thread safety
            const std::string name = entity->getName();
            auto [it, inserted] = mEntityMap.emplace(name, std::move(entity));
            addedEntity = it->second.get();
        }

        addedEntity->start(); // Initialize the entity after insertion without holding the GameState lock.
        LOG("Added entity '" << addedEntity->getName() << "' to GameState");
        return addedEntity;
    }

    bool GameState::removeEntity(const std::string& name) {
        std::lock_guard<std::mutex> lock(mMutex); // Lock the mutex for thread safety
        auto it = mEntityMap.find(name);
        if (it != mEntityMap.end()) {
            if (it->second) {
                it->second->destroyed();
            }
            mEntityMap.erase(it);
            return true;
        }

        return false;
    }

    void GameState::queueEntityRemoval(const std::string& name) {
        std::lock_guard<std::mutex> lock(mMutex);
        if (std::find(mPendingRemovals.begin(), mPendingRemovals.end(), name) == mPendingRemovals.end()) {
            mPendingRemovals.push_back(name);
        }
    }

    void GameState::processPendingRemovals() {
        std::vector<std::string> pendingRemovals;
        {
            std::lock_guard<std::mutex> lock(mMutex);
            pendingRemovals = std::move(mPendingRemovals);
        }

        for (const auto& name : pendingRemovals) {
            removeEntity(name);
        }
    }

    void GameState::wipe() {
        std::lock_guard<std::mutex> lock(mMutex);
        for (const auto& pair : mEntityMap) {
            if (pair.second) {
                pair.second->destroyed();
            }
        }
        mEntityMap.clear();
        mPendingRemovals.clear();
    }

    Object::Entity* GameState::getEntityByName(const std::string& name) {
        std::lock_guard<std::mutex> lock(mMutex); // Lock the mutex for thread safety
        auto it = mEntityMap.find(name);
        if (it != mEntityMap.end()) {
            return it->second.get();
        }
        return nullptr; // Return nullptr if no entity with the name exists
    }

    std::vector<Object::Entity*> GameState::getEntitiesSnapshot(bool sortByZIndex) {
        std::vector<Object::Entity*> snapshot;

        std::lock_guard<std::mutex> lock(mMutex);
        snapshot.reserve(mEntityMap.size());
        for (const auto& entity : mEntityMap) {
            snapshot.push_back(entity.second.get());
        }

        if (sortByZIndex) {
            std::sort(snapshot.begin(), snapshot.end(), [](Object::Entity* a, Object::Entity* b) {
                return a->getZIndex() < b->getZIndex();
            });
        }

        return snapshot;
    }
}