#include "pch.h"

#include "usrerr.h"
#include <strsafe.h>
#include <tchar.h>
#include <Iphlpapi.h>
#include <iostream>
#include <ws2tcpip.h>
#include <netlistmgr.h>
#include <timeapi.h>

#include "globalcfg.h"
#include "misc.h"
#include "network.h"

#include <netcon.h>
#include <shlwapi.h>

#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "Ws2_32.lib")

static NIC_CONTENT g_NetAdapterInfo[NET_CARD_MAX];

int GetInterfaceIfIndexByIpAddr(const TCHAR *pIpAddr, ULONG *pIfIndex) {
    PIP_ADAPTER_INFO pAdapterInfo;
    DWORD            dwRetVal;
    ULONG            ulOutBufLen;

    if (pIpAddr == nullptr || lstrlen(pIpAddr) == 0) {
        SPDLOG_ERROR(TEXT("Input pIpAddr error: {0}"), pIpAddr);
        return -ERR_INPUT_PARAMS;
    }

    if (pIfIndex == nullptr) {
        SPDLOG_ERROR(TEXT("Input pIfIndex params error"));
        return -ERR_INPUT_PARAMS;
    }

    ulOutBufLen  = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO)));

    if (pAdapterInfo == nullptr) {
        SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo"));
        return -ERR_MALLOC_MEMORY;
    }

    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, ulOutBufLen));
        if (pAdapterInfo == nullptr) {
            SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo\n"));
            return -ERR_MALLOC_MEMORY;
        }
    }

    *pIfIndex = -1;

    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
        while (pAdapter) {
            PIP_ADDR_STRING ipAddressListPointer = &(pAdapter->IpAddressList);

            while (ipAddressListPointer != nullptr) {
                if (StrCmp((ipAddressListPointer->IpAddress).String, pIpAddr) == 0) {
                    *pIfIndex = pAdapter->Index;
                    HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                    return ERR_SUCCESS;
                } else {
                    ipAddressListPointer = ipAddressListPointer->Next;
                }
            }
            pAdapter = pAdapter->Next;
        }
    } else {
        SPDLOG_ERROR(TEXT("GetAdaptersInfo failed with error: {0}\n"), dwRetVal);
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        return -ERR_SYS_CALL;
    }

    if (pAdapterInfo) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
    }

    return -ERR_ITEM_UNEXISTS;
}

int GetInterfaceIfIndexByGUID(const TCHAR *pGUID, int *pIfIndex) {
    PIP_ADAPTER_INFO pAdapterInfo;
    DWORD            dwRetVal;

    if (pGUID == nullptr || lstrlen(pGUID) == 0) {
        SPDLOG_ERROR(TEXT("Input pGUID error: {0}"), pGUID);
        return -ERR_INPUT_PARAMS;
    }

    if (pIfIndex == nullptr) {
        SPDLOG_ERROR(TEXT("Input pIfIndex params error"));
        return -ERR_INPUT_PARAMS;
    }

    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo      = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO)));

    if (pAdapterInfo == nullptr) {
        SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo"));
        return -ERR_MALLOC_MEMORY;
    }

    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, ulOutBufLen));
        if (pAdapterInfo == nullptr) {
            SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo\n"));
            return -ERR_MALLOC_MEMORY;
        }
    }

    *pIfIndex = -1;

    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
        while (pAdapter) {
            if (StrCmp(pAdapter->AdapterName, pGUID) == 0) {
                *pIfIndex = static_cast<int>(pAdapter->Index);
                HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                return ERR_SUCCESS;
            }
            pAdapter = pAdapter->Next;
        }
    } else {
        SPDLOG_ERROR(TEXT("GetAdaptersInfo failed with error: {0}\n"), dwRetVal);
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        return -ERR_SYS_CALL;
    }

    if (pAdapterInfo) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
    }

    return -ERR_ITEM_UNEXISTS;
}

int GetInterfaceNameByGUID(const TCHAR *pGUID, TCHAR ifName[MAX_NETCARD_NAME]) {
    VARIANT                               v;
    INetConnection                       *pNC    = nullptr;
    IEnumVARIANT                         *pEV    = nullptr;
    IUnknown                             *pUnk   = nullptr;
    INetSharingEveryConnectionCollection *pNSECC = nullptr;
    INetSharingManager                   *pNSM;
    HRESULT                               hr;

    if (pGUID == nullptr || lstrlen(pGUID) == 0) {
        SPDLOG_ERROR(TEXT("Input pGUID params error: {0}"), pGUID);
        return -ERR_INPUT_PARAMS;
    }

    hr = ::CoCreateInstance(CLSID_NetSharingManager,
                            nullptr,
                            CLSCTX_ALL,
                            IID_INetSharingManager,
                            reinterpret_cast<void **>(&pNSM));

    if (hr != S_OK || pNSM == nullptr) {
        CoInitialize(nullptr);
        CoInitializeSecurity(nullptr,
                             -1,
                             nullptr,
                             nullptr,
                             RPC_C_AUTHN_LEVEL_PKT,
                             RPC_C_IMP_LEVEL_IMPERSONATE,
                             nullptr,
                             EOAC_NONE,
                             nullptr);

        hr = ::CoCreateInstance(CLSID_NetSharingManager,
                                nullptr,
                                CLSCTX_ALL,
                                IID_INetSharingManager,
                                reinterpret_cast<void **>(&pNSM));
    }

    if (hr != S_OK || pNSM == nullptr) {
        SPDLOG_ERROR(TEXT("CoCreateInstance NetSharingManager failed: {0}"), hr);
        return -ERR_CREATE_COMMOBJECT;
    }

    VariantInit(&v);
    hr = pNSM->get_EnumEveryConnection(&pNSECC);

    if (hr != S_OK || !pNSECC) {
        SPDLOG_ERROR(TEXT("INetSharingManager get_EnumEveryConnection failed: {0}."), hr);
        return -ERR_SYS_CALL;
    }

    hr = pNSECC->get__NewEnum(&pUnk);
    pNSECC->Release();

    if (hr != S_OK || !pUnk) {
        SPDLOG_ERROR(TEXT("INetSharingManager get_EnumEveryConnection failed: {0}."), hr);
        return -ERR_SYS_CALL;
    }

    hr = pUnk->QueryInterface(IID_IEnumVARIANT, reinterpret_cast<void **>(&pEV));
    pUnk->Release();
    if (hr != S_OK || !pUnk) {
        SPDLOG_ERROR(TEXT("INetSharingManager get_EnumEveryConnection failed: {0}."), hr);
        return -ERR_SYS_CALL;
    }

    while (S_OK == pEV->Next(1, &v, nullptr)) {
        if (V_VT(&v) == VT_UNKNOWN) {
            V_UNKNOWN(&v)->QueryInterface(IID_INetConnection, reinterpret_cast<void **>(&pNC));
            if (pNC) {
                int                ret;
                TCHAR              strGuid[MAX_PATH] = {};
                NETCON_PROPERTIES *pNP;
                pNC->GetProperties(&pNP);

                StringCbPrintf(strGuid,
                               MAX_PATH,
                               TEXT("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
                               pNP->guidId.Data1,
                               pNP->guidId.Data2,
                               pNP->guidId.Data3,
                               pNP->guidId.Data4[0],
                               pNP->guidId.Data4[1],
                               pNP->guidId.Data4[2],
                               pNP->guidId.Data4[3],
                               pNP->guidId.Data4[4],
                               pNP->guidId.Data4[5],
                               pNP->guidId.Data4[6],
                               pNP->guidId.Data4[7]);

                // �ҵ���Ӧ���������д���
                if (StrCmp(pGUID, strGuid) != 0) {
                    continue;
                }

                memset(ifName, 0, MAX_NETCARD_NAME);
                ret = WideCharToTChar(pNP->pszwName, ifName, MAX_NETCARD_NAME);

                // ִ���ַ���ת��
                if (ret != ERR_SUCCESS) {
                    SPDLOG_ERROR(TEXT("Convert Unicode wide char to TCHAR failed: {0}."), ret);
                    return ret;
                }

                return ERR_SUCCESS;
            }
        }
    }

    return ERR_ITEM_UNEXISTS;
}

int GetInternetIfIndex(int *pIfIndex) {
    PIP_ADAPTER_INFO pAdapterInfo;
    DWORD            dwRetVal;
    ULONG            ulOutBufLen;

    if (pIfIndex == nullptr) {
        SPDLOG_ERROR(TEXT("Input pIfIndex params error"));
        return -ERR_INPUT_PARAMS;
    }

    ulOutBufLen  = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO)));

    if (pAdapterInfo == nullptr) {
        SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo"));
        return -ERR_MALLOC_MEMORY;
    }

    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, ulOutBufLen));
        if (pAdapterInfo == nullptr) {
            SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo\n"));
            return -ERR_MALLOC_MEMORY;
        }
    }

    *pIfIndex = -1;

    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
        while (pAdapter) {
            bool bIsInternel;
            int  index = static_cast<int>(pAdapter->Index);

            int ret = IsInternetConnectAdapter(index, &bIsInternel);

            if (ret != ERR_SUCCESS) {
                SPDLOG_ERROR(TEXT("IsInternetConnectAdapter {0} : {1}\n"), index, ret);
            }

            if (ret == ERR_SUCCESS && bIsInternel) {
                *pIfIndex = index;
                HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                return ERR_SUCCESS;
            }

            pAdapter = pAdapter->Next;
        }
    } else {
        SPDLOG_ERROR(TEXT("GetAdaptersInfo failed with error: {0}\n"), dwRetVal);
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        return -ERR_SYS_CALL;
    }

    if (pAdapterInfo) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
    }

    return -ERR_ITEM_UNEXISTS;
}

