//! GLFW Adaptor

// todo restructure to handle multiple windows

const std = @import("std");

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

pub const c = @cImport({
    @cDefine("GLFW_INCLUDE_NONE", {});
    @cInclude("GLFW/glfw3.h");
});

pub const Bus = @import("Bus.zig");

pub const Config = struct {
    title: [*:0]const u8 = "Hello World",
    width: u32 = 1280,
    height: u32 = 720,
    x11_class_name: [*:0]const u8 = "floating_window",
    x11_instance_name: [*:0]const u8 = "floating_window",
    unfocused_wait: f32 = 1.0 / 20.0,
};
const config: Config = nu.config.window;

pub fn driver() nu.Driver {
    return nu.Driver{
        .module = .{
            .name = "Window",
            .dependencies = &.{}, // todo bus
            .setup = setup,
            .teardown = teardown,
        },
        .next = next,
    };
}

pub var handle: *c.GLFWwindow = undefined;

pub fn setup(_: std.mem.Allocator) !void {
    if (c.glfwInit() != c.GLFW_TRUE) std.debug.panic("GLFW Init Failed", .{});

    if (c.glfwVulkanSupported() != c.GLFW_TRUE)
        std.debug.panic("GLFW Vulkan not supported", .{});

    c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
    c.glfwWindowHintString(c.GLFW_X11_CLASS_NAME, config.x11_class_name);
    c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, config.x11_instance_name);
    handle = c.glfwCreateWindow(
        @intCast(config.width),
        @intCast(config.height),
        config.title,
        null,
        null,
    ) orelse std.debug.panic("GLFW Create Window Failed", .{});

    _ = c.glfwSetFramebufferSizeCallback(handle, &resize_callback);

    // bus.connect(handle);
    // errdefer bus.disconnect(handle);
}

pub fn teardown() void {
    c.glfwDestroyWindow(handle);
    c.glfwTerminate();
}

pub fn next() bool {
    if (c.glfwWindowShouldClose(handle) == c.GLFW_TRUE)
        return false;

    if (c.glfwGetWindowAttrib(handle, c.GLFW_FOCUSED) == c.GLFW_TRUE) {
        c.glfwPollEvents();
    } else {
        c.glfwWaitEventsTimeout(config.unfocused_wait);
    }

    return true;
}

var _resize_callbacks: std.BoundedArray(*const fn (u32, u32) void, 16) = .{};

fn resize_callback(_: ?*c.GLFWwindow, w: c_int, h: c_int) callconv(.C) void {
    for (_resize_callbacks.slice()) |cb| {
        cb(@intCast(w), @intCast(h));
    }
}

pub fn add_resize_callback(cb: *const fn (u32, u32) void) void {
    _resize_callbacks.appendAssumeCapacity(cb);
}

pub fn set_title(title: [:0]const u8) void {
    c.glfwSetWindowTitle(handle, title);
}

pub fn size() std.meta.Tuple(&[_]type{ u32, u32 }) {
    var w: c_int = undefined;
    var h: c_int = undefined;
    c.glfwGetFramebufferSize(handle, &w, &h);
    return .{ @intCast(w), @intCast(h) };
}
