/*******************************************************************************
Copyright (c) 2014, Jan Koester jan.koester@gmx.net
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include <iostream>
#include <algorithm>
#include <chrono>
#include <memory>
#include <vector>
#include <atomic>
#include <mutex>
#include <map>

#ifdef _WIN32
    #include <winsock2.h>
    #include <windows.h>
    #include <process.h>
    #include <io.h>
    #include <cstdio>

    #define close(fd) closesocket(fd)
    #define GET_LAST_ERROR() WSAGetLastError()

    typedef HANDLE pthread_t;
    struct pthread_attr_t {};
    #define pthread_attr_init(a) 0
    #define pthread_attr_destroy(a)
    #define pthread_join(t, r) (WaitForSingleObject(t, INFINITE) == WAIT_OBJECT_0 ? 0 : -1)

    inline void strerror_r_netplus(int errnum, char *buf, size_t buflen) {
        if (errnum != 0) {
            std::snprintf(buf, buflen, "WinSock Error %d", errnum);
        } else {
            std::strncpy(buf, "No error", buflen);
            buf[buflen - 1] = '\0';
        }
    }
#else
    #include <unistd.h>
    #include <signal.h>
    #include <sys/select.h>
    #include <fcntl.h>
    #include <pthread.h>
#endif

#include <stdlib.h>
#include <stdint.h>
#include <cstring>
#include <errno.h>

#include "config.h"
#include "socket.h"
#include "exception.h"
#include "eventapi.h"
#include "connection.h"

#ifndef _WIN32
#include "../posix/error.h"
#endif

// Internal event flags for select-based implementation
#define SELECT_EV_READ   1
#define SELECT_EV_WRITE  2
#define SELECT_EV_ERROR  4

namespace netplus {

    static std::vector<socket*> SSOCKETS;
    static std::map<int, std::shared_ptr<con>> CONNECTIONS;

    static std::mutex POLL_HANDLER_MUTEX;

    // ------------------------------------------------------------
    // Utility: safe close (no deadlock, no double close)
    // ------------------------------------------------------------
    static void safeCloseSocket(std::shared_ptr<con>& c) {
        if (!c) return;
        if (!c->slots[0].csock) return;

        try {
            c->slots[0].csock->close();
        } catch (...) {}
    }

    // ------------------------------------------------------------
    // pollapi interface
    // ------------------------------------------------------------
    class pollapi {
    public:
        pollapi(eventapi* evcon, int timeout) : evconnection(evcon), _Timeout(timeout) {}
        virtual ~pollapi() = default;

        enum EventHandlerStatus { EVWAIT=0, EVIN=1, EVOUT=2, EVUP=3, EVERR=4, EVCON=-1 };

        virtual void initEventHandler()=0;
        virtual const char* getpolltype()=0;
        virtual int pollState(int pos)=0;
        virtual void setpollEventsFd(int fd, int events)=0;

        virtual int waitEventHandler(int timeout)=0;
        virtual void ConnectEventHandler(int tid, ULONG_PTR args)=0;
        virtual void IoEventHandler(int fd, int events, const int tid, ULONG_PTR args)=0;
        virtual void CloseEventHandler(int fd, int tid, ULONG_PTR args)=0;

    protected:
        eventapi* evconnection;
        int _Timeout;
    };

    // ------------------------------------------------------------
    // RAII Rearm for select (update master FD sets on scope exit)
    // ------------------------------------------------------------
    class SelectArmGuard {
    public:
        SelectArmGuard(int fd, std::shared_ptr<con> c, pollapi* pptr)
            : _fd(fd), _con(std::move(c)), _pptr(pptr) {}

        ~SelectArmGuard() {
            if (!_armed) return;
            if (!_con || !_con->slots[0].csock) return;

            int next = 0;

            if (!_con->slots[0].csock->getHandshakeDone()) {
                next |= SELECT_EV_READ;
                if (_con->slots[0].csock->hasPendingWrite())
                    next |= SELECT_EV_WRITE;
            } else {
                next |= SELECT_EV_READ;
                if (!_con->SendData.empty() || _con->slots[0].csock->hasPendingWrite()
                    || _con->slots[0].ResponsePending.exchange(false, std::memory_order_relaxed))
                    next |= SELECT_EV_WRITE;
                if (_con->slots[0].csock->hasBufferedData())
                    next |= SELECT_EV_WRITE;
            }

            try {
                _pptr->setpollEventsFd(_fd, next);
            } catch (...) {}
        }

        void disarm() { _armed = false; }

    private:
        bool _armed = true;
        int _fd;
        std::shared_ptr<con> _con;
        pollapi* _pptr;
    };

    // ------------------------------------------------------------
    // poll implementation (select-based)
    // ------------------------------------------------------------
    class poll : public pollapi {
    public:
        poll(socket* serversocket, eventapi* evcon, int /*pollfd*/, int timeout)
            : pollapi(evcon, timeout),
              _maxFD(0),
              _ServerSocket(serversocket)
        {
            FD_ZERO(&_masterReadSet);
            FD_ZERO(&_masterWriteSet);
            FD_ZERO(&_masterExceptSet);

            {
                std::lock_guard<std::mutex> lk(POLL_HANDLER_MUTEX);
                SSOCKETS.emplace_back(_ServerSocket);
            }
        }

        const char* getpolltype() override { return "SELECT"; }

        void initEventHandler() override {
            FD_SET(_ServerSocket->fd(), &_masterReadSet);
            _maxFD = std::max(_maxFD, _ServerSocket->fd());
        }

        int getEventFlags(int pos) {
            if (pos >= (int)_ReadyEvents.size()) return 0;
            return _ReadyEvents[pos].second;
        }

        int getEventFd(int pos) {
            if (pos >= (int)_ReadyEvents.size()) return -1;
            return _ReadyEvents[pos].first;
        }

        std::shared_ptr<con> getConByFd(int fd) {
            std::lock_guard<std::mutex> lk(POLL_HANDLER_MUTEX);
            auto it = CONNECTIONS.find(fd);
            if (it == CONNECTIONS.end()) return nullptr;
            return it->second;
        }

        void setpollEventsFd(int fd, int events) override {
            FD_CLR(fd, &_masterReadSet);
            FD_CLR(fd, &_masterWriteSet);

            if (events & SELECT_EV_READ) {
                FD_SET(fd, &_masterReadSet);
            }
            if (events & SELECT_EV_WRITE) {
                FD_SET(fd, &_masterWriteSet);
            }
        }

        int pollState(int pos) override {
            if (pos >= (int)_ReadyEvents.size()) return -1;
            if (_ReadyEvents[pos].first == _ServerSocket->fd())
                return EVCON;
            return _ReadyEvents[pos].second;
        }

        int waitEventHandler(int timeout) override {
            _ReadyEvents.clear();

            _currentReadSet   = _masterReadSet;
            _currentWriteSet  = _masterWriteSet;
            _currentExceptSet = _masterExceptSet;

            struct timeval tv;
            tv.tv_sec  = timeout / 1000;
            tv.tv_usec = (timeout % 1000) * 1000;

            int nready;
            #ifdef _WIN32
            nready = select(0, &_currentReadSet, &_currentWriteSet, &_currentExceptSet, &tv);
            #else
            nready = select(_maxFD + 1, &_currentReadSet, &_currentWriteSet, &_currentExceptSet, &tv);
            #endif

            if (nready < 0) {
                #ifdef _WIN32
                int error_code = GET_LAST_ERROR();
                if (error_code == WSAEINTR) return 0;
                #else
                int error_code = errno;
                if (error_code == EINTR) return 0;
                #endif

                NetException e;
                char str[255];
                strerror_r_netplus(error_code, str, 255);
                e[NetException::Error] << "waitEventHandler: select failure: " << str;
                throw e;
            }

            if (nready == 0) return 0;

            for (int fd = 0; fd <= _maxFD; ++fd) {
                int event_flags = 0;

                if (FD_ISSET(fd, &_currentReadSet) ||
                    FD_ISSET(fd, &_currentWriteSet) ||
                    FD_ISSET(fd, &_currentExceptSet)) {

                    if (FD_ISSET(fd, &_currentReadSet)) {
                        if (fd == _ServerSocket->fd()) {
                            _ReadyEvents.push_back({fd, EVCON});
                            continue;
                        }
                        event_flags |= SELECT_EV_READ;
                    }

                    if (FD_ISSET(fd, &_currentWriteSet)) {
                        event_flags |= SELECT_EV_WRITE;
                    }

                    if (FD_ISSET(fd, &_currentExceptSet)) {
                        event_flags |= SELECT_EV_ERROR;
                    }
                }

                if (event_flags != 0) {
                    _ReadyEvents.push_back({fd, event_flags});
                }
            }

            return (int)_ReadyEvents.size();
        }

        // ------------------------------------------------------------
        // Accept new connection
        // ------------------------------------------------------------
        void ConnectEventHandler(const int tid, ULONG_PTR args) override {
            try {
                std::shared_ptr<con> ccon;
                evconnection->CreateConnection(ccon);
                if (!ccon) return;

                if (_ServerSocket->_Type == sockettype::TCP)
                    ccon->slots[0].csock = std::make_unique<tcp>(-1);
                else if (_ServerSocket->_Type == sockettype::SSL) {
                    ssl* srv = static_cast<ssl*>(_ServerSocket);
                    ccon->slots[0].csock = std::make_unique<ssl>(srv->_tls.cert_map, -1);
                } else if (_ServerSocket->_Type == sockettype::QUIC) {
                    do {
                        std::unique_ptr<socket> quic_csock;
                        _ServerSocket->accept(quic_csock, true);
                        fd_set readfds;
                        FD_ZERO(&readfds);
                        FD_SET(_ServerSocket->fd(), &readfds);
                        timeval tv = {0, 0};
                        if (select(_ServerSocket->fd() + 1, &readfds, nullptr, nullptr, &tv) <= 0)
                            break;
                    } while (true);
                    return;
                }

                _ServerSocket->accept(ccon->slots[0].csock, true);
                if (!ccon || !ccon->slots[0].csock) return;

                ccon->slots[0].csock->setNonBlock();
                int fd = ccon->slots[0].csock->fd();

                {
                    std::lock_guard<std::mutex> lk(POLL_HANDLER_MUTEX);
                    CONNECTIONS.emplace(fd, ccon);
                }

                // Add to select master sets
                FD_SET(fd, &_masterReadSet);
                _maxFD = std::max(_maxFD, fd);

                evconnection->ConnectEvent(*ccon, tid, args);

                // If ConnectEvent queued data, also watch for write readiness
                if (!ccon->SendData.empty() || ccon->slots[0].csock->hasPendingWrite()) {
                    FD_SET(fd, &_masterWriteSet);
                }

            } catch (NetException& e) {
                if (e.getErrorType() == NetException::Note)
                    return;
                throw;
            }
        }

        // ------------------------------------------------------------
        // IO handling (unified for TCP/SSL/QUIC)
        // ------------------------------------------------------------
        void IoEventHandler(int fd, int events, const int tid, ULONG_PTR args) override {
            std::shared_ptr<con> c = getConByFd(fd);
            if (!c || !c->slots[0].csock) {
                return;
            }

            SelectArmGuard rearm(fd, c, this);
            bool needClose = false;

            {
                std::lock_guard<std::recursive_mutex> lk(c->event_mutex);
                if (!c->slots[0].csock) return;

                try {
                    // Error condition
                    if (events & SELECT_EV_ERROR) {
                        needClose = true;
                    }

                    if (!needClose) {
                        // Step 1: Read - buffer transport data
                        if (events & SELECT_EV_READ) {
                            buffer buf(BLOCKSIZE);
                            try {
                                c->slots[0].csock->prime_recv(buf, 0);
                            } catch (NetException& e) {
                                if (e.getErrorType() != NetException::Note) throw;
                            }
                        }

                        // Step 2: Handshake (if not done)
                        while (!c->slots[0].csock->getHandshakeDone() && !needClose) {
                            if (c->slots[0].csock->hasPendingWrite()) {
                                try {
                                    c->slots[0].csock->flush_out();
                                } catch (NetException& e) {
                                    if (e.getErrorType() == NetException::Note) {
                                        rearm.disarm();
                                        setpollEventsFd(fd, SELECT_EV_WRITE);
                                        return;
                                    }
                                    throw;
                                }
                            }

                            try {
                                c->slots[0].csock->handshake_after_accept();
                            } catch (NetException& e) {
                                if (e.getErrorType() != NetException::Note) throw;
                                break;
                            }
                        }

                        // Re-arm if handshake still in progress
                        if (!c->slots[0].csock->getHandshakeDone()) {
                            int ev = SELECT_EV_READ;
                            if (c->slots[0].csock->hasPendingWrite()) ev |= SELECT_EV_WRITE;
                            rearm.disarm();
                            setpollEventsFd(fd, ev);
                            return;
                        }

                        // Post-handshake framing callback (e.g. HTTP/2 SETTINGS)
                        if (auto *sslsock = dynamic_cast<ssl*>(c->slots[0].csock.get())) {
                            const auto &fcb = sslsock->getFramingCallback();
                            if (fcb && c->SendData.empty()) {
                                std::string init = fcb(sslsock->getSelectedAlpn(), 0, "", "");
                                if (!init.empty()) {
                                    c->SendData.append(init.data(), init.size());
                                }
                            }
                        }

                        // Step 3: Application data (handshake done)
                        bool hasAppData = (events & SELECT_EV_READ) ||
                                          c->slots[0].csock->hasBufferedData();
                        while (hasAppData && c->slots[0].csock->getHandshakeDone() && !needClose) {
                            buffer buf(BLOCKSIZE);
                            size_t rcv = 0;
                            bool wouldBlock = false;

                            try {
                                rcv = c->slots[0].csock->recvData(buf, 0);
                            } catch (NetException& e) {
                                if (e.getErrorType() == NetException::Note) {
                                    wouldBlock = true;
                                } else {
                                    throw;
                                }
                            }

                            if (!wouldBlock && rcv == 0) {
                                needClose = true;
                            } else if (rcv > 0) {
                                c->RecvData.append(buf.data.buf, rcv);
                                evconnection->RequestEvent(*c, tid, args);
                            }

                            if (wouldBlock || rcv == 0 || !c->slots[0].csock->hasBufferedData())
                                break;
                        }

                        // Step 4: Flush and send
                        if ((!c->SendData.empty() || c->slots[0].csock->hasPendingWrite()) && !needClose) {
                            if (c->slots[0].csock->hasPendingWrite()) {
                                try {
                                    c->slots[0].csock->flush_out();
                                } catch (NetException& e) {
                                    if (e.getErrorType() == NetException::Note) {
                                        rearm.disarm();
                                        setpollEventsFd(fd, SELECT_EV_WRITE);
                                        return;
                                    }
                                    throw;
                                }
                            }

                            if (!c->SendData.empty()) {
                                size_t sendlen = std::min((size_t)BLOCKSIZE, c->SendData.size());
                                buffer out(c->SendData.data(), sendlen);

                                size_t consumed = 0;
                                try {
                                    consumed = c->slots[0].csock->sendData(out, 0);
                                    if (c->slots[0].csock->hasPendingWrite())
                                        c->slots[0].csock->flush_out();
                                } catch (NetException& e) {
                                    if (e.getErrorType() == NetException::Note) {
                                        rearm.disarm();
                                        setpollEventsFd(fd, SELECT_EV_WRITE);
                                        return;
                                    }
                                    throw;
                                }

                                if (consumed > 0) {
                                    c->SendData.erase(c->SendData.begin(), c->SendData.begin() + consumed);
                                }
                            }

                            if (c->SendData.empty() && !c->slots[0].csock->hasPendingWrite()) {
                                evconnection->ResponseEvent(*c, tid, args);
                            }
                        }
                    }

                } catch (NetException& e) {
                    if (e.getErrorType() == NetException::Note) {
                        int ev = SELECT_EV_READ;
                        if (c->slots[0].csock->hasPendingWrite() || !c->SendData.empty())
                            ev |= SELECT_EV_WRITE;
                        rearm.disarm();
                        setpollEventsFd(fd, ev);
                        return;
                    }
                    needClose = true;
                } catch (...) {
                    needClose = true;
                }
            } // unlock event_mutex

            if (needClose) {
                rearm.disarm();
                CloseEventHandler(fd, tid, args);
            }
        }

        // ------------------------------------------------------------
        // Close handler (safe + no deadlock)
        // ------------------------------------------------------------
        void CloseEventHandler(int fd, const int tid, ULONG_PTR args) override {
            std::shared_ptr<con> ccon;

            {
                std::lock_guard<std::mutex> lock(POLL_HANDLER_MUTEX);
                auto it = CONNECTIONS.find(fd);
                if (it == CONNECTIONS.end())
                    return;
                ccon = it->second;
                CONNECTIONS.erase(it);
            }

            if (!ccon) return;

            {
                std::lock_guard<std::recursive_mutex> lk(ccon->event_mutex);

                // Remove from select master sets
                FD_CLR(fd, &_masterReadSet);
                FD_CLR(fd, &_masterWriteSet);
                FD_CLR(fd, &_masterExceptSet);

                // close socket
                safeCloseSocket(ccon);
            }

            // Recalculate _maxFD
            {
                std::lock_guard<std::mutex> lk(POLL_HANDLER_MUTEX);
                _maxFD = 0;
                for (auto const& [conn_fd, conn_ptr] : CONNECTIONS) {
                    _maxFD = std::max(_maxFD, conn_fd);
                }
                for (auto const& ssock : SSOCKETS) {
                    _maxFD = std::max(_maxFD, ssock->fd());
                }
            }

            try {
                evconnection->DisconnectEvent(*ccon, tid, args);
            } catch (...) {
                // ignore user callback failures
            }
        }

    private:
        fd_set _masterReadSet;
        fd_set _masterWriteSet;
        fd_set _masterExceptSet;
        fd_set _currentReadSet;
        fd_set _currentWriteSet;
        fd_set _currentExceptSet;
        std::vector<std::pair<int, int>> _ReadyEvents;
        int _maxFD;
        socket* _ServerSocket;
    };

    // ------------------------------------------------------------
    // Thread worker
    // ------------------------------------------------------------
    class EventWorkerArgs {
    public:
        int timeout;
        eventapi* event;
        std::map<int,socket*> ssocket;
    };

    class EventWorker {
    public:
        EventWorker(int tid, ULONG_PTR args, EventWorkerArgs* eargs) {
            std::vector<std::thread> local_threads;

            for(auto& kv : eargs->ssocket){
                local_threads.emplace_back([&, tid, args, kv] {
                    try {
                        poll pollptr(kv.second, eargs->event, kv.first, eargs->timeout);
                        pollptr.initEventHandler();

                        while (event::Running) {
                            try {
                                int wait = pollptr.waitEventHandler(eargs->timeout);

                                if (wait == 0) {
                                    #ifdef _WIN32
                                    Sleep(1);
                                    #else
                                    usleep(1000);
                                    #endif
                                    continue;
                                }

                                for (int i = 0; i < wait; ++i) {
                                    int evtype = pollptr.pollState(i);

                                    if (evtype == pollapi::EVCON) {
                                        pollptr.ConnectEventHandler(tid, args);
                                        continue;
                                    }

                                    int fd = pollptr.getEventFd(i);
                                    int flags = pollptr.getEventFlags(i);

                                    pollptr.IoEventHandler(fd, flags, tid, args);
                                }
                            } catch (NetException& e) {
                                std::cerr << "Poll Error: " << e.what() << std::endl;
                                #ifdef _WIN32
                                Sleep(10);
                                #else
                                usleep(10000);
                                #endif
                            }
                        }
                    } catch (NetException& e) {
                        std::cerr << "Worker thread " << tid << " exited with exception: " << e.what() << std::endl;
                    } catch (...) {
                        std::cerr << "Worker thread " << tid << " exited with unknown exception." << std::endl;
                    }
                });
            }

            for (auto& t : local_threads)
                t.join();
        }
    };

    struct ThreadHelperArgs {
        EventWorkerArgs* eargs;
        ULONG_PTR args;
        int tid;
    };

    #ifdef _WIN32
    DWORD WINAPI worker_wrapper(LPVOID data) {
        ThreadHelperArgs* helper_args = reinterpret_cast<ThreadHelperArgs*>(data);

        try {
            EventWorker(helper_args->tid, helper_args->args, helper_args->eargs);
        } catch (NetException& e) {
            std::cerr << "Thread " << helper_args->tid << " exited with exception: " << e.what() << std::endl;
        } catch (...) {
            std::cerr << "Thread " << helper_args->tid << " exited with unknown exception." << std::endl;
        }

        delete helper_args;
        return 0;
    }
    #else
    static void* worker_wrapper(void* data) {
        ThreadHelperArgs* helper_args = reinterpret_cast<ThreadHelperArgs*>(data);

        try {
            EventWorker(helper_args->tid, helper_args->args, helper_args->eargs);
        } catch (NetException& e) {
            std::cerr << "Thread " << helper_args->tid << " exited with exception: " << e.what() << std::endl;
        } catch (...) {
            std::cerr << "Thread " << helper_args->tid << " exited with unknown exception." << std::endl;
        }

        delete helper_args;
        return nullptr;
    }
    #endif

    // ------------------------------------------------------------
    // eventapi dummies
    // ------------------------------------------------------------
    void eventapi::RequestEvent(con&, const int, ULONG_PTR) {}
    void eventapi::ResponseEvent(con&, const int, ULONG_PTR) {}
    void eventapi::ConnectEvent(con&, const int, ULONG_PTR) {}
    void eventapi::DisconnectEvent(con&, const int, ULONG_PTR) {}

    // ------------------------------------------------------------
    // Signal handler
    // ------------------------------------------------------------
    // ------------------------------------------------------------
    // Signal handler — MUST be async-signal-safe.
    //
    // Acquiring mutexes here deadlocks if the signal is
    // delivered to a thread that already holds the lock.
    // Only set the atomic flag; cleanup happens in
    // runEventloop() after all threads have joined.
    // ------------------------------------------------------------
    #ifndef _WIN32
    static void signalHandler(int signum) {
        if (signum != SIGINT)
            return;
        event::Running = false;
    }
    #endif

    // Called from runEventloop() AFTER all worker threads have
    // joined — safe to acquire any lock.
    static void cleanupConnections() {
        std::vector<socket*> servers;
        std::vector<std::shared_ptr<con>> conns;

        {
            std::lock_guard<std::mutex> lk(POLL_HANDLER_MUTEX);

            servers = SSOCKETS;
            for (auto& kv : CONNECTIONS)
                conns.push_back(kv.second);

            CONNECTIONS.clear();
        }

        for (auto* s : servers) {
            try { s->close(); } catch (...) {}
        }

        for (auto& c : conns) {
            if (!c) continue;
            std::lock_guard<std::recursive_mutex> lk(c->event_mutex);
            safeCloseSocket(c);
        }
    }

    // ------------------------------------------------------------
    // event class
    // ------------------------------------------------------------
    event::event(std::vector<socket*> serversockets, int timeout)
        : _ServerSockets(serversockets), _Timeout(timeout)
    {
        if (_ServerSockets.empty()) {
            NetException e;
            e[NetException::Critical] << "server socket empty!";
            throw e;
        }

        #ifdef _WIN32
        WSADATA wsaData;
        int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (iResult != 0) {
            NetException e;
            e[NetException::Critical] << "WSAStartup failed: " << iResult;
            throw e;
        }
        #endif

        for(auto* s : _ServerSockets) {
            if (!s) continue;
            s->bind();
            s->setNonBlock();
            s->listen();
            s->setTimeout(timeout);
        }

        #ifdef _WIN32
        SYSTEM_INFO sysinfo;
        GetSystemInfo(&sysinfo);
        threads = sysinfo.dwNumberOfProcessors;
        #else
        threads = sysconf(_SC_NPROCESSORS_ONLN);
        #endif
    }

    event::event(const event& src) : _ServerSockets(src._ServerSockets), _Timeout(src._Timeout) {}

    event::~event() {
        #ifdef _WIN32
        WSACleanup();
        #endif
    }

    void event::runEventloop(ULONG_PTR args) {
        #ifndef _WIN32
        signal(SIGINT, signalHandler);
        signal(SIGPIPE, SIG_IGN);
        #endif

        EventWorkerArgs eargs;

    MAINWORKERLOOP:
        // select doesn't have a global poll fd, but we use the map key as an index
        {
            int idx = 0;
            for(auto ss : _ServerSockets) {
                eargs.ssocket[idx++] = ss;
            }
        }

        eargs.timeout = _Timeout;
        eargs.event = this;

        #ifdef _WIN32
        std::vector<HANDLE> threadpool;
        threadpool.reserve(threads);

        for (int i = 0; i < threads; ++i) {
            ThreadHelperArgs* helper_args = new ThreadHelperArgs{ &eargs, args, i };

            HANDLE tid = CreateThread(
                NULL, 0,
                (LPTHREAD_START_ROUTINE)worker_wrapper,
                helper_args, 0, NULL);

            if (tid == NULL) {
                delete helper_args;
                NetException e;
                e[NetException::Critical] << "runEventloop: CreateThread failed: " << GetLastError();
                throw e;
            }
            threadpool.push_back(tid);
        }

        for (auto& t : threadpool)
            WaitForSingleObject(t, INFINITE);

        for (auto& t : threadpool)
            CloseHandle(t);

        // All worker threads have exited — safe to clean up.
        cleanupConnections();

        #else
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE);

        std::vector<pthread_t> threadpool;
        threadpool.reserve(threads);

        for (int i = 0; i < threads; ++i) {
            ThreadHelperArgs* helper_args = new ThreadHelperArgs{ &eargs, args, i };

            pthread_t tid;
            if (pthread_create(&tid, &attr, worker_wrapper, helper_args) != 0) {
                delete helper_args;
                NetException e;
                e[NetException::Critical] << "runEventloop: pthread_create failed";
                throw e;
            }
            threadpool.push_back(tid);
        }

        pthread_attr_destroy(&attr);

        for (auto& t : threadpool)
            pthread_join(t, nullptr);

        // All worker threads have exited — safe to clean up.
        cleanupConnections();
        #endif

        if (event::Restart) {
            event::Restart = false;
            goto MAINWORKERLOOP;
        }
    }

    // ================================================================
    // socketwait – select/poll-based fallback implementation
    // ================================================================
