/* Copyright (c) 202325? retaining? existing license? not specified */

#include "registry.h"
#include "input.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"

struct registry_binding
{
    uint32_t name;
    uint32_t version;
};

static struct registry_binding compositor_binding = {0};
static struct registry_binding shm_binding = {0};
static struct registry_binding wm_base_binding = {0};
static struct registry_binding seat_binding = {0};

static __thread struct wl_compositor *tls_compositor;
static __thread struct wl_shm *tls_shm;
static __thread struct xdg_wm_base *tls_wm_base;

static uint32_t clamp_version(uint32_t announced, uint32_t supported)
{
    return (announced < supported) ? announced : supported;
}

static void registry_global(void *data, struct wl_registry *reg, uint32_t name, const char *intf, uint32_t version)
{
    printf("%u\t%s\tv:%u\n", name, intf, version);
    if (!strcmp(intf, wl_compositor_interface.name))
    {
        compositor_binding.name = name;
        compositor_binding.version = clamp_version(version, wl_compositor_interface.version);
    }
    else if (!strcmp(intf, wl_shm_interface.name))
    {
        shm_binding.name = name;
        shm_binding.version = clamp_version(version, wl_shm_interface.version);
    }
    else if (!strcmp(intf, xdg_wm_base_interface.name))
    {
        wm_base_binding.name = name;
        wm_base_binding.version = clamp_version(version, xdg_wm_base_interface.version);
    }
    else if (!strcmp(intf, wl_seat_interface.name))
    {
        seat_binding.name = name;
        seat_binding.version = clamp_version(version, wl_seat_interface.version);
    }
}

static void registry_global_remove(void *data, struct wl_registry *reg, uint32_t name)
{
    (void)data;
    (void)reg;
    (void)name;
}

static const struct wl_registry_listener registry_listener = {
    .global = registry_global,
    .global_remove = registry_global_remove
};

void registry_add_listener(struct wl_registry *registry)
{
    wl_registry_add_listener(registry, &registry_listener, NULL);
}

static void destroy_tls_objects(void)
{
    if (tls_compositor)
    {
        wl_compositor_destroy(tls_compositor);
        tls_compositor = NULL;
    }
    if (tls_shm)
    {
        wl_shm_destroy(tls_shm);
        tls_shm = NULL;
    }
    if (tls_wm_base)
    {
        xdg_wm_base_destroy(tls_wm_base);
        tls_wm_base = NULL;
    }
}

int registry_thread_bind(struct wl_display *display, struct wl_event_queue *queue)
{
    if (!display || !queue)
        return -1;
    if (!compositor_binding.name || !shm_binding.name || !wm_base_binding.name)
        return -1;

    destroy_tls_objects();

    struct wl_registry *registry = wl_display_get_registry(display);
    if (!registry)
        return -1;

    wl_proxy_set_queue((struct wl_proxy *)registry, queue);

    tls_compositor = wl_registry_bind(registry, compositor_binding.name, &wl_compositor_interface, compositor_binding.version);
    tls_shm = wl_registry_bind(registry, shm_binding.name, &wl_shm_interface, shm_binding.version);
    tls_wm_base = wl_registry_bind(registry, wm_base_binding.name, &xdg_wm_base_interface, wm_base_binding.version);

    if (!tls_compositor || !tls_shm || !tls_wm_base)
    {
        wl_registry_destroy(registry);
        destroy_tls_objects();
        return -1;
    }

    wl_proxy_set_queue((struct wl_proxy *)tls_compositor, queue);
    wl_proxy_set_queue((struct wl_proxy *)tls_shm, queue);
    wl_proxy_set_queue((struct wl_proxy *)tls_wm_base, queue);

    if (seat_binding.name)
    {
        struct wl_seat *seat = wl_registry_bind(registry, seat_binding.name, &wl_seat_interface, seat_binding.version);
        if (seat)
        {
            wl_proxy_set_queue((struct wl_proxy *)seat, queue);
            input_register_seat(seat);
        }
    }

    wl_registry_destroy(registry);
    return 0;
}

void registry_thread_unbind(void)
{
    destroy_tls_objects();
}

void registry_reset_globals(void)
{
    destroy_tls_objects();
    compositor_binding.name = 0;
    shm_binding.name = 0;
    wm_base_binding.name = 0;
    seat_binding.name = 0;
}

struct wl_compositor *registry_get_compositor(void)
{
    return tls_compositor;
}

struct wl_shm *registry_get_shm(void)
{
    return tls_shm;
}

struct xdg_wm_base *registry_get_xdg_wm_base(void)
{
    return tls_wm_base;
}
