// virtual_interface.hpp - Virtual Interface for Network Communication
// Copyright (C) 2025 DcruBro
// Distributed under the terms of the GNU General Public License, either version 2 only or version 3. See LICENSES/ for details.

#pragma once

#include <stdexcept>
#include <cstring>
#include <cerrno>
#include <vector>
#include <iostream>
#include <columnlynx/common/utils.hpp>

#if defined(__linux__)
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <linux/if.h>
    #include <linux/if_tun.h>
    #include <arpa/inet.h>
#elif defined(__APPLE__)
    #include <sys/socket.h>
    #include <sys/kern_control.h>
    #include <sys/sys_domain.h>
    #include <net/if_utun.h>
    #include <sys/ioctl.h>
    #include <unistd.h>
    #include <arpa/inet.h>
#elif defined(_WIN32)
    #include <windows.h>
    #include <ws2tcpip.h>
    #include <winsock2.h>
    #include <wintun/wintun.h>
    #pragma comment(lib, "advapi32.lib")
#endif

namespace ColumnLynx::Net {
    class VirtualInterface {
        public:
            explicit VirtualInterface(const std::string& ifName);
            ~VirtualInterface();

            bool configureIP(uint32_t clientIP, uint32_t serverIP,
                             uint8_t prefixLen, uint16_t mtu);

            std::vector<uint8_t> readPacket();
            void writePacket(const std::vector<uint8_t>& packet);

            const std::string& getName() const;
            int getFd() const; // For ASIO integration (on POSIX)

            static inline std::string ipv4ToString(uint32_t ip) {
                struct in_addr addr;
                addr.s_addr = htonl(ip);
            
                char buf[INET_ADDRSTRLEN];
                if (!inet_ntop(AF_INET, &addr, buf, sizeof(buf)))
                    return "0.0.0.0";
            
                return std::string(buf);
            }

            static inline uint32_t prefixLengthToNetmask(uint8_t prefixLen) {
                if (prefixLen == 0) return 0;
                uint32_t mask = (0xFFFFFFFF << (32 - prefixLen)) & 0xFFFFFFFF;
                return htonl(mask);    // convert to network byte order
            }

        private:
            bool mApplyLinuxIP(uint32_t clientIP, uint32_t serverIP, uint8_t prefixLen, uint16_t mtu);
            bool mApplyMacOSIP(uint32_t clientIP, uint32_t serverIP, uint8_t prefixLen, uint16_t mtu);
            bool mApplyWindowsIP(uint32_t clientIP, uint32_t serverIP, uint8_t prefixLen, uint16_t mtu);

            std::string mIfName;
            int mFd;           // POSIX
        #if defined(_WIN32)
            HANDLE mHandle;    // Windows
        #endif
    };
}