#ifdef _WIN32
    socketwait::socketwait() : _handle(-1) {}
    socketwait::~socketwait() {}

    bool socketwait::waitRead(socket &sock, int timeout_ms) {
        SOCKET fd = sock.fd();
        if (fd == INVALID_SOCKET) return false;
        fd_set rset, eset;
        FD_ZERO(&rset); FD_SET(fd, &rset);
        FD_ZERO(&eset); FD_SET(fd, &eset);
        timeval tv_buf;
        timeval* tvp = nullptr;
        if (timeout_ms >= 0) {
            tv_buf.tv_sec  = timeout_ms / 1000;
            tv_buf.tv_usec = (timeout_ms % 1000) * 1000;
            tvp = &tv_buf;
        }
        int r = ::select(0, &rset, nullptr, &eset, tvp);
        if (r == SOCKET_ERROR) {
            NetException e;
            e[NetException::Error] << "socketwait::waitRead select() failed: " << WSAGetLastError();
            throw e;
        }
        return r > 0;
    }

    bool socketwait::waitWrite(socket &sock, int timeout_ms) {
        SOCKET fd = sock.fd();
        if (fd == INVALID_SOCKET) return false;
        fd_set wset, eset;
        FD_ZERO(&wset); FD_SET(fd, &wset);
        FD_ZERO(&eset); FD_SET(fd, &eset);
        timeval tv_buf;
        timeval* tvp = nullptr;
        if (timeout_ms >= 0) {
            tv_buf.tv_sec  = timeout_ms / 1000;
            tv_buf.tv_usec = (timeout_ms % 1000) * 1000;
            tvp = &tv_buf;
        }
        int r = ::select(0, nullptr, &wset, &eset, tvp);
        if (r == SOCKET_ERROR) {
            NetException e;
            e[NetException::Error] << "socketwait::waitWrite select() failed: " << WSAGetLastError();
            throw e;
        }
        return r > 0 && FD_ISSET(fd, &wset);
    }

    int socketwait::waitReadMulti(std::vector<socket*> &sockets,
                                   std::vector<bool> &ready,
                                   int timeout_ms) {
        size_t n = sockets.size();
        ready.assign(n, false);
        if (n == 0) return 0;

        fd_set rset, eset;
        FD_ZERO(&rset); FD_ZERO(&eset);
        for (size_t i = 0; i < n; ++i) {
            if (!sockets[i]) continue;
            SOCKET fd = sockets[i]->fd();
            if (fd == INVALID_SOCKET) continue;
            FD_SET(fd, &rset);
            FD_SET(fd, &eset);
        }
        timeval tv_buf;
        timeval* tvp = nullptr;
        if (timeout_ms >= 0) {
            tv_buf.tv_sec  = timeout_ms / 1000;
            tv_buf.tv_usec = (timeout_ms % 1000) * 1000;
            tvp = &tv_buf;
        }
        int r = ::select(0, &rset, nullptr, &eset, tvp);
        if (r <= 0) return 0;
        int count = 0;
        for (size_t i = 0; i < n; ++i) {
            if (!sockets[i]) continue;
            SOCKET fd = sockets[i]->fd();
            if (fd == INVALID_SOCKET) continue;
            if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &eset)) {
                ready[i] = true;
                ++count;
            }
        }
        return count;
    }

    bool socketwait::isDisconnected(socket &sock) {
        SOCKET fd = sock.fd();
        if (fd == INVALID_SOCKET) return true;
        fd_set rset, eset;
        FD_ZERO(&rset); FD_SET(fd, &rset);
        FD_ZERO(&eset); FD_SET(fd, &eset);
        timeval tv{0, 0};
        int r = ::select(0, &rset, nullptr, &eset, &tv);
        if (r == SOCKET_ERROR) return true;
        if (r == 0) return false;
        if (FD_ISSET(fd, &eset)) return true;
        if (FD_ISSET(fd, &rset)) {
            char tmp;
            int n = ::recv(fd, &tmp, 1, MSG_PEEK);
            if (n == 0 || n == SOCKET_ERROR) return true;
        }
        return false;
    }
