#include <limits.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <wayland-client.h>
#include "../../../../gen/l_wl_protocols/xdg-shell-client.h"
#include <xkbcommon/xkbcommon.h>
#include "../../../l1/system/shared_memory.h"
#include "../../marie/graphics_geom.h"
#include "../../marie/rasterization.h"

#include <sys/poll.h>

#define MAX_BUFFER_WIDTH 1000
#define MAX_BUFFER_HEIGHT 800
#define SWAPCHAIN_SLOTS 2

_Static_assert(INT32_MAX / MAX_BUFFER_WIDTH / MAX_BUFFER_HEIGHT / 4 / SWAPCHAIN_SLOTS > 1, "Swapchain is too big");


typedef struct {
    int fd;
    uint32_t* data;
    struct wl_shm_pool* pool;
    bool want_to_draw;
    struct wl_buffer* used_buffers[SWAPCHAIN_SLOTS];
} swapchain_t;

typedef struct {
    vec2 pos;
    vec2 speed;
} durackaya_tochka;

typedef struct {
    /* Globals */
    struct wl_display *wl_display;
    struct wl_registry *wl_registry;
    struct wl_shm *wl_shm;
    struct wl_compositor *wl_compositor;
    struct xdg_wm_base *xdg_wm_base;
    struct wl_seat *wl_seat;
    /* Objects */
    swapchain_t swapchain;
    struct wl_surface *wl_surface;
    struct wl_callback* wl_callback;
    struct xdg_surface *xdg_surface;
    struct xdg_toplevel *xdg_toplevel;
    /* inputs */
    struct wl_pointer* pointer;
    struct wl_keyboard* keyboard;
    /* xkb */
    struct xkb_state* xkb_state;
    struct xkb_context* xkb_context;
    struct xkb_keymap* xkb_keymap;
    /* app state */
    vec2 pointer_pos;
    bool first_0x80_keys[0x80];
    durackaya_tochka v0;
    durackaya_tochka v1;
    durackaya_tochka v2;
    uint32_t last_frame_time;
    int32_t width;
    int32_t height;
    bool closed;
} state_t;

typedef struct {
    uint32_t* data;
    int32_t width;
    int32_t height;
} WhereToDrawTriangle;

U32 color_vec4_to_color_u32(vec4 color) {
    return (((U32)roundf(color.w * 255)) << 24) + (((U32)roundf(color.x * color.w * 255)) << 16) +
        (((U32)roundf(color.y * color.w * 255)) << 8) + (((U32)roundf(color.z * color.w * 255)) << 0);
}

void draw_frame_h_rasterizer_cb(void* ug, S32 x, S32 y, vec4 attr) {
    WhereToDrawTriangle* g = ug;
    if (0 <= x && x < g->width && 0 <= y && y < g->height)
        g->data[y * g->width + x] = color_vec4_to_color_u32(attr);
}

void draw_frame(state_t* state, uint32_t* data, int32_t width, int32_t height) {
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            data[y * width + x] = 0xFF00FFFF;
        }
    }
    WhereToDrawTriangle aboba = {data, width, height};
    marie_rasterize_triangle_with_attr(
        (MariePlaneVertAttr){.pos = state->v0.pos, .attr = {1, 0, 0, 1}},
        (MariePlaneVertAttr){.pos = state->v1.pos, .attr = {0, 1, 0, .5f}},
        (MariePlaneVertAttr){.pos = state->v2.pos, .attr = {0, 0, 1, .0f}},
        (FnMarieRasterizerCallback){.fn = draw_frame_h_rasterizer_cb, .guest = &aboba});
    vec2 points[3] = {state->v0.pos, state->v1.pos, state->v2.pos};
    for (int t = 0; t < 3; t++) {
        int x = (int)roundf(points[t].x - .5f);
        int y = (int)roundf(points[t].y - .5f);
        for (int i = -5; i <= 5; i++)
            for (int j = -5; j <= 5; j++)
                if (0 <= x+i && x+i < width && 0 <= y+j && y+j < height)
                    data[(y+j) * width + x + i] = 0;
    }
}