int GetInterfaceGUIDByIfIndex(const int ifIndex, GUID *pGuid) {
    PIP_ADAPTER_INFO pAdapterInfo;
    DWORD            dwRetVal;
    ULONG            ulOutBufLen;
    if (pGuid == nullptr) {
        SPDLOG_ERROR(TEXT("Input pGuid error."));
        return -ERR_INPUT_PARAMS;
    }

    ulOutBufLen  = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO)));

    if (pAdapterInfo == nullptr) {
        SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo"));
        return -ERR_MALLOC_MEMORY;
    }

    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, ulOutBufLen));
        if (pAdapterInfo == nullptr) {
            SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo"));
            return -ERR_MALLOC_MEMORY;
        }
    }

    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
        while (pAdapter) {
            if (ifIndex == static_cast<int>(pAdapter->Index)) {
                int   ret;
                WCHAR strGuid[MAX_PATH];

                if ((ret = TCharToWideChar(pAdapter->AdapterName, strGuid, MAX_PATH)) != ERR_SUCCESS) {
                    HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                    return ret;
                }

                if (CLSIDFromString(strGuid, pGuid) != NOERROR) {
                    HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                    return -ERR_MEMORY_STR;
                }

                HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                return ERR_SUCCESS;
            }
            pAdapter = pAdapter->Next;
        }
    } else {
        SPDLOG_ERROR(TEXT("GetAdaptersInfo failed with error: {0}"), dwRetVal);
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        return -ERR_SYS_CALL;
    }

    if (pAdapterInfo) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
    }

    return -ERR_ITEM_UNEXISTS;
}

/**
 * @brief ������������ȡ��������
 * @param[in] pInterfaceName ��������
 * @param[out] pIfIndex ���� Index
 * @return ����ִ�н�� 0: �ɹ��� С��0 ʧ�� @see USER_ERRNO
 * - -ERR_INPUT_PARAMS �����������
 * - -ERR_ITEM_UNEXISTS ����������
 * - -ERR_SYS_CALL ��ȡ����ϵͳ����������ʧ��
 * - -ERR_MALLOC_MEMORY �����ڴ�ʧ��
 * - ERR_SUCCESS �ɹ�
 */
int GetInterfaceIfIndexByName(const TCHAR *pInterfaceName, int *pIfIndex) {
    PIP_ADAPTER_INFO pAdapterInfo;
    DWORD            dwRetVal;
    ULONG            ulOutBufLen;

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR(TEXT("Input pInterfaceName error: {0}"), pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pIfIndex == nullptr) {
        SPDLOG_ERROR(TEXT("Input pIfIndex params error."));
        return -ERR_INPUT_PARAMS;
    }

    ulOutBufLen  = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO)));

    if (pAdapterInfo == nullptr) {
        SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo"));
        return -ERR_MALLOC_MEMORY;
    }

    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, ulOutBufLen));
        if (pAdapterInfo == nullptr) {
            SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo\n"));
            return -ERR_MALLOC_MEMORY;
        }
    }

    *pIfIndex = -1;

    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
        while (pAdapter) {
            TCHAR NetCardName[MAX_NETCARD_NAME] = {};
            GetInterfaceNameByGUID(pAdapter->AdapterName, NetCardName);

            if (StrCmp(pInterfaceName, NetCardName) == 0) {
                *pIfIndex = static_cast<int>(pAdapter->Index);
                HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                return ERR_SUCCESS;
            }

            pAdapter = pAdapter->Next;
        }
    } else {
        SPDLOG_ERROR(TEXT("GetAdaptersInfo failed with error: {0}\n"), dwRetVal);
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        return -ERR_SYS_CALL;
    }

    if (pAdapterInfo) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
    }

    return -ERR_ITEM_UNEXISTS;
}

