#include <errno.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <wayland-client.h>
#include <unistd.h>

#include "input.h"
#include "registry.h"
#include "wayland-runtime.h"
#include "window.h"
#include "figure-animate.h"

#define MAX_WINDOW_THREADS 128

struct window_thread_slot
{
    pthread_t thread;
    pthread_t aux_thread;
    int active;
    struct wl_event_queue *queue;
    struct wayland_window window;
};

static struct wl_display *g_display = NULL;
static struct window_thread_slot g_slots[MAX_WINDOW_THREADS];
static pthread_mutex_t g_thread_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_thread_cond = PTHREAD_COND_INITIALIZER;
static atomic_int g_active_threads = 0;
static atomic_bool g_shutdown = 0;
static int g_initialized = 0;

static void signal_thread_exit(struct window_thread_slot *slot)
{
    pthread_mutex_lock(&g_thread_lock);
    slot->active = 0;
    atomic_fetch_sub(&g_active_threads, 1);
    pthread_cond_broadcast(&g_thread_cond);
    pthread_mutex_unlock(&g_thread_lock);
}

#include <stdio.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>

// Прототип ASM-функции
void place_points_on_circle(
    float pos_x,
    float pos_y,
    float offset_rad,
    float radius,
    float *points,
    size_t count);

struct Point
{
    float x;
    float y;
};

static void *window_aux_loop(void *arg)
{
    struct window_thread_slot *slot = arg;
    struct window_draw_info *draw_info = &slot->window.draw_info;
    while (!atomic_load(&g_shutdown) && !window_should_close(&slot->window))
    {
        /* На время обновления позиции фигуры локаем мутекс */
        pthread_mutex_lock(&draw_info->figure_mutex);
        figure_animation_step(draw_info);

        // const size_t n = 8;
        // struct vec2 *pts = malloc(sizeof(struct vec2) * n);
        // if (!pts)
        // {
        //     printf("malloc failed\n");
        // }

        // float center_x = 0.0f;
        // float center_y = 0.0f;
        // float offset = 0.78f;
        // float radius = 1.0f;

        // // Вызов ASM-функции
        // place_points_on_circle(
        //     center_x,
        //     center_y,
        //     offset,
        //     radius,
        //     (float *)pts, // адрес выходного массива
        //     n);

        // // Вывод для проверки (и удобной точки останова)
        // for (size_t i = 0; i < n; i++)
        // {
        //     printf("%zu: x = %f, y = %f\n", i, pts[i].x, pts[i].y);
        // }

        // free(pts);
        pthread_mutex_unlock(&draw_info->figure_mutex);

        usleep(15 * 1000);
    }
    return NULL;
}

static void *window_thread_main(void *arg)
{
    struct window_thread_slot *slot = arg;
    memset(&slot->window, 0, sizeof(slot->window));

    slot->queue = wl_display_create_queue(g_display);
    if (!slot->queue)
    {
        signal_thread_exit(slot);
        return NULL;
    }

    if (window_init(g_display, slot->queue, &slot->window) < 0)
    {
        wl_event_queue_destroy(slot->queue);
        slot->queue = NULL;
        signal_thread_exit(slot);
        return NULL;
    }

    /* Запуск вспомогательного потока, который пока просто крутится с паузой 100 ms.
     * Поток завершится, как только окно будет помечено как закрыто. */
    int aux_res = pthread_create(&slot->aux_thread, NULL, window_aux_loop, slot);
    if (aux_res == 0)
        pthread_detach(slot->aux_thread);

    while (!atomic_load(&g_shutdown) && !window_should_close(&slot->window))
    {
        int dispatch = wl_display_dispatch_queue(g_display, slot->queue);
        if (dispatch < 0)
        {
            if (errno == EINTR)
                continue;
            atomic_store(&g_shutdown, 1);
            break;
        }
    }
    printf("Window #%d closed\n", slot->window.id);

    window_destroy(&slot->window);

    if (slot->queue)
    {
        wl_event_queue_destroy(slot->queue);
        slot->queue = NULL;
    }

    signal_thread_exit(slot);
    return NULL;
}

int32_t init_wayland(void)
{
    if (g_initialized)
        return 0;

    g_display = wl_display_connect(NULL);
    if (!g_display)
        return -1;

    /* Привязать глобальные объекты и запустить поток обработки (дефолтная очередь).
     * `registry_global_bind` добавляет listener и делает roundtrip. */
    if (registry_global_bind(g_display) < 0)
    {
        wl_display_disconnect(g_display);
        g_display = NULL;
        return -1;
    }

    atomic_store(&g_shutdown, 0);
    atomic_store(&g_active_threads, 0);
    g_initialized = 1;

    /* Print keyboard help to the console so users know available shortcuts and
     * behaviors. This mirrors the handling implemented in
     * `input-handle.c` and acts as a quick usage hint. */
    printf("\nKeyboard shortcuts and behavior:\n");
    printf("  - Enter: open a new window\n");
    printf("  - 1: switch to circle figure\n");
    printf("  - 2: switch to triangle figure\n");
    printf("  - 3: switch to square figure\n");
    printf("  - - : decrease animation speed (clamped, multiplicative)\n");
    printf("  - + or = : increase animation speed (clamped, multiplicative)\n");
    printf("  - Up arrow: increase figure radius (clamped to window bounds)\n");
    printf("  - Down arrow: decrease figure radius (minimum 1.0)\n");
    printf("  Note: actions are triggered on key press (not on release).\n\n");
    return 0;
}

int32_t run_window(void)
{
    if (!g_initialized || !g_display)
        return -1;

    int slot_index = -1;

    pthread_mutex_lock(&g_thread_lock);
    for (int i = 0; i < MAX_WINDOW_THREADS; ++i)
    {
        if (!g_slots[i].active)
        {
            slot_index = i;
            g_slots[i].active = 1;
            atomic_fetch_add(&g_active_threads, 1);
            break;
        }
    }
    pthread_mutex_unlock(&g_thread_lock);

    if (slot_index < 0)
        return -1;

    int create_res = pthread_create(&g_slots[slot_index].thread, NULL, window_thread_main, &g_slots[slot_index]);
    if (create_res != 0)
    {
        pthread_mutex_lock(&g_thread_lock);
        g_slots[slot_index].active = 0;
        atomic_fetch_sub(&g_active_threads, 1);
        pthread_mutex_unlock(&g_thread_lock);
        return -1;
    }

    pthread_detach(g_slots[slot_index].thread);
    return slot_index;
}

void wait_for_windows(void)
{
    pthread_mutex_lock(&g_thread_lock);
    while (atomic_load(&g_active_threads) > 0)
        pthread_cond_wait(&g_thread_cond, &g_thread_lock);
    pthread_mutex_unlock(&g_thread_lock);
}

void destroy_wayland(void)
{
    if (!g_initialized)
        return;

    wait_for_windows();

    input_cleanup();
    /* Остановить поток глобального реестра и сбросить глобальные данные */
    registry_global_unbind();

    if (g_display)
    {
        wl_display_disconnect(g_display);
        g_display = NULL;
    }

    memset(g_slots, 0, sizeof(g_slots));

    g_initialized = 0;
    atomic_store(&g_shutdown, 0);
}

struct wayland_window *get_window_by_surface(struct wl_surface *surf)
{
    for (int i = 0; i < MAX_WINDOW_THREADS; i++)
        if (g_slots[i].window.wl_surface == surf)
            return &g_slots[i].window;
    return NULL;
}