void durackaya_tochka_update(durackaya_tochka* dot, float dur, float width, float height, bool turning_left, bool turning_right) {
    vec2 *pos = &dot->pos;
    vec2 *speed = &dot->speed;
    float a = 0.9f * dur * ((turning_left ? -1.f : 0) + (turning_right ? 1.f : 0));
    *speed = mat2_mul_vec2(marie_2d_rot_mat2(a), *speed);
    *pos = vec2_add_vec2(*pos, vec2_mul_scal(*speed, dur));
    if (pos->x < 0) {
        speed->x = -speed->x;
        pos->x = 0;
    }
    if (pos->y < 0) {
        speed->y = -speed->y;
        pos->y = 0;
    }
    if (pos->x > width) {
        speed->x = -speed->x;
        pos->x = width;
    }
    if (pos->y > height) {
        speed->y = -speed->y;
        pos->y = height;
    }
}

void update_state(state_t* state, uint32_t fl) {
    const float width = (float)(state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH);
    const float height = (float)(state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT);
    float dur = (float)fl / 1000;
    durackaya_tochka_update(&state->v0, dur, width, height, state->first_0x80_keys[XKB_KEY_u], state->first_0x80_keys[XKB_KEY_i]);
    durackaya_tochka_update(&state->v1, dur, width, height, state->first_0x80_keys[XKB_KEY_j], state->first_0x80_keys[XKB_KEY_k]);
    durackaya_tochka_update(&state->v2, dur, width, height, state->first_0x80_keys[XKB_KEY_m], state->first_0x80_keys[XKB_KEY_less]);
}

static void main_h_wl_buffer_release(void *data, struct wl_buffer *buffer) {
    state_t* state = data;
    for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) {
        if (state->swapchain.used_buffers[ij] == buffer) {
            // printf("Buffer %p was released and destroyed\n", buffer);
            wl_buffer_destroy(buffer);
            state->swapchain.used_buffers[ij] = NULL;
            return;
        }
    }
    abortf("HUH??!!!\n");
}

static const struct wl_buffer_listener main_h_wl_buffer_listener = {
    .release = main_h_wl_buffer_release,
};

int swapchain_take_slot(state_t *state) {
    for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) {
        if (!state->swapchain.used_buffers[ij]) {
            const int32_t width = state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH;
            const int32_t height = state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT;
            struct wl_buffer* s = wl_shm_pool_create_buffer(
                state->swapchain.pool, ij * 4 * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT,
                width, height, 4 * width, WL_SHM_FORMAT_ARGB8888);
            state->swapchain.used_buffers[ij] = s;
            if (!s)
                abortf("wl_shm_pool_create_buffer\n");
            wl_buffer_add_listener(s, &main_h_wl_buffer_listener, state);

            return ij;
        }
    }
    return -1;
}

void try_drawing_frame(state_t *state){
    if (!state->swapchain.want_to_draw)
        return;
    int ij = swapchain_take_slot(state);
    // printf("Got slot %d", ij);
    if (ij < 0)
        return;
    assert(ij < SWAPCHAIN_SLOTS);
    state->swapchain.want_to_draw = false;
    const int32_t width = state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH;
    const int32_t height = state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT;

    /* Draw checkerboxed background */
    uint32_t* data = state->swapchain.data + ij * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT;
    draw_frame(state, data, width, height);
    printf("Attached\n");
    wl_surface_attach(state->wl_surface, state->swapchain.used_buffers[ij], 0, 0);
    printf("Damaged\n");
    wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX);
    printf("Commited\n");
    wl_surface_commit(state->wl_surface);
}

static void main_h_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial){
    state_t *state = data;
    printf("XDG surface configured!\n");
    printf("Acknowledged surface configure\n");
    xdg_surface_ack_configure(xdg_surface, serial);
    state->swapchain.want_to_draw = true;
    try_drawing_frame(state);
}

static void main_h_xdg_toplevel_configure(
    void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states
    ){
    state_t *state = data;
    printf("XDG toplevel configured to (%d %d)\n", width, height);
    if (width <= 0 || height < 0)
        return;
    state->width = width;
    state->height = height;
}

static void main_h_xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel){
    state_t *state = data;
    state->closed = true;
}

static const struct xdg_toplevel_listener main_h_xdg_toplevel_listener = {
    .configure = main_h_xdg_toplevel_configure,
    .close = main_h_xdg_toplevel_close,
};


