const std = @import("std");
const builtin = @import("builtin");

const Core = @import("Core.zig");

const vk = @import("vk");

const I = &Core.I;
const D = &Core.D;
const pdev = &Core.pdev;
const surface = &Core.surface;

pub fn SwapChain(Flight: type, N: u3) type {
    return struct {
        handle: vk.SwapchainKHR = .null_handle,
        cinfo: vk.SwapchainCreateInfoKHR,

        // todo populate and use this.
        flights: std.MultiArrayList(struct{
            fence: vk.Fence,
            acquired: vk.Semaphore,
            complete: vk.Semaphore,
        }),

        pub fn init(alloc: std.mem.Allocator) !SwapChain {
            var arena = std.heap.ArenaAllocator.init(alloc);
            defer arena.deinit();
            const ialloc = arena.allocator();

            const caps = try I.getPhysicalDeviceSurfaceCapabilitiesKHR(
                pdev,
                surface,
            );

            const formats = try I.getPhysicalDeviceSurfaceFormatsAllocKHR(
                pdev,
                surface,
                ialloc,
            );
            const format: vk.SurfaceFormatKHR = formats[0];
            const mode: vk.PresentModeKHR = .fifo_khr;

            return .{
                .cinfo = vk.SwapchainCreateInfoKHR{
                    .surface = surface,
                    .min_image_count = std.math.clamp(
                        @min(3, caps.min_image_count + 1),
                        caps.min_image_count,
                        if (caps.max_image_count > 0) caps.max_image_count else 127,
                    ),
                    .image_format = format.format,
                    .image_color_space = format.color_space,
                    .image_extent = undefined, // set in rebuild
                .image_array_layers = 1,
                    .image_usage = .{ .color_attachment_bit = true },
                    .image_sharing_mode = .exclusive,
                    .pre_transform = .{ .identity_bit_khr = true },
                    .composite_alpha = .{ .opaque_bit_khr = true },
                    .present_mode = mode,
                    .clipped = vk.TRUE,
                    .old_swapchain = .null_handle,
                },
            };
        }

        fn build(self: *SwapChain) !void {
            if (self.handle != .null_handle) return;

            const caps = try I.getPhysicalDeviceSurfaceCapabilitiesKHR(
                pdev,
                surface,
            );
            self.cinfo.image_extent = caps.current_extent;

            self.handle = try D.createSwapchainKHR(&self.cinfo, null);
            D.destroySwapchainKHR(self.cinfo.old_swapchain, null);
            self.cinfo.old_swapchain = self.handle;
        }

        pub fn acquire(self: *SwapChain) !void {
            D.acquireNextImageKHR(self.handle, std.math.maxInt(u64), semaphore: Semaphore, fence: Fence)
        }

        pub fn invalidate(self: *SwapChain) void {
            self.handle = .null_handle;
        }

        pub fn deinit(self: SwapChain) void {
            D.destroySwapchainKHR(self.cinfo.old_swapchain, null);
        }
    };
}
