#include "backend.hpp"

#include "vulkan-context.hpp"

#include <print>

#include "imgui_impl_glfw.h"

namespace {
inline void check_vk_result(VkResult err) {
    if (err == 0) {
        return;
    }
    std::println(stderr, "[vulkan] Error: VkResult = {}", static_cast<int>(err));
    if (err < 0) {
        std::abort();
    }
}
} // namespace

bool backend_init() {
    return glfwInit() == GLFW_TRUE;
}

float backend_get_main_scale() {
    return ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor());
}

bool backend_supports_vulkan() {
    return glfwVulkanSupported() == GLFW_TRUE;
}

bool backend_create_window(int32_t width, int32_t height, const std::string& title, BackendWindow** window) {
    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    *window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
    return *window != nullptr;
}

std::vector<const char*> backend_get_instance_extensions() {
    uint32_t count = 0;
    auto* extensions_ptr = glfwGetRequiredInstanceExtensions(&count);
    auto extensions_vec = std::vector<const char*>(count);
    for (uint32_t i = 0; i < count; i++) {
        extensions_vec[i] = extensions_ptr[i];
    }
    return extensions_vec;
}

bool backend_create_window_surface(VulkanContext& context, BackendWindow* window, VkSurfaceKHR* surface) {
    check_vk_result(glfwCreateWindowSurface(context.instance, window, context.allocator, surface));
    return true;
}

void backend_get_framebuffer_size(BackendWindow* window, int32_t* width, int32_t* height) {
    glfwGetFramebufferSize(window, width, height);
}

void backend_show_window([[maybe_unused]] BackendWindow* window) {}

bool backend_init_for_vulkan(BackendWindow* window, bool install_callbacks) {
    return ImGui_ImplGlfw_InitForVulkan(window, install_callbacks);
}

bool backend_poll_events(BackendWindow* window) {
    bool should_exit = glfwWindowShouldClose(window) == GLFW_TRUE;
    if (!should_exit) {
        glfwPollEvents();
    }
    return should_exit;
}

bool backend_window_is_minimized(BackendWindow* window) {
    return glfwGetWindowAttrib(window, GLFW_ICONIFIED) == GLFW_TRUE;
}

void backend_sleep(int32_t milliseconds) {
    ImGui_ImplGlfw_Sleep(milliseconds);
}

void backend_new_frame() {
    ImGui_ImplGlfw_NewFrame();
}

void backend_shutdown() {
    ImGui_ImplGlfw_Shutdown();
}

void backend_destroy_window(BackendWindow* window) {
    glfwDestroyWindow(window);
}

void backend_terminate() {
    glfwTerminate();
}
