/**
 * @file ntsync.c
 * @brief NTSync userspace library for Windows synchronization primitives
 *
 * MVGAL - Multi-Vendor GPU Aggregation Layer for Linux
 *
 * SPDX-License-Identifier: MIT
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/types.h>
#include <linux/ioctl.h>

#include "ntsync.h"

/* IOCTL definitions matching kernel */
#define MVGAL_NTSYNC_IOC_MAGIC 'N'
#define MVGAL_NTSYNC_IOC_CREATE_EVENT     _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 1, struct mvgal_ntsync_create_args)
#define MVGAL_NTSYNC_IOC_OPEN_EVENT       _IOW(MVGAL_NTSYNC_IOC_MAGIC, 2, struct mvgal_ntsync_open_args)
#define MVGAL_NTSYNC_IOC_WAIT_EVENT       _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 3, struct mvgal_ntsync_wait_args)
#define MVGAL_NTSYNC_IOC_SIGNAL_EVENT     _IOW(MVGAL_NTSYNC_IOC_MAGIC, 4, struct mvgal_ntsync_signal_args)
#define MVGAL_NTSYNC_IOC_CREATE_SEMAPHORE _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 5, struct mvgal_ntsync_create_args)
#define MVGAL_NTSYNC_IOC_WAIT_SEMAPHORE    _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 6, struct mvgal_ntsync_wait_args)
#define MVGAL_NTSYNC_IOC_RELEASE_SEMAPHORE  _IOW(MVGAL_NTSYNC_IOC_MAGIC, 7, struct mvgal_ntsync_signal_args)
#define MVGAL_NTSYNC_IOC_CREATE_MUTEX     _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 8, struct mvgal_ntsync_create_args)
#define MVGAL_NTSYNC_IOC_ACQUIRE_MUTEX      _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 9, struct mvgal_ntsync_wait_args)
#define MVGAL_NTSYNC_IOC_RELEASE_MUTEX      _IOW(MVGAL_NTSYNC_IOC_MAGIC, 10, struct mvgal_ntsync_signal_args)
#define MVGAL_NTSYNC_IOC_CREATE_FENCE     _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 13, struct mvgal_ntsync_fence_args)
#define MVGAL_NTSYNC_IOC_WAIT_FENCE       _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 14, struct mvgal_ntsync_wait_args)
#define MVGAL_NTSYNC_IOC_SIGNAL_FENCE     _IOW(MVGAL_NTSYNC_IOC_MAGIC, 15, struct mvgal_ntsync_signal_args)

/* Object types */
#define MVGAL_NTSYNC_TYPE_EVENT       1
#define MVGAL_NTSYNC_TYPE_SEMAPHORE   2
#define MVGAL_NTSYNC_TYPE_MUTEX       3
#define MVGAL_NTSYNC_TYPE_FENCE       5

/* Object flags */
#define MVGAL_NTSYNC_FLAG_AUTO_RESET   (1 << 0)
#define MVGAL_NTSYNC_FLAG_SIGNALED     (1 << 1)
#define MVGAL_NTSYNC_FLAG_SHARED       (1 << 2)

struct mvgal_ntsync_create_args {
    __u32 type;
    __u32 flags;
    __u32 initial;
    __u32 fd;
};

struct mvgal_ntsync_open_args {
    __u32 fd;
};

struct mvgal_ntsync_wait_args {
    __u32 fd;
    __u32 status;
    __u64 timeout_ns;
};

struct mvgal_ntsync_signal_args {
    __u32 fd;
    __u32 count;
    __u32 alertable;
};

struct mvgal_ntsync_fence_args {
    __u32 fd;
    __u32 gpu_index;
    __u32 flags;
    __u64 timeout_ns;
};

static int g_ntsync_fd = -1;

