#include <fcntl.h>
#include <limits.h>
#include <sys/mman.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/core/util.h"
#include "../../margaret/vulkan_utils.h"
#include <poll.h>


// todo: generate this function in l2
VkRenderPass create_render_pass_0(VkDevice logical_device, VkFormat colorbuffer_format) {
    VkAttachmentDescription all_attachments[1] = {
        {
            .format = colorbuffer_format,
            .samples = VK_SAMPLE_COUNT_1_BIT,
            .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
            .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
            .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
            .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
            .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
            .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
        }
    };

    // For our one single render subpass
    VkAttachmentReference color_attachments_refs[1] = {
        {
            .attachment = 0,
            .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    } };

    VkSubpassDescription subpasses_descr[1] = { (VkSubpassDescription){
        .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
        .colorAttachmentCount = ARRAY_SIZE(color_attachments_refs),
        .pColorAttachments = color_attachments_refs,
    } };

    VkSubpassDependency subpass_dependencies[1] = {
        // subpass_0_external
        (VkSubpassDependency) {
            .srcSubpass = VK_SUBPASS_EXTERNAL,
            .dstSubpass = 0,
            .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
            .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
            .srcAccessMask = 0,
            .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    }};

    VkRenderPassCreateInfo render_pass_crinfo = {
        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
        .attachmentCount = ARRAY_SIZE(all_attachments),
        .pAttachments = all_attachments,
        .subpassCount = ARRAY_SIZE(subpasses_descr),
        .pSubpasses = subpasses_descr,
        .dependencyCount = ARRAY_SIZE(subpass_dependencies),
        .pDependencies = subpass_dependencies,
    };
    VkRenderPass render_pass;
    if (vkCreateRenderPass(logical_device, &render_pass_crinfo, NULL, &render_pass) != VK_SUCCESS)
        abortf("vkCreateRenderPass");
    return render_pass;
}

void reset_and_record_command_buffer_0(
    VkCommandBuffer command_buffer, VkRenderPass render_pass_0,
    VkFramebuffer swapchain_image_framebuffer, VkExtent2D image_extent,
    float ht
    ) {
    if (vkResetCommandBuffer(command_buffer, 0) != VK_SUCCESS)
        abortf("vkResetCommandBuffer");
    VkCommandBufferBeginInfo info_begin = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
    if (vkBeginCommandBuffer(command_buffer, &info_begin) != VK_SUCCESS)
        abortf("vkBeginCommandBuffer");

    VkClearValue clear_values[1] = {{.color = {.float32={1, fabsf(sinf(ht)), 0, 1}}},};
    VkRenderPassBeginInfo renderpass_begin = {
        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
        .renderPass = render_pass_0,
        .framebuffer = swapchain_image_framebuffer,
        .renderArea.offset = (VkOffset2D){0, 0},
        .renderArea.extent = image_extent,
        .clearValueCount = ARRAY_SIZE(clear_values),
        .pClearValues = clear_values,
    };

    vkCmdBeginRenderPass(command_buffer, &renderpass_begin, VK_SUBPASS_CONTENTS_INLINE);
    vkCmdEndRenderPass(command_buffer);
    if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS)
        abortf("vkEndCommandBuffer");
}

typedef struct {
    VkSemaphore image_available_semaphore;
    VkFence in_flight_fence;
} Jane_r3;

NODISCARD Jane_r3 Jane_r3_create(VkDevice device) {
    return (Jane_r3){
        .image_available_semaphore = margaret_create_semaphore(device),
        .in_flight_fence = margaret_create_fence(device, true)
    };
}

void Jane_r3_destroy(VkDevice device, Jane_r3 jane) {
    vkDestroyFence(device, jane.in_flight_fence, NULL);
    vkDestroySemaphore(device, jane.image_available_semaphore, NULL);
}


typedef struct {
    /* Memory settings */
    VkExtent2D sane_image_extent_limit;
    /* Globals */
    struct wl_display *wl_display;
    struct wl_registry *wl_registry;
    struct wl_compositor *wl_compositor;
    struct xdg_wm_base *xdg_wm_base;
    struct wl_seat *wl_seat;
    /* Objects */
    struct wl_surface *wl_surface;
    struct wl_callback* wl_callback;
    struct xdg_surface *xdg_surface;
    struct xdg_toplevel *xdg_toplevel;
    /* vulkan objects */
    MargaretInstanceAndItsDebug vk_instance_and_debug;
    VkPhysicalDevice physical_device;
    MargaretChosenQueueFamilies queue_fam;
    VkDevice device;
    VkSurfaceKHR vk_surface;
    VkQueue graphics_queue;
    VkQueue presentation_queue;
    VkRenderPass render_pass_0;
    MargaretSwapchainBundle swfb;
    VkCommandPool command_pool;
    VkCommandBuffer rendering_command_buffer_0;
    Jane_r3 jane;
    /* 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 */
    float ht;
    uint32_t last_frame_time;
    int32_t width;
    int32_t height;
    bool closed;
    bool first_0x80_keys[0x80];
    /* framerate counting */
    U32 frame_count_since_key;
    margaret_ns_time prev_key_frame_time;
} state_t;