static const struct xdg_surface_listener xdg_surface_listener = {
    .configure = main_h_xdg_surface_configure,
};

static void main_h_xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial){
    xdg_wm_base_pong(xdg_wm_base, serial);
}

static const struct xdg_wm_base_listener main_h_xdg_wm_base_listener = {
    .ping = main_h_xdg_wm_base_ping,
};



static void main_h_wl_keyboard_keymap(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size
    ) {
    state_t* state = data;
    if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
        abortf("O_o");
    char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
    if (map_shm == MAP_FAILED)
        abortf("Couldn't mmap new keymap ha-ha\n");

    xkb_keymap_unref(state->xkb_keymap);
    xkb_state_unref(state->xkb_state);
    state->xkb_keymap = xkb_keymap_new_from_string(
         state->xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
    munmap(map_shm, size);
    close(fd);
    state->xkb_state = xkb_state_new(state->xkb_keymap);
    printf("Keymap changed!\n");
    memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys));
}

static void main_h_wl_keyboard_enter(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys
    ) {
    state_t* state = data;
    printf("keyboard enter; keys pressed are:\n");
    uint32_t *key;
    wl_array_for_each(key, keys) {
        char buf1[128];
        char buf2[128];
        uint32_t actual_key = *key + 8;
        xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb_state, actual_key);
        // xkb_keysym_get_name(sym, buf1, sizeof(buf1));
        // xkb_state_key_get_utf8(state->xkb_state, actual_key, buf2, sizeof(buf2));
        // printf("sym: %-12s (%d), utf8: '%s'\n", buf1, sym, buf2);
        if (sym < 0x80)
            state->first_0x80_keys[sym] = true;
    }
}

static void main_h_wl_keyboard_leave(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface
    ) {
    state_t* state = data;
    memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys));
}

static void main_h_wl_keyboard_key(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t key_action
    ) {
    state_t* state = data;
    char buf1[128];
    char buf2[128];
    uint32_t actual_key = key + 8;
    /* It actually could be nokey keysym. You never know without checking */
    xkb_keysym_t keysym = xkb_state_key_get_one_sym(state->xkb_state, actual_key);
    xkb_keysym_get_name(keysym, buf1, sizeof(buf1));
    xkb_state_key_get_utf8(state->xkb_state, actual_key, buf2, sizeof(buf2));
    printf("Key %s (%d) %s, utf8: '%s'\n",
        buf1, keysym, key_action == WL_KEYBOARD_KEY_STATE_PRESSED ? "pressed" : "released", buf2);
    if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_RELEASED) {
        state->first_0x80_keys[keysym] = false;
    } else if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_PRESSED) {
        state->first_0x80_keys[keysym] = true;
    }
}

static void main_h_wl_keyboard_modifiers(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
    uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group
    ) {
    state_t* state = data;
    xkb_state_update_mask(state->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
}

static void main_h_wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay){
    printf("Repeat timings changed: rate = %d, delay = %d", rate, delay);
}

static const struct wl_keyboard_listener main_h_wl_keyboard_listener = {
    .keymap = main_h_wl_keyboard_keymap,
    .enter = main_h_wl_keyboard_enter,
    .leave = main_h_wl_keyboard_leave,
    .key = main_h_wl_keyboard_key,
    .modifiers = main_h_wl_keyboard_modifiers,
    .repeat_info = main_h_wl_keyboard_repeat_info,
};


static void main_h_wl_pointer_enter(
    void *data, struct wl_pointer *wl_pointer, uint32_t serial,
    struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y
    ) {
    state_t *state = data;
    state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f};
}

static void main_h_wl_pointer_leave(
    void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface
    ) {
    state_t *state = data;
}

static void main_h_wl_pointer_motion(
    void *data,struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y
    ) {
    state_t *state = data;
    state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f};
}

static void main_h_wl_pointer_button(
    void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t btn_action
    ) {
    state_t *state = data;
    if (btn_action == WL_POINTER_BUTTON_STATE_PRESSED) {
        printf("button = %u\n", button);
        if (button == 0x110) {
            state->v0.pos = state->pointer_pos;
        }
    }
}

static void main_h_wl_pointer_axis(
    void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value
    ) {
    state_t *state = data;
}

static void main_h_wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
    state_t *state = data;
}

