#ifndef EL_PLATFORM_WIN_H
#define EL_PLATFORM_WIN_H
/*
 * el_platform_win.h — Windows OS-boundary shim for el_runtime.c.
 *
 * Branch: feat/windows-el-runtime. Included ONLY when _WIN32 is defined; the POSIX build is
 * untouched. Goal: let el_runtime.c (a BSD-sockets / dlfcn / fork host) compile and link with
 * mingw-w64 into a native neuron.exe, with no behavioural change to the Linux/macOS build.
 *
 * What it maps:
 *   - sockets  : winsock2 (same call names: socket/bind/listen/accept/recv/send/setsockopt).
 *                Sockets close with closesocket() (see el_closesocket), and the stack must be
 *                started once with WSAStartup — done automatically via a load-time constructor.
 *   - dlsym    : el_runtime.c uses dlsym(RTLD_DEFAULT, name) to resolve callback/tool symbols
 *                exported by the main module. Windows equivalent: GetProcAddress on the process
 *                module. Link the soul with -Wl,--export-all-symbols so the symbols are findable.
 *   - popen    : mapped to _popen/_pclose.
 *   - threads  : UNCHANGED. mingw-w64 ships winpthreads, so <pthread.h> + -lpthread just work.
 */

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <io.h>
#include <process.h>

/* Portable headers mingw-w64 provides (verified present). */
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>   /* strcasecmp */
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>  /* mingw-w64 provides gettimeofday here */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <pthread.h>

/* ── socket close ─────────────────────────────────────────────────────────── */
/* Winsock closes sockets with closesocket(), not close() (close() is for file fds). The POSIX
   build defines the same helper as close() so the call sites are identical across platforms. */
static inline int el_closesocket(SOCKET s) { return closesocket(s); }

/* ── winsock init (once, at load) ─────────────────────────────────────────── */
static void el__win_net_init(void) {
    static int inited = 0;
    if (!inited) { WSADATA w; WSAStartup(MAKEWORD(2, 2), &w); inited = 1; }
}
__attribute__((constructor)) static void el__win_ctor(void) { el__win_net_init(); }

/* ── dlsym → GetProcAddress ───────────────────────────────────────────────── */
#ifndef RTLD_DEFAULT
#define RTLD_DEFAULT ((void*)0)
#endif
static inline void* el_win_dlsym(void* handle, const char* name) {
    (void)handle;
    return (void*)(uintptr_t)GetProcAddress(GetModuleHandleA(NULL), name);
}
#define dlsym(h, n) el_win_dlsym((h), (n))

/* ── popen / pclose ───────────────────────────────────────────────────────── */
#define popen  _popen
#define pclose _pclose

/* ── misc POSIX → Win32 shims ─────────────────────────────────────────────── */
#include <direct.h>            /* _mkdir */
#define mkdir(path, mode) _mkdir(path)     /* POSIX mkdir(path,mode) → _mkdir(path) */
#define timegm _mkgmtime                   /* UTC tm → time_t */

/* setenv/unsetenv: not in the Windows CRT; map to _putenv_s / SetEnvironmentVariable. */
static inline int setenv(const char* name, const char* value, int overwrite) {
    (void)overwrite;
    return _putenv_s(name, value ? value : "");
}
static inline int unsetenv(const char* name) {
    /* _putenv_s(name, "") sets VAR="" rather than removing it.
     * SetEnvironmentVariableA(name, NULL) truly deletes it from the Win32
     * env block; then we sync the CRT cache with _putenv("NAME=").       */
    SetEnvironmentVariableA(name, NULL);
    size_t len = strlen(name);
    char *buf = (char*)malloc(len + 2);
    if (!buf) return -1;
    memcpy(buf, name, len);
    buf[len] = '=';
    buf[len + 1] = '\0';
    _putenv(buf);
    free(buf);
    return 0;
}

/* nanosleep — not available in MSVC/UCRT; approximate with Sleep(). */
static inline int el_nanosleep(const struct timespec *req, struct timespec *rem) {
    (void)rem;
    DWORD ms = (DWORD)((req->tv_sec * 1000ULL) + (req->tv_nsec / 1000000ULL));
    Sleep(ms ? ms : 1);
    return 0;
}
#define nanosleep(req, rem) el_nanosleep((req), (rem))

/* localtime_r/gmtime_r: Windows offers localtime_s/gmtime_s with reversed arg order. */
static inline struct tm* localtime_r(const time_t* t, struct tm* out) {
    return localtime_s(out, t) == 0 ? out : (struct tm*)0;
}
static inline struct tm* gmtime_r(const time_t* t, struct tm* out) {
    return gmtime_s(out, t) == 0 ? out : (struct tm*)0;
}

#endif /* EL_PLATFORM_WIN_H */