int GetInterfaceGUIDByName(const TCHAR *pInterfaceName, GUID *pGuid) {
    PIP_ADAPTER_INFO pAdapterInfo;
    DWORD            dwRetVal;
    ULONG            ulOutBufLen;

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR(TEXT("Input pInterfaceName error: {0}"), pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pGuid == nullptr) {
        SPDLOG_ERROR(TEXT("Input pGuid params error"));
        return -ERR_INPUT_PARAMS;
    }

    ulOutBufLen  = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO)));

    if (pAdapterInfo == nullptr) {
        SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo"));
        return -ERR_MALLOC_MEMORY;
    }

    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, ulOutBufLen));
        if (pAdapterInfo == nullptr) {
            SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo\n"));
            return -ERR_MALLOC_MEMORY;
        }
    }

    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
        while (pAdapter) {
            int   ret;
            TCHAR NetCardName[MAX_NETCARD_NAME] = {};
            GetInterfaceNameByGUID(pAdapter->AdapterName, NetCardName);

            if (StrCmp(pInterfaceName, NetCardName) == 0) {
                WCHAR strGuid[MAX_PATH];

                if ((ret = TCharToWideChar(pAdapter->AdapterName, strGuid, MAX_PATH)) != ERR_SUCCESS) {
                    HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                    return ret;
                }

                if (CLSIDFromString(strGuid, pGuid) != NOERROR) {
                    HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                    return -ERR_MEMORY_STR;
                }
                HeapFree(GetProcessHeap(), 0, pAdapterInfo);
                return ERR_SUCCESS;
            }

            pAdapter = pAdapter->Next;
        }
    } else {
        SPDLOG_ERROR(TEXT("GetAdaptersInfo failed with error: {0}\n"), dwRetVal);
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        return -ERR_SYS_CALL;
    }

    if (pAdapterInfo) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
    }

    return -ERR_ITEM_UNEXISTS;
}

int WaitNetAdapterConnected(const TCHAR *pInterfaceName, int timeOutOfMs) {
    INetworkListManager     *pNLM;
    IEnumNetworkConnections *pEnumConns;
    INetwork                *pINet;
    INetworkConnection      *pIConn;
    HRESULT                  hr;
    GUID                     guid;
    const DWORD              startTime = timeGetTime();

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR(TEXT("Input pInterfaceName params error: {0}"), pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    hr = ::CoCreateInstance(CLSID_NetworkListManager,
                            nullptr,
                            CLSCTX_ALL,
                            IID_INetworkListManager,
                            reinterpret_cast<void **>(&pNLM));

    if (hr != S_OK || pNLM == nullptr) {
        SPDLOG_ERROR(TEXT("CoCreateInstance NetworkListManager failed: {0}."), hr);
        return -ERR_CREATE_COMMOBJECT;
    }

    do {

        hr = pNLM->GetNetworkConnections(&pEnumConns);

        if (hr != S_OK || pEnumConns == nullptr) {
            SPDLOG_ERROR(TEXT("NetworkListManager GetNetworks failed: {0}."), hr);
            continue;
        }

        while (S_OK == pEnumConns->Next(1, &pIConn, nullptr)) {
            GUID adpterGuid;

            pIConn->GetAdapterId(&adpterGuid);
            pIConn->GetNetwork(&pINet);

            if (pINet) {
                BSTR  sName = {};
                TCHAR ifName[MAX_PATH];

                pINet->GetName(&sName);

                if (WideCharToTChar(sName, ifName, MAX_PATH) != ERR_SUCCESS) {
                    SysFreeString(sName);
                    return -ERR_MEMORY_STR;
                }

                SysFreeString(sName);

                if (StrNCmp(pInterfaceName, ifName, lstrlen(pInterfaceName)) == 0) {

                    int ret = GetInterfaceGUIDByName(pInterfaceName, &guid);
                    if (ret != ERR_SUCCESS) {
                        SPDLOG_ERROR(TEXT("Get Interface {0} GUID error: {1}"), pInterfaceName, ret);
                        continue;
                    }

                    /*SPDLOG_DEBUG(TEXT("Match Interface {0} --> {1}, Guid {2:x} --> {3:x}"),
                                 ifName,
                                 pInterfaceName,
                                 adpterGuid.Data1,
                                 guid.Data1);*/

                    if (memcmp(&adpterGuid, &guid, sizeof(GUID)) == 0) {
                        SPDLOG_DEBUG(TEXT("Interface {0}({1}) network connected now..."), ifName, pInterfaceName);
                        return ERR_SUCCESS;
                    }
                }
            }
        }

        Sleep(1000);
    } while (timeGetTime() - startTime <= static_cast<DWORD>(timeOutOfMs));

    return -ERR_SYS_TIMEOUT;
}

int GetAllNICInfo(PNIC_CONTENT *pInfo, int *pItemCounts) {
    PNIC_CONTENT     pNic;
    PIP_ADAPTER_INFO pAdapterInfo;
    DWORD            dwRetVal;

    if (pItemCounts == nullptr) {
        SPDLOG_ERROR(TEXT("Input pItemCounts params error"));
        return -ERR_INPUT_PARAMS;
    }

    if (pInfo == nullptr) {
        SPDLOG_ERROR(TEXT("Input pInfo params error"));
        return -ERR_INPUT_PARAMS;
    }

    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo      = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO)));

    if (pAdapterInfo == nullptr) {
        SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo"));
        return -ERR_MALLOC_MEMORY;
    }

    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        pAdapterInfo = static_cast<IP_ADAPTER_INFO *>(HeapAlloc(GetProcessHeap(), 0, ulOutBufLen));
        if (pAdapterInfo == nullptr) {
            SPDLOG_ERROR(TEXT("Error allocating memory needed to call GetAdaptersinfo\n"));
            return -ERR_MALLOC_MEMORY;
        }
    }

    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        int              id       = 0;
        PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
        while (pAdapter && id < NET_CARD_MAX) {
            // ��������
            g_NetAdapterInfo[id].InterfaceIndex = static_cast<int>(pAdapter->Index);
            // ��������
            StringCbCopy(g_NetAdapterInfo[id].NetCardUUID, MAX_ADAPTER_NAME_LENGTH, pAdapter->AdapterName);
            // ������ϸ����
            StringCbCopy(g_NetAdapterInfo[id].NetCardDescription,
                         MAX_ADAPTER_DESCRIPTION_LENGTH,
                         pAdapter->Description);
            // ���� IP ��ַ
            StringCbCopy(g_NetAdapterInfo[id].NetCardIpaddr, MAX_IP_LEN - 1, pAdapter->IpAddressList.IpAddress.String);
            // ������������
            StringCbCopy(g_NetAdapterInfo[id].NetCardNetmask, MAX_IP_LEN - 1, pAdapter->IpAddressList.IpMask.String);
            // �������ص�ַ
            StringCbCopy(g_NetAdapterInfo[id].NetCardGateway, MAX_IP_LEN - 1, pAdapter->GatewayList.IpAddress.String);
            // ���� MAC ��ַ
            StringCbPrintf(g_NetAdapterInfo[id].NetCardMacAddr,
                           20 - 1,
                           TEXT("%02X:%02X:%02X:%02X:%02X:%02X"),
                           pAdapter->Address[0],
                           pAdapter->Address[1],
                           pAdapter->Address[2],
                           pAdapter->Address[3],
                           pAdapter->Address[4],
                           pAdapter->Address[5]);

            GetInterfaceNameByGUID(pAdapter->AdapterName, g_NetAdapterInfo[id].NetCardName);

            id++;
            pAdapter = pAdapter->Next;
        }

        *pItemCounts = id;
    } else {
        SPDLOG_ERROR(TEXT("GetAdaptersInfo failed with error: {0}\n"), dwRetVal);
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
        return -ERR_SYS_CALL;
    }

    if (pAdapterInfo) {
        HeapFree(GetProcessHeap(), 0, pAdapterInfo);
    }

    pNic = static_cast<PNIC_CONTENT>(CoTaskMemAlloc(sizeof(NIC_CONTENT) * (*pItemCounts)));

    if (pNic == nullptr) {
        *pItemCounts = 0;
        SPDLOG_ERROR(TEXT("Error allocating memory {0} bytes"), sizeof(NIC_CONTENT) * (*pItemCounts));
        return -ERR_MALLOC_MEMORY;
    }

    memset(pNic, 0, sizeof(NIC_CONTENT) * (*pItemCounts));
    memcpy(pNic, g_NetAdapterInfo, sizeof(NIC_CONTENT) * *pItemCounts);
    *pInfo = pNic;

    return ERR_SUCCESS;
}

