#pragma once

#include <extra2d/core/types.h>
#include <extra2d/graphics/core/render_backend.h>

#include <functional>
#include <string>
#include <unordered_map>
#include <vector>

namespace extra2d {
namespace graphics {

/**
 * @brief 图形后端工厂
 * 用于注册和创建图形渲染后端
 */
class BackendFactory {
public:
  using BackendFn = std::function<UniquePtr<RenderBackend>()>;

  /**
   * @brief 注册图形后端
   * @param name 后端名称
   * @param backend 后端创建函数
   * @param windowBackends 支持的窗口后端名称列表
   */
  static void reg(const std::string &name, BackendFn backend,
                  const std::vector<std::string> &windowBackends = {});

  /**
   * @brief 创建渲染后端实例
   * @param name 后端名称
   * @return 渲染后端实例，如果后端不存在返回 nullptr
   */
  static UniquePtr<RenderBackend> createBackend(const std::string &name);

  /**
   * @brief 创建默认渲染后端
   * @return 默认渲染后端实例
   */
  static UniquePtr<RenderBackend> createDefaultBackend();

  /**
   * @brief 创建与指定窗口后端兼容的渲染后端
   * @param windowBackend 窗口后端名称
   * @return 兼容的渲染后端实例
   */
  static UniquePtr<RenderBackend>
  createBackendForWindow(const std::string &windowBackend);

  /**
   * @brief 获取所有已注册的后端名称
   */
  static std::vector<std::string> backends();

  /**
   * @brief 检查后端是否存在
   */
  static bool has(const std::string &name);

  /**
   * @brief 获取推荐的后端名称
   * @return 推荐的后端名称
   */
  static std::string getRecommendedBackend();

  /**
   * @brief 获取与指定窗口后端兼容的推荐图形后端
   * @param windowBackend 窗口后端名称
   * @return 推荐的图形后端名称
   */
  static std::string
  getRecommendedBackendForWindow(const std::string &windowBackend);

  /**
   * @brief 检查图形后端是否支持指定窗口后端
   * @param graphicsBackend 图形后端名称
   * @param windowBackend 窗口后端名称
   * @return 支持返回 true
   */
  static bool isCompatible(const std::string &graphicsBackend,
                           const std::string &windowBackend);

  /**
   * @brief 获取图形后端支持的窗口后端列表
   * @param graphicsBackend 图形后端名称
   * @return 支持的窗口后端列表
   */
  static std::vector<std::string>
  getSupportedWindowBackends(const std::string &graphicsBackend);

private:
  struct BackendEntry {
    BackendFn createFn;
    std::vector<std::string> windowBackends;
  };

  static std::unordered_map<std::string, BackendEntry> &registry();
};

/**
 * @brief 图形后端注册宏
 * 在全局作用域使用此宏注册图形后端
 *
 * @example
 * E2D_REG_GRAPHICS_BACKEND(opengl, GLRenderer, {"sdl2", "glfw"})
 */
#define E2D_REG_GRAPHICS_BACKEND(name, RendererClass, windowBackends)          \
  namespace {                                                                  \
  __attribute__((used)) static struct E2D_GRAPHICS_BACKEND_REG_##name {        \
    E2D_GRAPHICS_BACKEND_REG_##name() {                                        \
      ::extra2d::graphics::BackendFactory::reg(                                \
          #name,                                                               \
          []() -> ::extra2d::UniquePtr<::extra2d::RenderBackend> {             \
            return ::extra2d::makeUnique<RendererClass>();                     \
          },                                                                   \
          windowBackends);                                                     \
    }                                                                          \
  } e2d_graphics_backend_reg_##name;                                           \
  }

} // namespace graphics
} // namespace extra2d
