/*
 * DWN - Desktop Window Manager
 * retoor <retoor@molodetz.nl>
 * Async Context Implementation - Per-Module Async State
 */

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

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

/* Full definition of AsyncContext implementation */
struct AsyncContextImpl {
    char name[32];
    ThreadPool *pool;
    EventBus *event_bus;
    Channel *completion_channel;
    _Atomic int operation_count;
    _Atomic int shutdown_requested;
    pthread_mutex_t mutex;
    int event_fd;
};

typedef struct {
    struct AsyncContextImpl *ctx;
    TaskFunc user_func;
    void *user_data;
    Future *future;
} AsyncWrapperData;

static void async_wrapper_func(void *data, atomic_int *cancelled)
{
    AsyncWrapperData *wrap = data;
    
    /* Execute user's function */
    if (wrap->user_func) {
        wrap->user_func(wrap->user_data, cancelled);
    }
    
    /* Signal completion via channel */
    TaskHandle *task_ptr = dwn_malloc(sizeof(TaskHandle));
    if (task_ptr) {
        *task_ptr = (TaskHandle)wrap;  /* Just use wrapper as identifier */
        channel_try_send(wrap->ctx->completion_channel, task_ptr);
    }
    
    atomic_fetch_sub_explicit(&wrap->ctx->operation_count, 1, ATOMIC_RELAXED);
}

AsyncContext* async_context_create(const char *name)
{
    struct AsyncContextImpl *ctx = dwn_malloc(sizeof(struct AsyncContextImpl));
    if (!ctx) return NULL;
    
    strncpy(ctx->name, name ? name : "unnamed", sizeof(ctx->name) - 1);
    ctx->name[sizeof(ctx->name) - 1] = '\0';
    
    ctx->pool = thread_pool_default();
    ctx->event_bus = event_bus_default();
    ctx->completion_channel = channel_create(64);
    
    if (!ctx->completion_channel) {
        dwn_free(ctx);
        return NULL;
    }
    
    atomic_store_explicit(&ctx->operation_count, 0, ATOMIC_RELEASE);
    atomic_store_explicit(&ctx->shutdown_requested, 0, ATOMIC_RELEASE);
    
    if (pthread_mutex_init(&ctx->mutex, NULL) != 0) {
        channel_destroy(ctx->completion_channel);
        dwn_free(ctx);
        return NULL;
    }
    
    /* We use the channel's eventfd for polling */
    ctx->event_fd = -1;  /* Will use channel's fd via async_get_poll_fd */
    
    return (AsyncContext*)ctx;
}

void async_context_destroy(AsyncContext *ctx)
{
    if (!ctx) return;
    
    struct AsyncContextImpl *impl = (struct AsyncContextImpl*)ctx;
    
    atomic_store_explicit(&impl->shutdown_requested, 1, ATOMIC_RELEASE);
    
    /* Wait for operations to complete */
    int timeout = 100; /* 10 seconds max wait */
    while (atomic_load_explicit(&impl->operation_count, ATOMIC_ACQUIRE) > 0 && timeout > 0) {
        usleep(100000); /* 100ms */
        timeout--;
    }
    
    channel_close(impl->completion_channel);
    channel_destroy(impl->completion_channel);
    pthread_mutex_destroy(&impl->mutex);
    dwn_free(impl);
}

TaskHandle async_submit(AsyncContext *ctx, TaskFunc func, void *user_data,
                        TaskPriority priority)
{
    if (!ctx || !func) return NULL;
    
    struct AsyncContextImpl *impl = (struct AsyncContextImpl*)ctx;
    
    if (atomic_load_explicit(&impl->shutdown_requested, ATOMIC_ACQUIRE)) {
        return NULL;
    }
    
    AsyncWrapperData *wrap = dwn_malloc(sizeof(AsyncWrapperData));
    if (!wrap) return NULL;
    
    wrap->ctx = impl;
    wrap->user_func = func;
    wrap->user_data = user_data;
    wrap->future = NULL;
    
    atomic_fetch_add_explicit(&impl->operation_count, 1, ATOMIC_RELAXED);
    
    TaskHandle task = task_create_with_callback(
        async_wrapper_func, wrap, priority,
        NULL, NULL
    );
    
    if (!task || thread_pool_submit(thread_pool_default(), task) != THREAD_OK) {
        atomic_fetch_sub_explicit(&impl->operation_count, 1, ATOMIC_RELAXED);
        dwn_free(wrap);
        return NULL;
    }
    
    return task;
}

Future* async_submit_future(AsyncContext *ctx, TaskFunc func, void *user_data,
                            TaskPriority priority)
{
    if (!ctx || !func) return NULL;
    
    struct AsyncContextImpl *impl = (struct AsyncContextImpl*)ctx;
    
    Future *f = future_create();
    if (!f) return NULL;
    
    AsyncWrapperData *wrap = dwn_malloc(sizeof(AsyncWrapperData));
    if (!wrap) {
        future_destroy(f);
        return NULL;
    }
    
    wrap->ctx = impl;
    wrap->user_func = func;
    wrap->user_data = user_data;
    wrap->future = f;
    
    atomic_fetch_add_explicit(&impl->operation_count, 1, ATOMIC_RELAXED);
    
    TaskHandle task = task_create(async_wrapper_func, wrap, priority);
    if (!task) {
        atomic_fetch_sub_explicit(&impl->operation_count, 1, ATOMIC_RELAXED);
        dwn_free(wrap);
        future_destroy(f);
        return NULL;
    }
    
    ThreadStatus status = thread_pool_submit(thread_pool_default(), task);
    if (status != THREAD_OK) {
        task_destroy(task);
        atomic_fetch_sub_explicit(&impl->operation_count, 1, ATOMIC_RELAXED);
        dwn_free(wrap);
        future_destroy(f);
        return NULL;
    }
    
    return f;
}

uint32_t async_poll(AsyncContext *ctx)
{
    if (!ctx) return 0;
    
    struct AsyncContextImpl *impl = (struct AsyncContextImpl*)ctx;
    
    uint32_t processed = 0;
    void *data;
    
    while (channel_try_recv(impl->completion_channel, &data) == THREAD_OK) {
        dwn_free(data);
        processed++;
    }
    
    return processed;
}

int async_get_poll_fd(AsyncContext *ctx)
{
    if (!ctx) return -1;
    struct AsyncContextImpl *impl = (struct AsyncContextImpl*)ctx;
    return impl->event_fd;
}
