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

namespace Game::State {
    Object::Entity* GameState::getAtIndex(size_t at) {
        std::lock_guard<std::mutex> lock(mMutex); // Lock the mutex for thread safety
        try {
            return mEntities.at(at).get();
        } catch (const std::out_of_range& e) {
            WARN("Tried to access entity from GameState out of range!");
            return nullptr;
        }
    }
    
    void 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
            mEntities.push_back(std::move(entity));
            addedEntity = mEntities.back().get();
        }

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

    bool GameState::removeEntity(const std::string& name) {
        std::lock_guard<std::mutex> lock(mMutex); // Lock the mutex for thread safety
        for (size_t i = 0; i < mEntities.size(); i++) {
            if (mEntities[i]->getName() == name) {
                mEntities[i]->destroyed();
                mEntities.erase(mEntities.begin() + i);
                return true;
            }
        }

        return false;
    }

    void GameState::sort() {
        std::lock_guard<std::mutex> lock(mMutex); // Lock the mutex for thread safety
        std::sort(mEntities.begin(), mEntities.end(), [](const std::unique_ptr<Object::Entity>& a, const std::unique_ptr<Object::Entity>& b) {
            return a->getZIndex() < b->getZIndex();
        });
    }

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

    Object::Entity* GameState::getEntityByName(const std::string& name) {
        std::lock_guard<std::mutex> lock(mMutex); // Lock the mutex for thread safety
        for (const auto& entity : mEntities) {
            if (entity->getName() == name) {
                return entity.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(mEntities.size());
        for (const auto& entity : mEntities) {
            snapshot.push_back(entity.get());
        }

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

        return snapshot;
    }
}