const struct wl_pointer_listener main_h_wl_pointer_listener = {
    .enter = main_h_wl_pointer_enter,
    .leave = main_h_wl_pointer_leave,
    .motion = main_h_wl_pointer_motion,
    .button = main_h_wl_pointer_button,
    .axis = main_h_wl_pointer_axis,
    .frame = main_h_wl_pointer_frame
};

static void main_h_wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) {
    state_t* state = data;
    if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
        state->pointer = wl_seat_get_pointer(wl_seat);
        if (!state->pointer)
            abortf("wl_seat_get_pointer\n");
        wl_pointer_add_listener(state->pointer, &main_h_wl_pointer_listener, state);
    }
    if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
        state->keyboard = wl_seat_get_keyboard(wl_seat);
        if (!state->keyboard)
            abortf("wl_seat_get_keyboard\n");
        wl_keyboard_add_listener(state->keyboard, &main_h_wl_keyboard_listener, state);
    }
}

static void main_h_wl_seat_name(void* data, struct wl_seat* wl_seat, const char* name) {
    printf("Our seat name: %s\n", name);
}

static const struct wl_seat_listener main_h_wl_seat_listener = {
    .capabilities = main_h_wl_seat_capabilities,
    .name = main_h_wl_seat_name,
};

static void main_h_wl_registry_global(
    void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version
    ) {
    state_t *state = data;
    if (strcmp(interface, wl_shm_interface.name) == 0) {
        state->wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1);
        if (!state->wl_shm)
            abortf("wl_registry_bind\n");
    } else if (strcmp(interface, wl_compositor_interface.name) == 0) {
        state->wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4);
        if (!state->wl_compositor)
            abortf("wl_registry_bind\n");
    } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
        state->xdg_wm_base = wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1);
        if (!state->xdg_wm_base)
            abortf("wl_registry_bind\n");
        xdg_wm_base_add_listener(state->xdg_wm_base, &main_h_xdg_wm_base_listener, state);
    } else if (strcmp(interface, wl_seat_interface.name) == 0) {
        if (state->wl_seat) {
            printf("We got second seat, but we only need one\n");
            return;
        }
        state->wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 4);
        if (!state->wl_seat)
            abortf("wl_registry_bind\n");
        wl_seat_add_listener(state->wl_seat, &main_h_wl_seat_listener, state);
    }
}

static void main_h_wl_registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name){
    // todo: delete seats
}

static const struct wl_registry_listener main_h_wl_registry_listener = {
    .global = main_h_wl_registry_global,
    .global_remove = main_h_wl_registry_global_remove,
};

static const struct wl_callback_listener main_h_wl_surface_frame_listener;

static void main_h_wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time){
    state_t *state = data;
    wl_callback_destroy(cb);
    printf("AAA!!! CALLBACK!!!\n");
    state->wl_callback = wl_surface_frame(state->wl_surface);
    if (!state->wl_callback)
        abortf("wl_surface_frame\n");
    wl_callback_add_listener(state->wl_callback, &main_h_wl_surface_frame_listener, state);

    if (state->last_frame_time != 0) {
        update_state(state, time - state->last_frame_time);
    }

    state->swapchain.want_to_draw = true;
    try_drawing_frame(state);

    state->last_frame_time = time;
}

static const struct wl_callback_listener main_h_wl_surface_frame_listener = {
    .done = main_h_wl_surface_frame_done,
};