int IsInternetConnectAdapter(int ifIndex, bool *pRet) {
    DWORD               dwSize = 0;
    DWORD               dwRetVal;
    PMIB_IPFORWARDTABLE pIpForwardTable;

    if (ifIndex < 0 || ifIndex > 255) {
        SPDLOG_ERROR(TEXT("Input ifIndex params error: {0}"), ifIndex);
        return -ERR_INPUT_PARAMS;
    }

    if (pRet == nullptr) {
        SPDLOG_ERROR(TEXT("Input pRet params error"));
        return -ERR_INPUT_PARAMS;
    }

    pIpForwardTable = static_cast<MIB_IPFORWARDTABLE *>(HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPFORWARDTABLE)));

    if (pIpForwardTable == nullptr) {
        SPDLOG_ERROR(TEXT("Malloc {0} bytes memory error"), sizeof(MIB_IPFORWARDTABLE));
        return -ERR_MALLOC_MEMORY;
    }

    if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
        HeapFree(GetProcessHeap(), 0, pIpForwardTable);
        pIpForwardTable = static_cast<MIB_IPFORWARDTABLE *>(HeapAlloc(GetProcessHeap(), 0, dwSize));
        if (pIpForwardTable == nullptr) {
            SPDLOG_ERROR(TEXT("Malloc {0} bytes memory error"), dwSize);
            return -ERR_MALLOC_MEMORY;
        }
    }

    *pRet = false;

    if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR) {
        for (DWORD i = 0; i < pIpForwardTable->dwNumEntries; i++) {
            TCHAR ipStr[24]   = {};
            TCHAR maskStr[24] = {};

            if (static_cast<int>(pIpForwardTable->table[i].dwForwardIfIndex) != ifIndex) {
                continue;
            }

            if (InetNtop(AF_INET, &pIpForwardTable->table[i].dwForwardDest, ipStr, 24) == nullptr) {
                continue;
            }

            if (InetNtop(AF_INET, &pIpForwardTable->table[i].dwForwardMask, maskStr, 24) == nullptr) {
                continue;
            }

            if (StrCmp(ipStr, TEXT("0.0.0.0")) == 0 && StrCmp(maskStr, TEXT("0.0.0.0")) == 0) {
                *pRet = true;
                break;
            }
        }
        HeapFree(GetProcessHeap(), 0, pIpForwardTable);
        return ERR_SUCCESS;
    } else {
        SPDLOG_ERROR(TEXT("GetIpForwardTable failed: {0}."), dwRetVal);
        HeapFree(GetProcessHeap(), 0, pIpForwardTable);
        return ERR_GET_IPFOWARDTBL;
    }
}