#else
    socketwait::socketwait() : _handle(-1) {}
    socketwait::~socketwait() {}

    bool socketwait::waitRead(socket &sock, int timeout_ms) {
        struct pollfd pfd{};
        pfd.fd = sock.fd();
        pfd.events = POLLIN;
        int r;
        do {
            r = ::poll(&pfd, 1, timeout_ms);
        } while (r < 0 && errno == EINTR);
        if (r < 0) {
            NetException e;
            e[NetException::Error] << "socketwait::waitRead poll() failed: " << strerror(errno);
            throw e;
        }
        return r > 0 && (pfd.revents & (POLLIN | POLLHUP | POLLERR));
    }

    bool socketwait::waitWrite(socket &sock, int timeout_ms) {
        struct pollfd pfd{};
        pfd.fd = sock.fd();
        pfd.events = POLLOUT;
        int r;
        do {
            r = ::poll(&pfd, 1, timeout_ms);
        } while (r < 0 && errno == EINTR);
        if (r < 0) {
            NetException e;
            e[NetException::Error] << "socketwait::waitWrite poll() failed: " << strerror(errno);
            throw e;
        }
        return r > 0 && (pfd.revents & POLLOUT);
    }

    int socketwait::waitReadMulti(std::vector<socket*> &sockets,
                                   std::vector<bool> &ready,
                                   int timeout_ms) {
        size_t n = sockets.size();
        ready.assign(n, false);
        if (n == 0) return 0;

        std::vector<struct pollfd> pfds(n);
        for (size_t i = 0; i < n; ++i) {
            pfds[i].fd = sockets[i] ? sockets[i]->fd() : -1;
            pfds[i].events = POLLIN;
            pfds[i].revents = 0;
        }
        int r;
        do {
            r = ::poll(pfds.data(), static_cast<nfds_t>(n), timeout_ms);
        } while (r < 0 && errno == EINTR);
        if (r <= 0) return 0;
        int count = 0;
        for (size_t i = 0; i < n; ++i) {
            if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) {
                ready[i] = true;
                ++count;
            }
        }
        return count;
    }

    bool socketwait::isDisconnected(socket &sock) {
        struct pollfd pfd{};
        pfd.fd = sock.fd();
        pfd.events = POLLIN;
        int r;
        do {
            r = ::poll(&pfd, 1, 0);
        } while (r < 0 && errno == EINTR);
        if (r < 0) return true;
        return r > 0 && (pfd.revents & (POLLHUP | POLLERR));
    }
#endif

} // namespace netplus
