#define _GNU_SOURCE     # required for NI_NUMERICHOST
#include "libkync.h"
#include <cstring-extension.h>
#include <errno.h>
#include <kerr.h>
#include <libkylog.h>
#include <stdio.h>
#include <string.h>
#include <sdkmarcos.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ifaddrs.h>

enum cardspec{
    NCSPEC_ALL,
    NCSPEC_UP,
    NCSPEC_DOWN
};

enum cardstat{
    NCSTAT_LINK_UP,
    NCSTAT_LINK_DOWN,
    NCSTAT_ERROR
};

enum cardcfg{
    NCCFG_MAC,
    NCCFG_IPv4,
    NCCFG_IPv6,
    NCCFG_IPv4_MASK,
    NCCFG_IPv6_MASK
};

#define NC_IPv4_SIZE    16
#define NC_MAC_SIZE     18

static enum cardstat _get_card_stat(const char *nc)
{
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (!sfd)
    {
        klog_err("网卡[%s]状态获取失败：%s\n", nc, strerror(errno));
        return NCSTAT_ERROR;
    }

    struct ifreq stIf;
    strcpy(stIf.ifr_ifrn.ifrn_name, nc);

    if (ioctl(sfd, SIOCGIFFLAGS, &stIf) < 0)
    {
        klog_err("网卡[%s]状态获取失败：%s\n", nc, strerror(errno));
        close (sfd);
        return NCSTAT_ERROR;
    }

    close(sfd);
    if (stIf.ifr_ifru.ifru_flags & IFF_RUNNING)
        return NCSTAT_LINK_UP;

    return NCSTAT_LINK_DOWN;
}

static char **_get_cardlist(enum cardspec spec)
{
    char **cards = NULL;
    size_t cardnum = 0;
#ifdef __linux__
    FILE* fp = fopen("/proc/net/dev", "r");
    ASSERT_NOT_NULL(fp, NULL);
    char buffer[1024];
    while (fgets(buffer, 1024, fp))
    {
        if (strncmp(strskipblank(buffer), "Inter", 5) == 0 || strncmp(strskipblank(buffer), "face", 4) == 0)
            continue;
        int pos = strfirstof(buffer, ':');
        if (pos > 0)
        {
            buffer[pos] = 0;
            strstripblank(buffer);
            if (spec == NCSPEC_UP)
            {
                if (_get_card_stat(buffer) != NCSTAT_LINK_UP)
                    continue;
            }
            else if (spec == NCSPEC_DOWN)
            {
                if (_get_card_stat(buffer) != NCSTAT_LINK_DOWN)
                    continue;
            }
            cardnum ++;
            char **tmp = realloc(cards, sizeof(char*) * cardnum);
            if (!tmp)
            {
                klog_err("内存申请失败：%s\n", strerror(errno));
                goto err_out;
            }
            cards = tmp;
            int index = cardnum - 1;
            cards[index] = malloc(sizeof(char) * (strlen(buffer) + 1));
            if (!cards[index])
            {
                klog_err("内存申请失败：%s\n", strerror(errno));
                goto err_out;
            }
            strcpy(cards[index], buffer);
        }
    }
    char **tmp = realloc(cards, sizeof(char *) * (cardnum + 1));
    if (!tmp)
    {
        klog_err("内存申请失败：%s\n", strerror(errno));
        goto err_out;
    }
    cards = tmp;
    cards[cardnum] = NULL;
    goto out;
#endif  // __linux__
err_out:
#ifdef __linux__
    fclose(fp);
#endif
    while (cardnum)
    {
        free(cards[cardnum - 1]);
        cardnum--;
    }
    SAFE_FREE(cards);
    return NULL;
out:
    fclose(fp);
    return cards;
}