int SetNetConnectionNetworkCategory(const TCHAR *pInterfaceName, const bool isPrivate) {
    INetworkListManager     *pNLM;
    IEnumNetworkConnections *pEnumConns;
    INetwork                *pINet;
    INetworkConnection      *pIConn;
    HRESULT                  hr;
    GUID                     guid;
    int                      ret;

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR(TEXT("Input pInterfaceName params error: {0}"), pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if ((ret = GetInterfaceGUIDByName(pInterfaceName, &guid)) != ERR_SUCCESS) {
        SPDLOG_ERROR(TEXT("Get NetCard [{0}] GUID error: {1}"), pInterfaceName, ret);
        return ret;
    }

    hr = ::CoCreateInstance(CLSID_NetworkListManager,
                            nullptr,
                            CLSCTX_ALL,
                            IID_INetworkListManager,
                            reinterpret_cast<void **>(&pNLM));

    if (hr != S_OK || pNLM == nullptr) {
        SPDLOG_ERROR(TEXT("CoCreateInstance NetworkListManager failed: {0}."), hr);
        return -ERR_CREATE_COMMOBJECT;
    }

    hr = pNLM->GetNetworkConnections(&pEnumConns);

    if (hr != S_OK || pEnumConns == nullptr) {
        SPDLOG_ERROR(TEXT("NetworkListManager GetNetworks failed: {0}."), hr);
        return -ERR_CREATE_COMMOBJECT;
    }

    while (S_OK == pEnumConns->Next(1, &pIConn, nullptr)) {
        GUID adpterGuid;
        pIConn->GetNetwork(&pINet);
        pIConn->GetAdapterId(&adpterGuid);

        if (pINet) {
            if (memcmp(&adpterGuid, &guid, sizeof(GUID)) == 0) {
                pINet->SetCategory(isPrivate ? NLM_NETWORK_CATEGORY_PRIVATE : NLM_NETWORK_CATEGORY_PUBLIC);
                return ERR_SUCCESS;
            }
        }
    }

    return -ERR_ITEM_UNEXISTS;
}

int GetNetConnectionNetworkCategory(const TCHAR *pInterfaceName, bool *pIsPrivate) {
    INetworkListManager     *pNLM;
    IEnumNetworkConnections *pEnumConns;
    INetwork                *pINet;
    INetworkConnection      *pIConn;
    HRESULT                  hr;
    GUID                     guid;
    int                      ret;

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR(TEXT("Input pInterfaceName params error: {0}"), pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pIsPrivate == nullptr) {
        SPDLOG_ERROR(TEXT("Input pIsPrivate params error"));
        return -ERR_INPUT_PARAMS;
    }

    if ((ret = GetInterfaceGUIDByName(pInterfaceName, &guid)) != ERR_SUCCESS) {
        SPDLOG_ERROR(TEXT("Get NetCard [{0}] GUID error: {1}"), pInterfaceName, ret);
        return ret;
    }

    hr = ::CoCreateInstance(CLSID_NetworkListManager,
                            nullptr,
                            CLSCTX_ALL,
                            IID_INetworkListManager,
                            reinterpret_cast<void **>(&pNLM));

    if (hr != S_OK || pNLM == nullptr) {
        SPDLOG_ERROR(TEXT("CoCreateInstance NetworkListManager failed: {0}."), hr);
        return -ERR_CREATE_COMMOBJECT;
    }

    hr = pNLM->GetNetworkConnections(&pEnumConns);

    if (hr != S_OK || pEnumConns == nullptr) {
        SPDLOG_ERROR(TEXT("NetworkListManager GetNetworks failed: {0}."), hr);
        return -ERR_CREATE_COMMOBJECT;
    }

    while (S_OK == pEnumConns->Next(1, &pIConn, nullptr)) {
        GUID adpterGuid;
        pIConn->GetNetwork(&pINet);
        pIConn->GetAdapterId(&adpterGuid);

        if (pINet) {
            if (memcmp(&adpterGuid, &guid, sizeof(GUID)) == 0) {
                NLM_NETWORK_CATEGORY cat;
                pINet->GetCategory(&cat);
                *pIsPrivate = (cat == NLM_NETWORK_CATEGORY_PRIVATE) ? true : false;
                return ERR_SUCCESS;
            }
        }
    }

    return -ERR_ITEM_UNEXISTS;
}

int SetNetIntelnetConnectionSharing(int ifIndex, bool isEnable, bool isSetPrivate) {
    VARIANT                               v;
    INetConnection                       *pNC    = nullptr;
    INetSharingConfiguration             *pNSC   = nullptr;
    IEnumVARIANT                         *pEV    = nullptr;
    IUnknown                             *pUnk   = nullptr;
    INetSharingEveryConnectionCollection *pNSECC = nullptr;
    INetSharingManager                   *pNSM;
    HRESULT                               hr;
    GUID                                  ifGuid;
    int                                   ret;

    if ((ret = GetInterfaceGUIDByIfIndex(ifIndex, &ifGuid)) != ERR_SUCCESS) {
        return ret;
    }

    hr = ::CoCreateInstance(CLSID_NetSharingManager,
                            nullptr,
                            CLSCTX_ALL,
                            IID_INetSharingManager,
                            reinterpret_cast<void **>(&pNSM));

    if (hr != S_OK || pNSM == nullptr) {
        SPDLOG_ERROR(TEXT("CoCreateInstance NetSharingManager failed: {0}."), hr);
        return -ERR_CREATE_COMMOBJECT;
    }

    VariantInit(&v);
    hr = pNSM->get_EnumEveryConnection(&pNSECC);

    if (hr != S_OK || !pNSECC) {
        SPDLOG_ERROR(TEXT("INetSharingManager get_EnumEveryConnection failed: {0}."), hr);
        return -ERR_SYS_CALL;
    }

    hr = pNSECC->get__NewEnum(&pUnk);
    pNSECC->Release();

    if (hr != S_OK || !pUnk) {
        SPDLOG_ERROR(TEXT("INetSharingManager get_EnumEveryConnection failed: {0}."), hr);
        return -ERR_SYS_CALL;
    }

    hr = pUnk->QueryInterface(IID_IEnumVARIANT, reinterpret_cast<void **>(&pEV));
    pUnk->Release();
    if (hr != S_OK || !pUnk) {
        SPDLOG_ERROR(TEXT("INetSharingManager get_EnumEveryConnection failed: {0}."), hr);
        return -ERR_SYS_CALL;
    }

    while (S_OK == pEV->Next(1, &v, nullptr)) {
        if (V_VT(&v) == VT_UNKNOWN) {
            V_UNKNOWN(&v)->QueryInterface(IID_INetConnection, reinterpret_cast<void **>(&pNC));
            if (pNC) {
                NETCON_PROPERTIES *pNP;
                pNC->GetProperties(&pNP);

                // �ҵ���Ӧ���������д���
                if (memcmp(&ifGuid, &pNP->guidId, sizeof(GUID)) != 0) {
                    continue;
                }

                // δ���ӵ�����������
                if (pNP->Status != NCS_CONNECTED) {
                    return -ERR_NET_UNCONNECT;
                }

                hr = pNSM->get_INetSharingConfigurationForINetConnection(pNC, &pNSC);

                if (hr != S_OK || !pNSC) {
                    continue;
                }

                if (pNSC) {
                    if (isEnable) {
                        hr = pNSC->DisableSharing();

                        if (hr != S_OK) {
                            SPDLOG_ERROR(TEXT("INetSharingManager DisableSharing failed: {0}."), hr);
                            pNSC->Release();
                            return -ERR_CALL_COMMOBJECT;
                        }
                        Sleep(500);
                        hr = pNSC->EnableSharing(isSetPrivate ? ICSSHARINGTYPE_PRIVATE : ICSSHARINGTYPE_PUBLIC);

                        if (hr != S_OK) {
                            SPDLOG_ERROR(TEXT("INetSharingManager EnableSharing failed: {0}."), hr);
                            pNSC->Release();
                            return -ERR_CALL_COMMOBJECT;
                        }
                    } else {
                        hr = pNSC->DisableSharing();

                        if (hr != S_OK) {
                            SPDLOG_ERROR(TEXT("INetSharingManager DisableSharing failed: {0}."), hr);
                            pNSC->Release();
                            return -ERR_CALL_COMMOBJECT;
                        }
                    }

                    pNSC->Release();
                    return ERR_SUCCESS;
                }
            }
        }
    }

    return ERR_ITEM_UNEXISTS;
}

int GetNetIntelnetConnectionSharing(int ifIndex, bool *pIsEnable) {
    VARIANT                               v;
    INetConnection                       *pNC    = nullptr;
    INetSharingConfiguration             *pNSC   = nullptr;
    IEnumVARIANT                         *pEV    = nullptr;
    IUnknown                             *pUnk   = nullptr;
    INetSharingEveryConnectionCollection *pNSECC = nullptr;
    INetSharingManager                   *pNSM;
    HRESULT                               hr;
    GUID                                  ifGuid;
    int                                   ret;

    if (pIsEnable == nullptr) {
        SPDLOG_ERROR(TEXT("Input pIsEnable params error"));
        return -ERR_INPUT_PARAMS;
    }

    // ��������������ȡ����GUID
    if ((ret = GetInterfaceGUIDByIfIndex(ifIndex, &ifGuid)) != ERR_SUCCESS) {
        return ret;
    }

    hr = ::CoCreateInstance(CLSID_NetSharingManager,
                            nullptr,
                            CLSCTX_ALL,
                            IID_INetSharingManager,
                            reinterpret_cast<void **>(&pNSM));

    if (hr != S_OK || pNSM == nullptr) {
        SPDLOG_ERROR(TEXT("CoCreateInstance NetSharingManager failed: {0}."), hr);
        return -ERR_CREATE_COMMOBJECT;
    }

    VariantInit(&v);
    hr = pNSM->get_EnumEveryConnection(&pNSECC);

    if (hr != S_OK || !pNSECC) {
        SPDLOG_ERROR(TEXT("INetSharingManager get_EnumEveryConnection failed: {0}."), hr);
        return -ERR_SYS_CALL;
    }

    hr = pNSECC->get__NewEnum(&pUnk);
    pNSECC->Release();

    if (hr != S_OK || !pUnk) {
        SPDLOG_ERROR(TEXT("INetSharingManager get_EnumEveryConnection failed: {0}."), hr);
        return -ERR_SYS_CALL;
    }

    hr = pUnk->QueryInterface(IID_IEnumVARIANT, reinterpret_cast<void **>(&pEV));
    pUnk->Release();
    if (hr != S_OK || !pUnk) {
        SPDLOG_ERROR(TEXT("INetSharingManager get_EnumEveryConnection failed: {0}."), hr);
        return -ERR_SYS_CALL;
    }

    while (S_OK == pEV->Next(1, &v, nullptr)) {
        if (V_VT(&v) == VT_UNKNOWN) {
            V_UNKNOWN(&v)->QueryInterface(IID_INetConnection, reinterpret_cast<void **>(&pNC));
            if (pNC) {
                NETCON_PROPERTIES *pNP;
                pNC->GetProperties(&pNP);

                // �ҵ���Ӧ���������д���
                if (memcmp(&ifGuid, &pNP->guidId, sizeof(GUID)) != 0) {
                    continue;
                }

                //if (StrCmp(pInterfaceName, ifName) != 0) {
                //    continue;
                //}

                hr = pNSM->get_INetSharingConfigurationForINetConnection(pNC, &pNSC);

                if (hr != S_OK || !pNSC) {
                    continue;
                }

                if (pNSC) {
                    VARIANT_BOOL bRet = false;
                    hr                = pNSC->get_SharingEnabled(&bRet);
                    pNSC->Release();

                    if (hr != S_OK) {
                        SPDLOG_ERROR(TEXT("INetSharingManager DisableSharing failed: {0}."), hr);
                        return -ERR_CALL_COMMOBJECT;
                    }

                    *pIsEnable = bRet;

                    return ERR_SUCCESS;
                }
            }
        }
    }

    return ERR_ITEM_UNEXISTS;
}

int AddRouteTable(const char *pIP, const char *pMask, const char *pGateway) {
    PMIB_IPFORWARDTABLE pIpForwardTable = nullptr;
    PMIB_IPFORWARDROW   pRow            = nullptr;
    DWORD               dwSize          = 0;
    DWORD               dwStatus;
    DWORD               dwDestIp;
    int                 ret;
    IP_INFO             ipInfo;

    if (pIP == nullptr || lstrlen(pIP) < MIN_IP_LEN) {
        SPDLOG_ERROR(TEXT("Input pIP params error: {0}"), pIP);
        return -ERR_INPUT_PARAMS;
    }

    if (pMask == nullptr || lstrlen(pMask) < MIN_IP_LEN) {
        SPDLOG_ERROR(TEXT("Input pMask params error: {0}"), pMask);
        return -ERR_INPUT_PARAMS;
    }

    if (pGateway == nullptr || lstrlen(pGateway) < MIN_IP_LEN) {
        SPDLOG_ERROR(TEXT("Input pGateway params error: {0}"), pGateway);
        return -ERR_INPUT_PARAMS;
    }

    if ((ret = GetIpV4InfoFromNetmask(pIP, pMask, &ipInfo)) != ERR_SUCCESS) {
        return ret;
    }

    if (inet_pton(AF_INET, ipInfo.network, &dwDestIp) <= 0) {
        SPDLOG_ERROR(TEXT("Convert {0} to network ipaddress error."), pIP);
        return -ERR_UN_SUPPORT;
    }

    // Find out how big our buffer needs to be.
    dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, FALSE);
    if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
        // Allocate the memory for the table
        pIpForwardTable = static_cast<PMIB_IPFORWARDTABLE>(malloc(dwSize));
        if (!pIpForwardTable) {
            SPDLOG_ERROR(TEXT("Malloc failed. Out of memory."));
            return -ERR_MALLOC_MEMORY;
        }
        // Now get the table.
        dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, FALSE);
    }

    if (dwStatus != ERROR_SUCCESS) {
        SPDLOG_ERROR(TEXT("getIpForwardTable failed."));
        if (pIpForwardTable) {
            free(pIpForwardTable);
        }
        return -ERR_SYS_CALL;
    }

    for (DWORD i = 0; i < pIpForwardTable->dwNumEntries; i++) {
        if (pIpForwardTable->table[i].dwForwardDest == 0) {
            if (!pRow) {
                pRow = static_cast<PMIB_IPFORWARDROW>(malloc(sizeof(MIB_IPFORWARDROW)));
                if (!pRow) {
                    SPDLOG_ERROR(TEXT("Malloc failed. Out of memory."));
                    free(pIpForwardTable);
                    free(pRow);
                    return -ERR_MALLOC_MEMORY;
                }
                // Copy the row
                memcpy(pRow, &(pIpForwardTable->table[i]), sizeof(MIB_IPFORWARDROW));
            }
        } else if (pIpForwardTable->table[i].dwForwardDest == dwDestIp) {
            // ɾ�����ܴ��ڵľɵ�·����Ϣ
            dwStatus = DeleteIpForwardEntry(&(pIpForwardTable->table[i]));

            if (dwStatus != ERROR_SUCCESS) {
                SPDLOG_ERROR(TEXT("Could not delete old gateway"));
                return -ERR_NET_REMOVE_ROUTE;
            }
        }
    }

    free(pIpForwardTable);

    pRow->dwForwardDest = dwDestIp;

    if (inet_pton(AF_INET, ipInfo.netmask, &pRow->dwForwardMask) <= 0) {
        SPDLOG_ERROR(TEXT("Convert {0} to network ipaddress error."), pMask);
        return -ERR_UN_SUPPORT;
    }

    if (inet_pton(AF_INET, pGateway, &pRow->dwForwardNextHop) <= 0) {
        SPDLOG_ERROR(TEXT("Convert {0} to network ipaddress error."), pGateway);
        free(pRow);
        return -ERR_UN_SUPPORT;
    }

    if ((ret = GetInterfaceIfIndexByIpAddr(pGateway, &pRow->dwForwardIfIndex)) != ERR_SUCCESS) {
        free(pRow);
        return ret;
    }

    if ((dwStatus = CreateIpForwardEntry(pRow)) != NO_ERROR) {
        SPDLOG_ERROR(TEXT("Add Route {1} netmask {2} gateway {3} error: {0}."), dwStatus, pIP, pMask, pGateway);
        free(pRow);
        return -ERR_NET_ADD_ROUTE;
    }

    free(pRow);

    return ERR_SUCCESS;
}