int main() {
    state_t state = { .width = 800, .height = 480 };
    state.v0 = (durackaya_tochka){.pos = {10, 10}, .speed = {100, 100}};
    state.v1 = (durackaya_tochka){.pos = {100, 10}, .speed = {100, -50}};
    state.v2 = (durackaya_tochka){.pos = {5, 330}, .speed = {-20, 170}};
    state.wl_display = wl_display_connect(NULL);
    if (!state.wl_display)
        abortf("Could not connect");
    state.wl_registry = wl_display_get_registry(state.wl_display);
    if (!state.wl_registry)
        abortf("wl_display_get_registry");
    wl_registry_add_listener(state.wl_registry, &main_h_wl_registry_listener, &state);
    wl_display_roundtrip(state.wl_display);
    if (!state.wl_shm)
        abortf("No wl_shm");
    if (!state.wl_compositor)
        abortf("No wl_compositor");
    if (!state.xdg_wm_base)
        abortf("No xdg_wm_base");
    {
        size_t size = SWAPCHAIN_SLOTS * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT * 4;
        assert(size < INT32_MAX);
        state.swapchain.fd = allocate_shm_file(size);
        if (state.swapchain.fd == -1) {
            abortf("AAA");
        }

        state.swapchain.data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, state.swapchain.fd, 0);
        if (state.swapchain.data == MAP_FAILED) {
            close(state.swapchain.fd);
            abortf("what the dog doing");
        }

        state.swapchain.pool = wl_shm_create_pool(state.wl_shm, state.swapchain.fd, size);
        if (!state.swapchain.pool)
            abortf("wl_shm_create_pool");
        close(state.swapchain.fd);
    }
    state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
    if (!state.xkb_context)
        abortf("xkb_context_new\n");
    state.wl_surface = wl_compositor_create_surface(state.wl_compositor);
    if (!state.wl_surface)
        abortf("wl_compositor_create_surface\n");
    state.xdg_surface = xdg_wm_base_get_xdg_surface(
            state.xdg_wm_base, state.wl_surface);
    if (!state.xdg_surface)
        abortf("xdg_wm_base_get_xdg_surface\n");
    xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, &state);
    state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface);
    if (!state.xdg_toplevel)
        abortf("xdg_surface_get_toplevel\n");
    xdg_toplevel_add_listener(state.xdg_toplevel, &main_h_xdg_toplevel_listener, &state);
    xdg_toplevel_set_title(state.xdg_toplevel, "r1");
    printf("Commited\n");
    wl_surface_commit(state.wl_surface);

    state.wl_callback = wl_surface_frame(state.wl_surface);
    if (!state.wl_callback)
        abortf("wl_surface_frame\n");
    wl_callback_add_listener(state.wl_callback, &main_h_wl_surface_frame_listener, &state);


    int display_fd = wl_display_get_fd(state.wl_display);
    struct pollfd pollfds[1] = {{.fd = display_fd, .events = POLLIN | POLLERR}};
    while (!state.closed) {
        wl_display_prepare_read(state.wl_display);
        if (wl_display_flush(state.wl_display) < 0) {
            if (errno == EAGAIN) {
                printf("Not enough space for a flush!\n");
                pollfds[0].events |= POLLOUT;
            }
            else {
                abortf("wl_display_flush\n");
            }
        } else {
            pollfds[0].events &= ~POLLOUT;
        }
        if (poll(pollfds, ARRAY_SIZE(pollfds), -1) < 0)
            abortf("poll\n");
        if (pollfds[0].revents & (POLLIN | POLLERR)) {
            if (wl_display_read_events(state.wl_display))
                abortf("wl_display_read_events\n");
            int ret;
            if ((ret =wl_display_dispatch_pending(state.wl_display) )< 0)
                abortf("wl_display_dispatch_pending\n");
        } else {
            wl_display_cancel_read(state.wl_display);
        }
    }
    if (state.wl_callback)
        wl_callback_destroy(state.wl_callback);
    xdg_toplevel_destroy(state.xdg_toplevel);
    xdg_surface_destroy(state.xdg_surface);
    xdg_wm_base_destroy(state.xdg_wm_base);
    wl_surface_destroy(state.wl_surface);
    wl_compositor_destroy(state.wl_compositor);
    if (state.pointer)
        wl_pointer_destroy(state.pointer);
    xkb_context_unref(state.xkb_context);
    xkb_keymap_unref(state.xkb_keymap);
    xkb_state_unref(state.xkb_state);
    if (state.keyboard)
        wl_keyboard_destroy(state.keyboard);
    if (state.wl_seat)
        wl_seat_destroy(state.wl_seat);
    munmap(state.swapchain.data, SWAPCHAIN_SLOTS * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT * 2);
    for (size_t i = 0; i < SWAPCHAIN_SLOTS; i++) {
        if (state.swapchain.used_buffers[i])
            wl_buffer_destroy(state.swapchain.used_buffers[i]);
    }
    wl_shm_pool_destroy(state.swapchain.pool);
    wl_shm_destroy(state.wl_shm);
    wl_registry_destroy(state.wl_registry);
    wl_display_disconnect(state.wl_display);
    return 0;
}