int ntsync_init(void) {
    if (g_ntsync_fd >= 0) {
        return 0;  /* Already initialized */
    }
    
    g_ntsync_fd = open("/dev/mvgal_ntsync", O_RDWR);
    if (g_ntsync_fd < 0) {
        fprintf(stderr, "MVGAL: Failed to open /dev/mvgal_ntsync: %s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

void ntsync_fini(void) {
    if (g_ntsync_fd >= 0) {
        close(g_ntsync_fd);
        g_ntsync_fd = -1;
    }
}

int ntsync_create_event(int *handle, int initial_state, int auto_reset) {
    struct mvgal_ntsync_create_args args = {0};
    int fd;
    int ret;
    
    if (g_ntsync_fd < 0) {
        return -1;
    }
    
    args.type = MVGAL_NTSYNC_TYPE_EVENT;
    args.flags = 0;
    if (initial_state) {
        args.flags |= MVGAL_NTSYNC_FLAG_SIGNALED;
    }
    if (auto_reset) {
        args.flags |= MVGAL_NTSYNC_FLAG_AUTO_RESET;
    }
    
    ret = ioctl(g_ntsync_fd, MVGAL_NTSYNC_IOC_CREATE_EVENT, &args);
    if (ret < 0) {
        return -1;
    }
    
    fd = args.fd;
    *handle = fd;
    return 0;
}

int ntsync_create_semaphore(int *handle, int initial_count, int max_count) {
    struct mvgal_ntsync_create_args args = {0};
    int ret;
    
    if (g_ntsync_fd < 0) {
        return -1;
    }
    
    args.type = MVGAL_NTSYNC_TYPE_SEMAPHORE;
    args.flags = 0;
    args.initial = initial_count;
    
    ret = ioctl(g_ntsync_fd, MVGAL_NTSYNC_IOC_CREATE_SEMAPHORE, &args);
    if (ret < 0) {
        return -1;
    }
    
    *handle = args.fd;
    return 0;
}

int ntsync_create_mutex(int *handle) {
    struct mvgal_ntsync_create_args args = {0};
    int ret;
    
    if (g_ntsync_fd < 0) {
        return -1;
    }
    
    args.type = MVGAL_NTSYNC_TYPE_MUTEX;
    
    ret = ioctl(g_ntsync_fd, MVGAL_NTSYNC_IOC_CREATE_MUTEX, &args);
    if (ret < 0) {
        return -1;
    }
    
    *handle = args.fd;
    return 0;
}

int ntsync_wait(int handle, uint64_t timeout_ns) {
    struct mvgal_ntsync_wait_args args = {0};
    int ret;
    
    if (g_ntsync_fd < 0) {
        return -1;
    }
    
    args.fd = handle;
    args.timeout_ns = timeout_ns;
    
    ret = ioctl(g_ntsync_fd, MVGAL_NTSYNC_IOC_WAIT_EVENT, &args);
    if (ret < 0) {
        return -1;
    }
    
    if (args.status == 0) {
        return 0;  /* Signaled */
    } else if (args.status == 1) {
        return -1;  /* Timeout */
    }
    return -2;  /* Error */
}

int ntsync_signal(int handle) {
    struct mvgal_ntsync_signal_args args = {0};
    int ret;
    
    if (g_ntsync_fd < 0) {
        return -1;
    }
    
    args.fd = handle;
    args.count = 1;
    
    ret = ioctl(g_ntsync_fd, MVGAL_NTSYNC_IOC_SIGNAL_EVENT, &args);
    return ret < 0 ? -1 : 0;
}

int ntsync_release_semaphore(int handle, int count) {
    struct mvgal_ntsync_signal_args args = {0};
    int ret;
    
    if (g_ntsync_fd < 0) {
        return -1;
    }
    
    args.fd = handle;
    args.count = count;
    
    ret = ioctl(g_ntsync_fd, MVGAL_NTSYNC_IOC_RELEASE_SEMAPHORE, &args);
    return ret < 0 ? -1 : 0;
}

int ntsync_release_mutex(int handle) {
    struct mvgal_ntsync_signal_args args = {0};
    int ret;
    
    if (g_ntsync_fd < 0) {
        return -1;
    }
    
    args.fd = handle;
    
    ret = ioctl(g_ntsync_fd, MVGAL_NTSYNC_IOC_RELEASE_MUTEX, &args);
    return ret < 0 ? -1 : 0;
}

int ntsync_create_fence(int *handle, uint32_t gpu_index) {
    struct mvgal_ntsync_fence_args args = {0};
    int ret;
    
    if (g_ntsync_fd < 0) {
        return -1;
    }
    
    args.gpu_index = gpu_index;
    args.flags = 0;
    
    ret = ioctl(g_ntsync_fd, MVGAL_NTSYNC_IOC_CREATE_FENCE, &args);
    if (ret < 0) {
        return -1;
    }
    
    *handle = args.fd;
    return 0;
}

int ntsync_close(int handle) {
    struct mvgal_ntsync_open_args args = {0};
    int ret;
    
    if (g_ntsync_fd < 0) {
        return -1;
    }
    
    args.fd = handle;
    ret = ioctl(g_ntsync_fd, MVGAL_NTSYNC_IOC_OPEN_EVENT, &args);
    return ret < 0 ? -1 : 0;
}

/* ============================================================================
 * Fallback fsync-style implementation (when kernel ntsync unavailable)
 * ============================================================================ */

#ifdef MVGAL_NTSYNC_FALLBACK

#include <pthread.h>
#include <stdatomic.h>

/* Simple event implementation using eventfd */
struct ntsync_fallback_event {
    int fd;
    atomic_int signaled;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
};

static struct ntsync_fallback_event *fallback_events[1024];
static pthread_mutex_t fallback_lock = PTHREAD_MUTEX_INITIALIZER;

int ntsync_wait_fallback(int handle, uint64_t timeout_ns) {
    struct ntsync_fallback_event *evt = NULL;
    
    pthread_mutex_lock(&fallback_lock);
    if (handle >= 0 && handle < 1024) {
        evt = fallback_events[handle];
    }
    pthread_mutex_unlock(&fallback_lock);
    
    if (!evt) {
        return -1;
    }
    
    /* Try to read from eventfd first (fast path) */
    uint64_t val;
    ssize_t n = read(evt->fd, &val, sizeof(val));
    if (n == sizeof(val)) {
        return 0;
    }
    
    /* Fall back to condition variable */
    struct timespec ts;
    if (timeout_ns > 0) {
        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_sec += timeout_ns / 1000000000;
        ts.tv_nsec += timeout_ns % 1000000000;
        if (ts.tv_nsec >= 1000000000) {
            ts.tv_sec++;
            ts.tv_nsec -= 1000000000;
        }
    }
    
    pthread_mutex_lock(&evt->mutex);
    while (!evt->signaled) {
        if (timeout_ns == 0) {
            pthread_cond_wait(&evt->cond, &evt->mutex);
        } else {
            if (pthread_cond_timedwait(&evt->cond, &evt->mutex, &ts) != 0) {
                pthread_mutex_unlock(&evt->mutex);
                return -1; /* Timeout */
            }
            break;
        }
    }
    if (evt->cond || (evt->flags & MVGAL_NTSYNC_FLAG_AUTO_RESET)) {
        evt->signaled = 0;
    }
    pthread_mutex_unlock(&evt->mutex);
    
    return 0;
}

#endif /* MVGAL_NTSYNC_FALLBACK */