/*
 * DWN - Desktop Window Manager
 * retoor <retoor@molodetz.nl>
 * Event Bus Implementation - Thread-Safe Pub/Sub
 */

#include "threading.h"
#include "util.h"

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/eventfd.h>

#define MAX_SUBSCRIPTIONS_PER_TYPE 64
#define EVENT_QUEUE_SIZE 4096

/* ============================================================================
 * Event Bus Types
 * ============================================================================ */

typedef struct Subscription {
    SubscriptionId id;
    EventType type;
    EventHandler handler;
    void *user_data;
    EventFilter filter;
    void *filter_data;
    bool active;
} Subscription;

typedef struct EventNode {
    EventType type;
    void *data;
    void (*free_fn)(void*);
    struct EventNode *next;
} EventNode;

struct EventBus {
    Subscription subscriptions[MAX_SUBSCRIPTIONS_PER_TYPE * 32]; /* Fixed pool */
    uint32_t subscription_count;
    _Atomic SubscriptionId next_id;
    
    /* Event queue (MPSC) */
    pthread_mutex_t queue_mutex;
    EventNode *event_head;
    EventNode *event_tail;
    _Atomic uint32_t event_count;
    
    int event_fd;
    _Atomic int shutdown;
    
    uint32_t batch_size;
    pthread_mutex_t sub_mutex;
};

static SubscriptionId generate_subscription_id(EventBus *bus)
{
    return atomic_fetch_add_explicit(&bus->next_id, 1, ATOMIC_RELAXED) + 1;
}

/* ============================================================================
 * Event Bus Implementation
 * ============================================================================ */

EventBus* event_bus_create(void)
{
    EventBus *bus = dwn_malloc(sizeof(EventBus));
    if (!bus) return NULL;
    
    memset(bus->subscriptions, 0, sizeof(bus->subscriptions));
    bus->subscription_count = 0;
    atomic_store_explicit(&bus->next_id, 0, ATOMIC_RELEASE);
    
    bus->event_head = NULL;
    bus->event_tail = NULL;
    atomic_store_explicit(&bus->event_count, 0, ATOMIC_RELEASE);
    
    atomic_store_explicit(&bus->shutdown, 0, ATOMIC_RELEASE);
    bus->batch_size = 100;
    
    if (pthread_mutex_init(&bus->queue_mutex, NULL) != 0) {
        dwn_free(bus);
        return NULL;
    }
    
    if (pthread_mutex_init(&bus->sub_mutex, NULL) != 0) {
        pthread_mutex_destroy(&bus->queue_mutex);
        dwn_free(bus);
        return NULL;
    }
    
    bus->event_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if (bus->event_fd < 0) {
        pthread_mutex_destroy(&bus->sub_mutex);
        pthread_mutex_destroy(&bus->queue_mutex);
        dwn_free(bus);
        return NULL;
    }
    
    return bus;
}

void event_bus_destroy(EventBus *bus)
{
    if (!bus) return;
    
    atomic_store_explicit(&bus->shutdown, 1, ATOMIC_RELEASE);
    
    /* Drain remaining events */
    event_bus_process(bus);
    
    /* Free remaining events */
    pthread_mutex_lock(&bus->queue_mutex);
    EventNode *node = bus->event_head;
    while (node) {
        EventNode *next = node->next;
        if (node->free_fn) {
            node->free_fn(node->data);
        }
        dwn_free(node);
        node = next;
    }
    pthread_mutex_unlock(&bus->queue_mutex);
    
    close(bus->event_fd);
    pthread_mutex_destroy(&bus->sub_mutex);
    pthread_mutex_destroy(&bus->queue_mutex);
    dwn_free(bus);
}

SubscriptionId event_bus_subscribe(EventBus *bus, EventType type,
                                   EventHandler handler, void *user_data)
{
    return event_bus_subscribe_filtered(bus, type, handler, user_data, NULL, NULL);
}

SubscriptionId event_bus_subscribe_filtered(EventBus *bus, EventType type,
                                            EventHandler handler, void *user_data,
                                            EventFilter filter, void *filter_data)
{
    if (!bus || !handler || type >= 32) return 0;
    
    pthread_mutex_lock(&bus->sub_mutex);
    
    if (bus->subscription_count >= sizeof(bus->subscriptions) / sizeof(bus->subscriptions[0])) {
        pthread_mutex_unlock(&bus->sub_mutex);
        return 0;
    }
    
    /* Find slot */
    for (uint32_t i = 0; i < sizeof(bus->subscriptions) / sizeof(bus->subscriptions[0]); i++) {
        if (!bus->subscriptions[i].active) {
            Subscription *sub = &bus->subscriptions[i];
            sub->id = generate_subscription_id(bus);
            sub->type = type;
            sub->handler = handler;
            sub->user_data = user_data;
            sub->filter = filter;
            sub->filter_data = filter_data;
            sub->active = true;
            
            bus->subscription_count++;
            pthread_mutex_unlock(&bus->sub_mutex);
            return sub->id;
        }
    }
    
    pthread_mutex_unlock(&bus->sub_mutex);
    return 0;
}

