//! all imgui code through this path

const std = @import("std");

const vk = @import("vk");
const nu = @import("../nu.zig");

const Render = @import("Render.zig");
const Window = @import("Window.zig");

const im = @import("cimgui");
pub usingnamespace im;

pub const Config = struct {};
const config = nu.config.imgui;

pub const depends = .{ Render, Window };

pub fn loader_wrapper(procname: [*c]const u8, _: ?*anyopaque) callconv(.C) vk.PfnVoidFunction {
    return Render.ctx.glfwGetInstanceProcAddress(Render.ctx.instance.*, procname);
}

var ctx: *im.ImGuiContext = undefined;
var descriptor_pool: vk.DescriptorPool = undefined;

pub fn setup(_: std.mem.Allocator) !void {
    ctx = im.igCreateContext(null) orelse {
        return error.igCreateContextFailed;
    };
    errdefer im.igDestroyContext(ctx);

    if (!im.impl.ImGui_ImplVulkan_LoadFunctions(loader_wrapper, null)) {
        return error.igVulkanLoadFunctionsFailed;
    }

    if (!im.impl.ImGui_ImplGlfw_InitForVulkan(@ptrCast(Window.handle), true)) {
        return error.igGlfwInitFailed;
    }
    errdefer im.impl.ImGui_ImplGlfw_Shutdown();

    descriptor_pool = try Render.ctx.D.createDescriptorPool(
        &vk.DescriptorPoolCreateInfo{
            .flags = .{ .free_descriptor_set_bit = true },
            .pool_size_count = 1,
            .p_pool_sizes = &.{vk.DescriptorPoolSize{
                .descriptor_count = 32,
                .type = .combined_image_sampler,
            }},
            .max_sets = 32,
        },
        null,
    );
    errdefer Render.ctx.D.destroyDescriptorPool(descriptor_pool, null);

    if (im.impl.ImGui_ImplVulkan_Init(@constCast(&im.impl.ImGui_ImplVulkan_InitInfo{
        .Instance = @ptrFromInt(@intFromEnum(Render.ctx.instance.*)),
        .PhysicalDevice = @ptrFromInt(@intFromEnum(Render.ctx.pdevice.*)),
        .Device = @ptrFromInt(@intFromEnum(Render.ctx.device.*)),
        .QueueFamily = Render.ctx.family.*, // todo
        .Queue = @ptrFromInt(@intFromEnum(Render.ctx.Q.handle)), // todo
        .DescriptorPool = @ptrFromInt(@intFromEnum(descriptor_pool)),
        .RenderPass = null,
        .MinImageCount = 2,
        .ImageCount = @intCast(Render.sc.frames_in_flight),
        .PipelineRenderingCreateInfo = @bitCast(vk.PipelineRenderingCreateInfo{
            .view_mask = 0,
            .depth_attachment_format = .undefined,
            .stencil_attachment_format = .undefined,
            .color_attachment_count = 1,
            .p_color_attachment_formats = &.{Render.sc.cinfo.image_format}, // todo
        }),
        .MSAASamples = 0,
        .PipelineCache = null,
        .Subpass = 0,
        .UseDynamicRendering = true,
        .Allocator = null,
    })) != true) {
        return error.igVulkanInitFailed;
    }
    errdefer im.impl.ImGui_ImplVulkan_Shutdown();

    if (!im.impl.ImGui_ImplVulkan_CreateFontsTexture()) {
        return error.igVulkanFontTextureFailed;
    }
}

pub fn teardown() void {
    Render.ctx.D.deviceWaitIdle() catch |err| std.debug.panic("Device wait failed: {!}", .{err});
    im.impl.ImGui_ImplVulkan_Shutdown();
    Render.ctx.D.destroyDescriptorPool(descriptor_pool, null);
    im.impl.ImGui_ImplGlfw_Shutdown();
    im.igDestroyContext(ctx);
}

pub fn frame() !void {
    im.impl.ImGui_ImplGlfw_NewFrame();
    im.impl.ImGui_ImplVulkan_NewFrame();
    im.igNewFrame();
}

pub fn rpresent(cmd: Render.ctx.CommandBufferProxy) void { // todo
    im.igEndFrame();
    im.igRender();

    im.impl.ImGui_ImplVulkan_RenderDrawData(
        @ptrCast(im.igGetDrawData()),
        @ptrFromInt(@intFromEnum(cmd.handle)),
        null,
    );
}