int SetNATRule(const TCHAR *pInterfaceName, const TCHAR *pCidrIpaddr) {
    int   ret;
    TCHAR cmdBuf[1024];
    DWORD retCode;

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR("Input pInterfaceName params error: {0}", pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pCidrIpaddr == nullptr || lstrlen(pCidrIpaddr) == 0) {
        SPDLOG_ERROR("Input pCidrIpaddr params error: {0}", pCidrIpaddr);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbPrintf(
            cmdBuf,
            1024,
            TEXT("PowerShell -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass Invoke-Command "
                 "-ScriptBlock { Get-NetNat -Name %s_nat -ErrorAction Ignore | Remove-NetNat -Confirm:$false; "
                 "New-NetNat -Name %s_nat -InternalIPInterfaceAddressPrefix %s }"),
            pInterfaceName,
            pInterfaceName,
            pCidrIpaddr))) {
        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, nullptr, 0, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG("Run Command({1}): {0}", cmdBuf, retCode);

    if (retCode != 0) {
        SPDLOG_ERROR("PowerShell return error({1}): {0}", cmdBuf, retCode);
        return -ERR_PROCESS_RETURN;
    }

    return ERR_SUCCESS;
}

int RemoveNATRule(const TCHAR *pInterfaceName) {
    int   ret;
    TCHAR cmdBuf[1024];
    DWORD retCode;

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR("Input pInterfaceName params error: {0}", pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbPrintf(
            cmdBuf,
            1024,
            TEXT("PowerShell -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass Invoke-Command "
                 "-ScriptBlock { Get-NetNat -Name %s_nat -ErrorAction Ignore | Remove-NetNat -Confirm:$false}"),
            pInterfaceName))) {
        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, nullptr, 0, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG("Run Command({1}): {0}", cmdBuf, retCode);

    return ERR_SUCCESS;
}