bool event_bus_unsubscribe(EventBus *bus, SubscriptionId id)
{
    if (!bus || id == 0) return false;
    
    pthread_mutex_lock(&bus->sub_mutex);
    
    for (uint32_t i = 0; i < sizeof(bus->subscriptions) / sizeof(bus->subscriptions[0]); i++) {
        if (bus->subscriptions[i].active && bus->subscriptions[i].id == id) {
            bus->subscriptions[i].active = false;
            bus->subscription_count--;
            pthread_mutex_unlock(&bus->sub_mutex);
            return true;
        }
    }
    
    pthread_mutex_unlock(&bus->sub_mutex);
    return false;
}

void event_bus_publish(EventBus *bus, EventType type, void *event_data)
{
    event_bus_publish_owned(bus, type, event_data, NULL);
}

void event_bus_publish_owned(EventBus *bus, EventType type, void *event_data,
                             void (*free_fn)(void*))
{
    if (!bus || type >= 32) {
        if (free_fn && event_data) free_fn(event_data);
        return;
    }
    
    if (atomic_load_explicit(&bus->shutdown, ATOMIC_ACQUIRE)) {
        if (free_fn && event_data) free_fn(event_data);
        return;
    }
    
    EventNode *node = dwn_malloc(sizeof(EventNode));
    if (!node) {
        if (free_fn && event_data) free_fn(event_data);
        return;
    }
    
    node->type = type;
    node->data = event_data;
    node->free_fn = free_fn;
    node->next = NULL;
    
    pthread_mutex_lock(&bus->queue_mutex);
    
    if (bus->event_tail) {
        bus->event_tail->next = node;
    } else {
        bus->event_head = node;
    }
    bus->event_tail = node;
    
    atomic_fetch_add_explicit(&bus->event_count, 1, ATOMIC_RELAXED);
    
    pthread_mutex_unlock(&bus->queue_mutex);
    
    /* Signal via eventfd */
    uint64_t one = 1;
    ssize_t w __attribute__((unused)) = write(bus->event_fd, &one, sizeof(one));
}

uint32_t event_bus_process(EventBus *bus)
{
    if (!bus) return 0;
    
    /* Acknowledge eventfd */
    uint64_t val;
    ssize_t r __attribute__((unused)) = read(bus->event_fd, &val, sizeof(val));
    
    uint32_t processed = 0;
    
    while (processed < bus->batch_size) {
        /* Dequeue event */
        pthread_mutex_lock(&bus->queue_mutex);
        
        EventNode *node = bus->event_head;
        if (!node) {
            pthread_mutex_unlock(&bus->queue_mutex);
            break;
        }
        
        bus->event_head = node->next;
        if (!bus->event_head) {
            bus->event_tail = NULL;
        }
        
        atomic_fetch_sub_explicit(&bus->event_count, 1, ATOMIC_RELAXED);
        pthread_mutex_unlock(&bus->queue_mutex);
        
        /* Dispatch to subscribers */
        pthread_mutex_lock(&bus->sub_mutex);
        
        for (uint32_t i = 0; i < sizeof(bus->subscriptions) / sizeof(bus->subscriptions[0]); i++) {
            Subscription *sub = &bus->subscriptions[i];
            if (sub->active && sub->type == node->type) {
                /* Check filter if present */
                bool should_handle = true;
                if (sub->filter) {
                    should_handle = sub->filter(node->type, node->data, sub->filter_data);
                }
                
                if (should_handle) {
                    sub->handler(node->type, node->data, sub->user_data);
                }
            }
        }
        
        pthread_mutex_unlock(&bus->sub_mutex);
        
        /* Free event data */
        if (node->free_fn) {
            node->free_fn(node->data);
        }
        dwn_free(node);
        
        processed++;
    }
    
    return processed;
}

void event_bus_set_batch_size(EventBus *bus, uint32_t batch_size)
{
    if (!bus) return;
    bus->batch_size = batch_size ? batch_size : 100;
}

/* ============================================================================
 * Default Event Bus
 * ============================================================================ */

static EventBus *g_default_bus = NULL;

EventBus* event_bus_default(void)
{
    return g_default_bus;
}

bool event_bus_init_default(void)
{
    if (g_default_bus) return true;
    g_default_bus = event_bus_create();
    return g_default_bus != NULL;
}

void event_bus_shutdown_default(void)
{
    if (g_default_bus) {
        event_bus_destroy(g_default_bus);
        g_default_bus = NULL;
    }
}
