#include "app/AssetLoader.h"
#include <SDL3_image/SDL_image.h>
#include <algorithm>

AssetLoader::AssetLoader() = default;

AssetLoader::~AssetLoader() {
    shutdown();
}

void AssetLoader::init(SDL_Renderer* renderer) {
    m_renderer = renderer;
}

void AssetLoader::shutdown() {
    // Destroy textures
    {
        std::lock_guard<std::mutex> lk(m_texturesMutex);
        for (auto &p : m_textures) {
            if (p.second) SDL_DestroyTexture(p.second);
        }
        m_textures.clear();
    }

    // Clear queue and errors
    {
        std::lock_guard<std::mutex> lk(m_queueMutex);
        m_queue.clear();
    }
    {
        std::lock_guard<std::mutex> lk(m_errorsMutex);
        m_errors.clear();
    }

    m_totalTasks = 0;
    m_loadedTasks = 0;
    m_renderer = nullptr;
}

void AssetLoader::setBasePath(const std::string& basePath) {
    m_basePath = basePath;
}

void AssetLoader::queueTexture(const std::string& path) {
    {
        std::lock_guard<std::mutex> lk(m_queueMutex);
        m_queue.push_back(path);
    }
    m_totalTasks.fetch_add(1, std::memory_order_relaxed);
}

bool AssetLoader::performStep() {
    std::string path;
    {
        std::lock_guard<std::mutex> lk(m_queueMutex);
        if (m_queue.empty()) return true;
        path = m_queue.front();
        m_queue.erase(m_queue.begin());
    }

    {
        std::lock_guard<std::mutex> lk(m_currentLoadingMutex);
        m_currentLoading = path;
    }

    std::string fullPath = m_basePath.empty() ? path : (m_basePath + "/" + path);

    SDL_Surface* surf = IMG_Load(fullPath.c_str());
    if (!surf) {
        std::lock_guard<std::mutex> lk(m_errorsMutex);
        m_errors.push_back(std::string("IMG_Load failed: ") + fullPath + " -> " + SDL_GetError());
    } else {
        SDL_Texture* tex = SDL_CreateTextureFromSurface(m_renderer, surf);
        SDL_DestroySurface(surf);
        if (!tex) {
            std::lock_guard<std::mutex> lk(m_errorsMutex);
            m_errors.push_back(std::string("CreateTexture failed: ") + fullPath);
        } else {
            std::lock_guard<std::mutex> lk(m_texturesMutex);
            auto& slot = m_textures[path];
            if (slot && slot != tex) {
                SDL_DestroyTexture(slot);
            }
            slot = tex;
        }
    }

    m_loadedTasks.fetch_add(1, std::memory_order_relaxed);

    {
        std::lock_guard<std::mutex> lk(m_currentLoadingMutex);
        m_currentLoading.clear();
    }

    // Return true when no more queued tasks
    {
        std::lock_guard<std::mutex> lk(m_queueMutex);
        return m_queue.empty();
    }
}

void AssetLoader::adoptTexture(const std::string& path, SDL_Texture* texture) {
    if (!texture) {
        return;
    }

    std::lock_guard<std::mutex> lk(m_texturesMutex);
    auto& slot = m_textures[path];
    if (slot && slot != texture) {
        SDL_DestroyTexture(slot);
    }
    slot = texture;
}

float AssetLoader::getProgress() const {
    int total = m_totalTasks.load(std::memory_order_relaxed);
    if (total <= 0) return 1.0f;
    int loaded = m_loadedTasks.load(std::memory_order_relaxed);
    return static_cast<float>(loaded) / static_cast<float>(total);
}

std::vector<std::string> AssetLoader::getAndClearErrors() {
    std::lock_guard<std::mutex> lk(m_errorsMutex);
    std::vector<std::string> out = m_errors;
    m_errors.clear();
    return out;
}

SDL_Texture* AssetLoader::getTexture(const std::string& path) const {
    std::lock_guard<std::mutex> lk(m_texturesMutex);
    auto it = m_textures.find(path);
    if (it == m_textures.end()) return nullptr;
    return it->second;
}

std::string AssetLoader::getCurrentLoading() const {
    std::lock_guard<std::mutex> lk(m_currentLoadingMutex);
    return m_currentLoading;
}
