#include <extra2d/graphics/backends/backend_factory.h>
#include <extra2d/utils/logger.h>

namespace extra2d {
namespace graphics {

std::unordered_map<std::string, BackendFactory::BackendEntry>& BackendFactory::registry() {
    static std::unordered_map<std::string, BackendEntry> reg;
    return reg;
}

void BackendFactory::reg(const std::string& name, BackendFn backend, 
                         const std::vector<std::string>& windowBackends) {
    registry()[name] = {backend, windowBackends};
    E2D_LOG_DEBUG("Registered graphics backend: {} (window backends: {})", 
                  name, windowBackends.size());
}

UniquePtr<RenderBackend> BackendFactory::createBackend(const std::string& name) {
    auto& reg = registry();
    auto it = reg.find(name);
    if (it != reg.end() && it->second.createFn) {
        E2D_LOG_INFO("Creating graphics backend: {}", name);
        return it->second.createFn();
    }
    E2D_LOG_ERROR("Graphics backend '{}' not found", name);
    return nullptr;
}

UniquePtr<RenderBackend> BackendFactory::createDefaultBackend() {
    std::string recommended = getRecommendedBackend();
    if (recommended.empty()) {
        E2D_LOG_ERROR("No graphics backend available");
        return nullptr;
    }
    return createBackend(recommended);
}

UniquePtr<RenderBackend> BackendFactory::createBackendForWindow(const std::string& windowBackend) {
    std::string recommended = getRecommendedBackendForWindow(windowBackend);
    if (recommended.empty()) {
        E2D_LOG_ERROR("No compatible graphics backend for window backend: {}", windowBackend);
        return nullptr;
    }
    return createBackend(recommended);
}

std::vector<std::string> BackendFactory::backends() {
    std::vector<std::string> result;
    for (const auto& pair : registry()) {
        result.push_back(pair.first);
    }
    return result;
}

bool BackendFactory::has(const std::string& name) {
    return registry().find(name) != registry().end();
}

std::string BackendFactory::getRecommendedBackend() {
    auto& reg = registry();
    
    static const std::vector<std::string> priority = {
        "vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"
    };
    
    for (const auto& name : priority) {
        if (reg.find(name) != reg.end()) {
            return name;
        }
    }
    
    if (!reg.empty()) {
        return reg.begin()->first;
    }
    
    E2D_LOG_WARN("No graphics backend registered");
    return "";
}

std::string BackendFactory::getRecommendedBackendForWindow(const std::string& windowBackend) {
    auto& reg = registry();
    
    static const std::vector<std::string> priority = {
        "vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"
    };
    
    for (const auto& name : priority) {
        auto it = reg.find(name);
        if (it != reg.end() && isCompatible(name, windowBackend)) {
            return name;
        }
    }
    
    for (const auto& pair : reg) {
        if (isCompatible(pair.first, windowBackend)) {
            return pair.first;
        }
    }
    
    E2D_LOG_WARN("No compatible graphics backend for window backend: {}", windowBackend);
    return "";
}

bool BackendFactory::isCompatible(const std::string& graphicsBackend, const std::string& windowBackend) {
    auto& reg = registry();
    auto it = reg.find(graphicsBackend);
    if (it == reg.end()) {
        return false;
    }
    
    const auto& windowBackends = it->second.windowBackends;
    if (windowBackends.empty()) {
        return true;
    }
    
    for (const auto& wb : windowBackends) {
        if (wb == windowBackend) {
            return true;
        }
    }
    
    return false;
}

std::vector<std::string> BackendFactory::getSupportedWindowBackends(const std::string& graphicsBackend) {
    auto& reg = registry();
    auto it = reg.find(graphicsBackend);
    if (it != reg.end()) {
        return it->second.windowBackends;
    }
    return {};
}

} // namespace graphics
} // namespace extra2d