void update_state(state_t* state, uint32_t fl) {
    float dur = (float)fl / 1000;
    state->ht += dur;
}


void recreate_swapchain(state_t* state) {
    // We are about stop program and rebuild our sem+sem+fence synchronization mechanism
    vkDeviceWaitIdle(state->device);
    Jane_r3_destroy(state->device, state->jane);
    state->jane = Jane_r3_create(state->device);
    VkSwapchainKHR old_swapchain = MargaretSwapchainBundle_pop_swapchain_drop_rest(state->device, state->swfb);
    // old swfb is 83% dropped
    ResultMargaretChosenSwapchainDetailsOrSpanU8 swapchain_details_res = margaret_choose_swapchain_details(
        state->physical_device, state->vk_surface, state->sane_image_extent_limit);
    if (swapchain_details_res.variant != Result_Ok)
        abortf("swapchain_details_res.variant != Result_Ok");
    MargaretChosenSwapchainDetails swapchain_details = swapchain_details_res.ok;
    MargaretSwapchainBundle new_swfb = MargaretSwapchainBundle_new(
        state->device, state->queue_fam, swapchain_details, state->vk_surface,
        state->render_pass_0, state->swfb.swapchain);
    vkDestroySwapchainKHR(state->device, old_swapchain, NULL);
    // Now old swfb is 100% dropped
    state->swfb = new_swfb;
}


