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

const wl = wayland.client.wl;
const zriver = wayland.client.zriver;

dpy: *wl.Display,
seat: ?*wl.Seat,
ctl: ?*zriver.ControlV1,

const Connection = @This();

pub fn init() !Connection {
    const dpy = try wl.Display.connect(null);
    errdefer dpy.disconnect();

    const reg = try dpy.getRegistry();
    defer reg.destroy();

    var self = Connection{
        .dpy = dpy,
        .seat = null,
        .ctl = null,
    };

    reg.setListener(*Connection, registryListener, &self);

    if (dpy.roundtrip() != .SUCCESS) return error.RoundtripFailed;
    if (self.seat == null or self.ctl == null) return error.MissingGlobals;

    std.log.info("successfully initialized wayland connection", .{});

    return self;
}

pub fn deinit(self: Connection) void {
    self.seat.?.destroy();
    self.ctl.?.destroy();
    self.dpy.disconnect();
}

pub fn runCommand(self: Connection, args: []const [:0]const u8) !void {
    std.log.debug("running command: {s}", .{args});
    for (args) |arg| self.ctl.?.addArgument(arg.ptr);

    var success: ?bool = null;
    const cb = try self.ctl.?.runCommand(self.seat.?);
    cb.setListener(*?bool, cmdCallbackListener, &success);

    while (success == null) {
        if (self.dpy.dispatch() != .SUCCESS) return error.RoundtripFailed;
    }

    if (!success.?) return error.CommandFailed;
}

fn cmdCallbackListener(
    _: *zriver.CommandCallbackV1,
    ev: zriver.CommandCallbackV1.Event,
    success: *?bool,
) void {
    switch (ev) {
        .success => |suc| {
            success.* = true;
            const output = std.mem.span(suc.output);

            if (output.len != 0)
                std.log.info("cmd output: {s}", .{output});
        },
        .failure => |fail| {
            success.* = false;
            std.log.err("cmd fail: {s}", .{fail.failure_message});
        },
    }
}

fn registryListener(reg: *wl.Registry, ev: wl.Registry.Event, self: *Connection) void {
    switch (ev) {
        .global => |glob| {
            if (std.mem.orderZ(u8, glob.interface, wl.Seat.getInterface().name) == .eq) {
                self.seat = reg.bind(
                    glob.name,
                    wl.Seat,
                    wl.Seat.generated_version,
                ) catch @panic("OOM");
            } else if (std.mem.orderZ(u8, glob.interface, zriver.ControlV1.getInterface().name) == .eq) {
                self.ctl = reg.bind(
                    glob.name,
                    zriver.ControlV1,
                    zriver.ControlV1.generated_version,
                ) catch @panic("OOM");
            }
        },
        .global_remove => {},
    }
}