#if 0
int SetInterfaceIpAddress(const TCHAR *pInterfaceName, const TCHAR *pIpaddr, const TCHAR *pNetmask) {
    int   ret;
    TCHAR cmdBuf[MAX_PATH];
    //int   cidr;
    DWORD retCode;
    IP_INFO ipInfo {};
    
    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR("Input pInterfaceName params error: {0}", pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pIpaddr == nullptr || lstrlen(pIpaddr) == 0) {
        SPDLOG_ERROR("Input pIpaddr params error: {0}", pIpaddr);
        return -ERR_INPUT_PARAMS;
    }

    if (pNetmask == nullptr || lstrlen(pNetmask) == 0) {
        SPDLOG_WARN("Input pNetmask params error: {0}", pNetmask);
        return -ERR_INPUT_PARAMS;
    }

    GetIpV4InfoFromNetmask(pIpaddr, pNetmask, &ipInfo);
    //cidr = NetmaskToCIDR(pNetmask);

    if (FAILED(StringCbPrintf(
            cmdBuf,
            MAX_PATH,
            "PowerShell -Command \"& {New-NetIPAddress -InterfaceAlias %s -AddressFamily IPv4 -IPAddress %s -PrefixLength %d}\"",
            pInterfaceName,
            pIpaddr,
            ipInfo.prefix))) {
        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, nullptr, 0, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG("Run Set IP Command({1}): {0}", cmdBuf, retCode);

    return ERR_SUCCESS;
}

int GetWindowsHyperVStatus(int *pEnabled) {
    int   ret;
    TCHAR cmdBuf[MAX_PATH];
    TCHAR cmdResult[2048];
    DWORD retCode;

    if (pEnabled == nullptr) {
        SPDLOG_ERROR(TEXT("Input pEnabled params error"));
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbPrintf(cmdBuf,
                              MAX_PATH,
                              TEXT("PowerShell -Command \"& {Get-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V-All "
                              "-Online | Format-List -Property State}\"")))) {
        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, cmdResult, 2048, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    if (StrStr(cmdResult, TEXT("Enabled")) != nullptr) {
        *pEnabled = TRUE;
    } else {
        *pEnabled = FALSE;
    }

    SPDLOG_DEBUG(TEXT("Run Get Windows Hyper-V status Command({1}): {0} result: {2}\n"), cmdBuf, retCode, cmdResult);

    return ERR_SUCCESS;
}

int EnableWindowsHyperV(bool enabled) {
    int   ret;
    TCHAR cmdBuf[MAX_PATH];
    DWORD retCode;

    if (enabled) {
        if (FAILED(StringCbPrintf(cmdBuf,
                                  MAX_PATH,
                                  TEXT("PowerShell -Command \"& {Enable-WindowsOptionalFeature -Online -FeatureName "
                                  "Microsoft-Hyper-V -All}\"")))) {
            SPDLOG_ERROR("Format String Error");
            return -ERR_MEMORY_STR;
        }
    } else {
        if (FAILED(StringCbPrintf(cmdBuf,
                                  MAX_PATH,
                                  TEXT("PowerShell -Command \"& {Disable-WindowsOptionalFeature -Online -FeatureName "
                                  "Microsoft-Hyper-V-Hypervisor}\"")))) {
            SPDLOG_ERROR("Format String Error");
            return -ERR_MEMORY_STR;
        }
    }

    if ((ret = RunCommand(cmdBuf, nullptr, 0, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR(TEXT("Run command [{0}] error: {1}"), cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    if (retCode != 0) {
        SPDLOG_ERROR(TEXT("PowerShell return error({1}): {0}"), cmdBuf, retCode);
        return -ERR_PROCESS_RETURN;
    }

    return ERR_SUCCESS;
}

int GetInterfaceIndexByName(const TCHAR *pInterfaceName, int *pIndex) {
    int   ret;
    DWORD retCode;
    TCHAR cmdBuf[MAX_PATH];
    TCHAR cmdResult[MAX_PATH] = {};

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR(TEXT("Input pInterfaceName params error: {0}"), pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pIndex == nullptr) {
        SPDLOG_ERROR(TEXT("Input pIndex params error"));
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(
            StringCbPrintf(cmdBuf,
                           MAX_PATH,
                           TEXT("PowerShell -Command \"& {Get-NetAdapter -Name %s | Format-List -Property InterfaceIndex}\""),
                           pInterfaceName))) {
        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, cmdResult, MAX_PATH, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR(TEXT("Run command [{0}] error: {1}"), cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG(TEXT("Run command [{0}] resutl \'{1}\' return {2}"), cmdBuf, cmdResult, retCode);

    // �Ƴ�
    StringRemoveAll(cmdResult, TEXT("\r\n"));
    StringRemoveAll(cmdResult, TEXT(" "));
    StringRemoveAll(cmdResult, TEXT("InterfaceIndex:"));

    *pIndex = strtol(cmdResult, nullptr, 10);

    return ERR_SUCCESS;
}

int RemoveInterfaceIpAddress(const TCHAR *pInterfaceName) {
    int   ret;
    TCHAR cmdBuf[MAX_PATH];
    DWORD retCode;

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR("Input pInterfaceName params error: {0}", pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbPrintf(cmdBuf,
                              MAX_PATH,
                              "PowerShell -Command \"& {Remove-NetIPAddress -InterfaceAlias %s  -Confirm:$false}\"",
                              pInterfaceName))) {
        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, nullptr, 0, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG("Run Set IP Command({1}): {0}", cmdBuf, retCode);

    return ERR_SUCCESS;
}

int SetInterfaceIpAddress(const TCHAR *pInterfaceName, const TCHAR *pIpaddr, const TCHAR *pNetmask) {
    int   ret;
    TCHAR cmdBuf[MAX_PATH];
    int   cidr;
    DWORD retCode;

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR("Input pInterfaceName params error: {0}", pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pIpaddr == nullptr || lstrlen(pIpaddr) == 0) {
        SPDLOG_ERROR("Input pIpaddr params error: {0}", pIpaddr);
        return -ERR_INPUT_PARAMS;
    }

    if (pNetmask == nullptr || lstrlen(pNetmask) == 0) {
        SPDLOG_WARN("Input pNetmask params error: {0}", pNetmask);
        return -ERR_INPUT_PARAMS;
    }

    cidr = NetmaskToCIDR(pNetmask);

    if (FAILED(StringCbPrintf(
            cmdBuf,
            MAX_PATH,
            "PowerShell -Command \"& {New-NetIPAddress -InterfaceAlias %s  -IPAddress %s -PrefixLength %d}\"",
            pInterfaceName,
            pIpaddr,
            cidr))) {
        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, nullptr, 0, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG("Run Set IP Command({1}): {0}", cmdBuf, retCode);

    return ERR_SUCCESS;
}

int SetInterfaceIpAddressFromCIDR(const TCHAR *pInterfaceName, const TCHAR *pCidrIpaddr) {
    TCHAR ipAddr[MAX_IP_LEN];
    TCHAR ip[MAX_IP_LEN];
    int   ret;
    TCHAR cmdBuf[MAX_PATH];
    DWORD retCode;

    TCHAR *token, *p = nullptr;

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR("Input pInterfaceName params error: {0}", pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pCidrIpaddr == nullptr || lstrlen(pCidrIpaddr) == 0) {
        SPDLOG_ERROR("Input pCidrIpaddr params error: {0}", pCidrIpaddr);
        return -ERR_INPUT_PARAMS;
    }

    StringCbCopy(ipAddr, MAX_IP_LEN, pCidrIpaddr);

    // ��ȡǰ��IP��ַ
    token = strtok_s(ipAddr, TEXT("/"), &p);
    if (token == nullptr) {
        SPDLOG_ERROR("CIDR IpAddress string format error: {0}", pCidrIpaddr);
        return -ERR_INPUT_PARAMS;
    }
    StringCbCopy(ip, MAX_IP_LEN, token);

    // ��ȡ������������
    token = strtok_s(nullptr, TEXT("/"), &p);
    if (token == nullptr) {
        SPDLOG_ERROR("CIDR IpAddress string format error: {0}", pCidrIpaddr);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbPrintf(
            cmdBuf,
            MAX_PATH,
            "PowerShell -Command \"& {New-NetIPAddress -InterfaceAlias %s  -IPAddress %s -PrefixLength %s}\"",
            pInterfaceName,
            ip,
            token))) {
        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, nullptr, 0, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG("Run Set IP Command({1}): {0}", cmdBuf, retCode);

    return ERR_SUCCESS;
}

int IsInterfacePrivate(const TCHAR *pInterfaceName, bool *pIsPrivateMode) {
    int   ret;
    DWORD retCode;
    TCHAR cmdBuf[MAX_PATH];
    TCHAR cmdResult[MAX_PATH] = {};

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR("Input pInterfaceName params error: {0}", pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pIsPrivateMode == nullptr) {
        SPDLOG_ERROR("Input pIsPrivateMode params error");
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbPrintf(
            cmdBuf,
            MAX_PATH,
            "PowerShell -Command \"& {Get-NetConnectionProfile -Name %s | Format-List -Property NetworkCategory}\"",
            pInterfaceName))) {
        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, cmdResult, MAX_PATH, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG("Run command [{0}] resutl \'{1}\' return {2}", cmdBuf, cmdResult, retCode);

    if (StrStr(cmdResult, TEXT("Private")) != nullptr) {
        *pIsPrivateMode = true;
        return ERR_SUCCESS;
    } else {
        if (StrStr(cmdResult, TEXT("Public")) != nullptr) {
            *pIsPrivateMode = false;
            return ERR_SUCCESS;
        }
    }

    return -ERR_ITEM_UNEXISTS;
}

int SetInterfacePrivate(const TCHAR *pInterfaceName, bool isPrivate) {
    int   ret;
    TCHAR cmdBuf[MAX_PATH];
    DWORD retCode;

    if (isPrivate) {
        if (FAILED(StringCbPrintf(cmdBuf,
                                  MAX_PATH,
                                  "PowerShell -Command \"& {Get-NetConnectionProfile -InterfaceAlias %s | "
                                  "Set-NetConnectionProfile -NetworkCategory Private}\"",
                                  pInterfaceName))) {
            SPDLOG_ERROR("Format String Error");
            return -ERR_MEMORY_STR;
        }
    } else {
        if (FAILED(StringCbPrintf(cmdBuf,
                                  MAX_PATH,
                                  "PowerShell -Command \"& {Get-NetConnectionProfile -InterfaceAlias %s | "
                                  "Set-NetConnectionProfile -NetworkCategory Public}\"",
                                  pInterfaceName))) {
            SPDLOG_ERROR("Format String Error");
            return -ERR_MEMORY_STR;
        }
    }

    if ((ret = RunCommand(cmdBuf, nullptr, 0, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    if (retCode != 0) {
        SPDLOG_ERROR("PowerShell return error({1}): {0}", cmdBuf, retCode);
        return -ERR_PROCESS_RETURN;
    }

    return ERR_SUCCESS;
}

bool IsCustomNatPSCmdInstalled() {
    TCHAR psCmdPath[MAX_PATH];

    StringCbPrintf(psCmdPath,
                   MAX_PATH,
                   TEXT("%s\\system32\\WindowsPowerShell\\v1.0\\Modules\\wireguard"),
                   GetGlobalCfgInfo()->systemDirectory);

    // �ж� WireGuard NAT �����Ƿ�װ
    if (!PathFileExists(psCmdPath)) {
        if (!CreateDirectory(psCmdPath, nullptr)) {
            return false;
        }
    }

    StringCbCat(psCmdPath, MAX_PATH, "\\wireguard.psm1");

    if (PathFileExists(psCmdPath)) {
        // �ļ�����˵���Ѿ���װ
        return true;
    }

    return false;
}

int IsNetConnectionSharingEnabled(const TCHAR *pInterfaceName, bool *pIsEnabled) {
    int   ret;
    DWORD retCode;
    TCHAR cmdResult[MAX_PATH] = {};
    TCHAR cmdBuf[512];

    if (pInterfaceName == nullptr || lstrlen(pInterfaceName) == 0) {
        SPDLOG_ERROR("Input pInterfaceName params error: {0}", pInterfaceName);
        return -ERR_INPUT_PARAMS;
    }

    if (pIsEnabled == nullptr) {
        SPDLOG_ERROR("Input pIsEnabled params error");
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbPrintf(
            cmdBuf,
            512,
            TEXT("PowerShell -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass Invoke-Command "
                 "-ArgumentList '%s' -ScriptBlock {param($IFNAME);$netShare = New-Object -ComObject HNetCfg.HNetShare;"
                 "$privateConnection = $netShare.EnumEveryConnection |? { $netShare.NetConnectionProps.Invoke($_).Name "
                 "-eq "
                 "'wg_cli' };$privateConfig = "
                 "$netShare.INetSharingConfigurationForINetConnection.Invoke($privateConnection);"
                 "Write-Output $privateConfig}"),
            pInterfaceName))) {

        SPDLOG_ERROR("Format String Error");
        return -ERR_MEMORY_STR;
    }

    if ((ret = RunCommand(cmdBuf, nullptr, 0, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    if (retCode != 0) {
        SPDLOG_ERROR("PowerShell return error({1}): {0}", cmdBuf, retCode);
        return -ERR_PROCESS_RETURN;
    }

    SPDLOG_DEBUG("Run command [{0}] resutl \'{1}\' return {2}", cmdBuf, cmdResult, retCode);

    if (StrStr(cmdResult, TEXT("False")) != nullptr) {
        *pIsEnabled = false;
        return ERR_SUCCESS;
    } else {
        if (StrStr(cmdResult, TEXT("True")) != nullptr) {
            *pIsEnabled = true;
            return ERR_SUCCESS;
        }
    }

    return -ERR_ITEM_UNEXISTS;
}
#endif