char **_get_nc_cfg(const char *nc, enum cardcfg cfg)
{
    char **res = NULL;
    struct ifreq stIf;
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd < 0)
    {
        klog_err("网卡[%s]信息获取失败：%s\n", nc, strerror(errno));
        return NULL;
    }
    strncpy(stIf.ifr_ifrn.ifrn_name, nc, sizeof(stIf.ifr_ifrn.ifrn_name) - 1);

    switch (cfg)
    {
    case NCCFG_MAC:{
        if (ioctl(sfd, SIOCGIFHWADDR, &stIf) < 0)
        {
            klog_err("网卡[%s]MAC获取失败：%s\n", nc, strerror(errno));
            close(sfd);
            return NULL;
        }
        res = malloc(sizeof(char*));
        if (!res)
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            return NULL;
        }
        res[0] = malloc(sizeof(char) * NC_MAC_SIZE);
        if (!res[0])
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            return NULL;
        }
        snprintf(res[0], NC_MAC_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[0],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[1],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[2],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[3],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[4],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[5]);
    }break;
    case NCCFG_IPv4:{
        if (ioctl(sfd, SIOCGIFADDR, &stIf) < 0)
        {
            klog_err("网卡[%s]IPv4获取失败：%s\n", nc, strerror(errno));
            close(sfd);
            return NULL;
        }
        res = malloc(sizeof(char*));
        if (!res)
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            return NULL;
        }
        res[0] = malloc(sizeof(char) * NC_IPv4_SIZE);
        if (!res[0])
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            return NULL;
        }
        struct sockaddr_in sin;
        memcpy(&sin, &stIf.ifr_ifru.ifru_addr, sizeof(sin));
        snprintf(res[0], NC_IPv4_SIZE, "%s", inet_ntoa(sin.sin_addr));
    }break;
    case NCCFG_IPv6:{
        struct ifaddrs *ifap, *ifa;
        struct sockaddr_in6 *sa;
        char addr[INET6_ADDRSTRLEN] = {0};
        getifaddrs(&ifap);
        for (ifa = ifap; ifa; ifa = ifa->ifa_next)
        {
            if (ifa->ifa_addr->sa_family == AF_INET6 && !strcmp(ifa->ifa_name, nc))
            {
                sa = (struct sockaddr_in6 *)ifa->ifa_addr;
                getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr,
                            sizeof(addr), NULL, 0, NI_NUMERICHOST);
            }
        }

        res = malloc(sizeof(char*));
        if (!res)
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            freeifaddrs(ifap);
            return NULL;
        }
        res[0] = malloc(sizeof(char) * INET6_ADDRSTRLEN);
        if (!res[0])
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            freeifaddrs(ifap);
            return NULL;
        }

        int i = 0;
        while (addr[i] != '%' && addr[i] != '\0')
            i++;
        addr[i] = '\0';
        
        sprintf(res[0], "%s",addr);

        freeifaddrs(ifap);
    }break;
    default:
        break;
    }
    close(sfd);
    return res;
}

char** kdk_nc_get_cardlist()
{
    return _get_cardlist(NCSPEC_ALL);
}

int kdk_nc_is_up(const char *nc)
{
    if (!nc)
        return -1;
    enum cardstat stat = _get_card_stat(nc);
    if (stat == NCSTAT_LINK_UP)
        return 1;
    else if (stat == NCSTAT_LINK_DOWN)
        return 0;
    return -1;
}

char** kdk_nc_get_upcards()
{
    return _get_cardlist(NCSPEC_UP);
}

char* kdk_nc_get_phymac(const char *nc)
{
    if (!nc)
        return NULL;
    
    char **maclist = _get_nc_cfg(nc, NCCFG_MAC);
    if (!maclist)
        return NULL;
    char *mac = maclist[0];
    free(maclist);
    return mac;
}

char* kdk_nc_get_private_ipv4(const char *nc)
{
    if (!nc)
        return NULL;
    
    char **ipv4list = _get_nc_cfg(nc, NCCFG_IPv4);
    if (!ipv4list)
        return NULL;
    char *ipv4 = ipv4list[0];
    free(ipv4list);
    return ipv4;
}

char* kdk_nc_get_private_ipv6(const char *nc)
{
    if (!nc)
        return NULL;

    char **ipv6list = _get_nc_cfg(nc, NCCFG_IPv6);
    if (!ipv6list)
        return NULL;
    char *ipv6 = ipv6list[0];
    free(ipv6list);
    return ipv6;
}

inline void kdk_nc_freeall(char **list)
{
    if (! list)
        return;
    size_t index = 0;
    while (list[index])
    {
        free(list[index]);
        index ++;
    }
    free(list);
}