void vulkano_frame_drawing(state_t* state) {
    // Rendering
    and_try_again:
    vkWaitForFences(state->device, 1, &state->jane.in_flight_fence, VK_TRUE, UINT64_MAX);
    // printf("Dozhdalisya!\n");
    uint32_t ij;
    VkResult aq_ret = vkAcquireNextImageKHR(
        state->device, state->swfb.swapchain,
        UINT64_MAX, state->jane.image_available_semaphore, VK_NULL_HANDLE, &ij
    );
    if (aq_ret == VK_ERROR_OUT_OF_DATE_KHR) {
        fprintf(stderr, "vkAcquireNextImageKHR: VK_ERROR_OUT_OF_DATE_KHR\n");
        recreate_swapchain(state);
        goto and_try_again;
    } else if (aq_ret == VK_SUBOPTIMAL_KHR) {
        fprintf(stderr, "vkAcquireNextImageKHR: VK_SUBOPTIMAL_KHR\n");
        recreate_swapchain(state);
        goto and_try_again;
    } else if (aq_ret != VK_SUCCESS) {
        abortf("vkAcquireNextImageKHR");
    }
    // printf("Image acquired\n");
    vkResetFences(state->device, 1, &state->jane.in_flight_fence);

    reset_and_record_command_buffer_0(state->rendering_command_buffer_0, state->render_pass_0,
        *VecVkFramebuffer_at(&state->swfb.framebuffers, ij), state->swfb.extent, state->ht);

    {
        VkSemaphore waiting_for_semaphores[1] = {state->jane.image_available_semaphore};
        VkPipelineStageFlags waiting_stages[1] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
        VkCommandBuffer command_buffers[1] = {state->rendering_command_buffer_0};
        VkSemaphore signaling_semaphores[1] = {
            *VecVkSemaphore_at(&state->swfb.rendering_finished_here_semaphores, ij)
        };
        VkSubmitInfo submit_info = {
            .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,

            .waitSemaphoreCount =  1,
            .pWaitSemaphores = waiting_for_semaphores,
            .pWaitDstStageMask =  waiting_stages,

            .commandBufferCount = ARRAY_SIZE(command_buffers),
            .pCommandBuffers = command_buffers,

            .signalSemaphoreCount = ARRAY_SIZE(signaling_semaphores),
            .pSignalSemaphores = signaling_semaphores,
        };
        if (vkQueueSubmit(state->graphics_queue, 1, &submit_info, state->jane.in_flight_fence) != VK_SUCCESS)
            abortf("vkQueueSubmit");
    }

    {
        VkSemaphore waiting_for_semaphores[] = {
            *VecVkSemaphore_at(&state->swfb.rendering_finished_here_semaphores, ij)
        };
        VkSwapchainKHR swapchains[] = { state->swfb.swapchain };
        uint32_t image_indices[] = { ij };
        assert( ARRAY_SIZE(swapchains) == ARRAY_SIZE(image_indices) );

        VkPresentInfoKHR present_info = {
            .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
            .waitSemaphoreCount = ARRAY_SIZE(waiting_for_semaphores),
            .pWaitSemaphores = waiting_for_semaphores,

            .swapchainCount = ARRAY_SIZE(swapchains),
            .pSwapchains = swapchains,
            .pImageIndices = image_indices,
            .pResults = NULL,
        };

        // printf("Now I will present it\n");
        VkResult pres_ret = vkQueuePresentKHR(state->presentation_queue, &present_info);
        // todo: ponder more over this
        if (pres_ret == VK_ERROR_OUT_OF_DATE_KHR) {
            fprintf(stderr, "vkQueuePresentKHR: VK_ERROR_OUT_OF_DATE_KHR\n");
            recreate_swapchain(state);
            goto and_try_again;
        } else if (pres_ret == VK_SUBOPTIMAL_KHR) {
            fprintf(stderr, "vkQueuePresentKHR: VK_SUBOPTIMAL_KHR\n");
            recreate_swapchain(state);
            goto and_try_again;
        } else if (pres_ret != VK_SUCCESS) {
            abortf("vkQueuePresentKHR");
        }
    }
    margaret_ns_time frame_B0 = margaret_clock_gettime_monotonic_raw();
    state->frame_count_since_key++;
    if (margaret_ns_time_sec_diff(state->prev_key_frame_time, frame_B0) > 1.0) {
        float fps = (float)state->frame_count_since_key / margaret_ns_time_sec_diff(state->prev_key_frame_time, frame_B0);
        printf("FPS: %0.1f\n", fps);
        state->frame_count_since_key = 0;
        state->prev_key_frame_time = frame_B0;
    }
}


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! (%d %d)\n", state->width, state->height);
    printf("Acknowledged surface configure\n");
    xdg_surface_ack_configure(xdg_surface, serial);

    vulkano_frame_drawing(state);
}

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

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;
    uint32_t *conf_state;
    wl_array_for_each(conf_state, states) {
        printf("A state of xdg toplevel: %u\n", *conf_state);
    }
    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 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) {
        uint32_t actual_key = *key + 8;
        xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb_state, actual_key);
        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;
    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);
    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); // todo: use this information
}

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;
}

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;
}

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;
}

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_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){

}

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);
    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);
    }
    vulkano_frame_drawing(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 = { .sane_image_extent_limit = {1000, 700}, .width = 800, .height = 480 };

    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_compositor)
        abortf("No wl_compositor");
    if (!state.xdg_wm_base)
        abortf("No xdg_wm_base");
    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, "r3");
    xdg_toplevel_set_app_id(state.xdg_toplevel, "r3");
    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);

    state.vk_instance_and_debug = MargaretInstanceAndItsDebug_new(true);
    VkInstance vk_instance = state.vk_instance_and_debug.instance;
    state.vk_surface = margaret_create_surface(vk_instance, state.wl_display, state.wl_surface);
    state.physical_device = margaret_select_one_physical_device(vk_instance, state.vk_surface,
        cstr("nvidia"), cstr("NOT SPECIFIED"), state.sane_image_extent_limit);
    ResultMargaretChosenQueueFamiliesOrSpanU8 queue_fam_res = margaret_choose_good_queue_families(
        state.physical_device, state.vk_surface);
    if (queue_fam_res.variant != Result_Ok)
        abortf("queue_fam_res.variant != Result_Ok");
    state.queue_fam = queue_fam_res.ok;
    state.device = margaret_create_logical_device(state.physical_device, state.queue_fam);
    vkGetDeviceQueue(state.device, state.queue_fam.for_graphics, 0, &state.graphics_queue);
    vkGetDeviceQueue(state.device, state.queue_fam.for_presentation, 0, &state.presentation_queue);
    ResultMargaretChosenSwapchainDetailsOrSpanU8 swapchain_details_res = margaret_choose_swapchain_details(
        state.physical_device, state.vk_surface, state.sane_image_extent_limit);
    if (swapchain_details_res.variant != Result_Ok)
        abortf("swapchain_details_res.variant != Result_Ok");
    MargaretChosenSwapchainDetails swapchain_details = swapchain_details_res.ok;
    state.render_pass_0 = create_render_pass_0(state.device, swapchain_details.surface_format.format);
    // PipelineHands pipeline_hands_0 = create_graphics_pipeline_0(device, render_pass_0, 0);

    state.swfb = MargaretSwapchainBundle_new(state.device, state.queue_fam,
        swapchain_details_res.ok, state.vk_surface, state.render_pass_0, NULL);
    state.command_pool = margaret_create_resettable_command_pool(state.device, state.queue_fam.for_graphics);
    state.rendering_command_buffer_0 = margaret_allocate_command_buffer(state.device, state.command_pool);

    state.jane = Jane_r3_create(state.device);

    // int wl_display_loop_fd = wl_display_get_fd(state.wl_display);
    // struct pollfd mainloop_fds[1] = {(struct pollfd){}};
    while (wl_display_dispatch(state.wl_display) >= 0) {
        if (state.closed)
            break;
    }
    printf("Finished!\n");
    vkDeviceWaitIdle(state.device);
    // todo: destroy instance and all the crrp


    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);
    wl_registry_destroy(state.wl_registry);
    wl_display_disconnect(state.wl_display);
    return 0;
}
