#include "pch.h"
#include "AZBK_Network.h"
#include "MFCStrUtil.h"

#include <winsock2.h>
#include <iphlpapi.h>

#pragma comment(lib, "IPHLPAPI.lib")

#define WORKING_BUFFER_SIZE 15000
#define MAX_TRIES 3

// получаем список сетевых адаптеров, чтобы выбрать, с какого адаптера брать параметры.

std::vector<AZNetwork::sAdapterName> AZNetwork::getAdaptersList()
{
	std::vector<sAdapterName> vRet;
	// Set the flags to pass to GetAdaptersAddresses
#if defined TARGET_WINXP
	ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
	              | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
#else
	ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
	              | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_INFO;
#endif
	// default to unspecified address family (both)
	ULONG family = AF_INET;
	// Allocate a 15 KB buffer to start with.
	ULONG outBufLen = WORKING_BUFFER_SIZE;
	ULONG Iterations = 0;
	DWORD dwRetVal = 0;
	std::vector<uint8_t> vBuf;
	PIP_ADAPTER_ADDRESSES pAddresses = nullptr;

	do
	{
		vBuf.resize(outBufLen);
		pAddresses = (IP_ADAPTER_ADDRESSES *)vBuf.data();

		if (pAddresses == nullptr)
		{
			TRACE("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
			return vRet;
		}

		dwRetVal = GetAdaptersAddresses(family, flags, nullptr, pAddresses, &outBufLen);
		Iterations++;
	}
	while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));

	if (dwRetVal == NO_ERROR)
	{
		// If successful, output some information from the data we received
		while (pAddresses)
		{
			vRet.push_back(
			{
				MFCStrUtil::Str2WideStr(pAddresses->AdapterName),
				CString(pAddresses->Description)
			});
			pAddresses = pAddresses->Next;
		}
	}
	else
	{
		TRACE("Call to GetAdaptersAddresses failed with error: %d\n", dwRetVal);

		if (dwRetVal == ERROR_NO_DATA)
		{
			TRACE("\tNo addresses were found for the requested parameters\n");
		}
	}

	vBuf.clear();
	return vRet;
}

static void storeIPv4(uint8_t *buf, const int nOffset, char *src)
{
	memcpy(buf + nOffset, src + 2, 4);
}


// получаем параметры выбранного адаптера
// вход: buf - массив из 6 двордов,
//      strAdapterName - имя выбранного адаптера
bool AZNetwork::getNetParameters(uint8_t *buf, CString &strAdapterName)
{
	memset(buf, 0, 24);

	if (strAdapterName.IsEmpty())
	{
		return false;
	}

	// Set the flags to pass to GetAdaptersAddresses
	const ULONG flags = GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
	                    | GAA_FLAG_SKIP_FRIENDLY_NAME;
	// default to unspecified address family (both)
	const ULONG family = AF_INET;
	// Allocate a 15 KB buffer to start with.
	ULONG outBufLen = WORKING_BUFFER_SIZE;
	ULONG Iterations = 0;
	DWORD dwRetVal = 0;
	std::vector<uint8_t> vBuf;
	PIP_ADAPTER_ADDRESSES pAddresses = nullptr;

	do
	{
		vBuf.resize(outBufLen);
		pAddresses = (IP_ADAPTER_ADDRESSES *)vBuf.data();

		if (pAddresses == nullptr)
		{
			TRACE("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
			return false;
		}

		dwRetVal = GetAdaptersAddresses(family, flags, nullptr, pAddresses, &outBufLen);
		Iterations++;
	}
	while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));

	if (dwRetVal == NO_ERROR)
	{
		// If successful, output some information from the data we received
		while (pAddresses)
		{
			CString strName = MFCStrUtil::Str2WideStr(pAddresses->AdapterName);

			if (strName == strAdapterName) // находим нужный адаптер
			{
				// получим IP адрес
				const PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddresses->FirstUnicastAddress;

				if (pUnicast) // берём только первый из списка
				{
					storeIPv4(buf, 0, pUnicast->Address.lpSockaddr->sa_data);
				}

				// получим шлюз
				const PIP_ADAPTER_GATEWAY_ADDRESS pGateway = pAddresses->FirstGatewayAddress;

				if (pGateway) // берём только первый из списка
				{
					storeIPv4(buf, 8, pGateway->Address.lpSockaddr->sa_data);
				}

				// в поле NTP вернём флаг - состояние DHCP
				if (pAddresses->Flags & IP_ADAPTER_DHCP_ENABLED)
				{
					buf[12] = 1;
				}

				// получим dnsы
				const IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = pAddresses->FirstDnsServerAddress;

				if (pDnServer)
				{
					storeIPv4(buf, 16, pDnServer->Address.lpSockaddr->sa_data);
					pDnServer = pDnServer->Next;

					if (pDnServer)
					{
						storeIPv4(buf, 20, pDnServer->Address.lpSockaddr->sa_data);
					}
				}

				// а теперь, из-за того, что в этой монструозной структуре есть куча всего ненужного, но нету маски
				// получим маску другим способом
				ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
				Iterations = 0;
				IP_ADAPTER_INFO *pAdapterInfo = nullptr;

				// Make an initial call to GetAdaptersInfo to get
				// the necessary size into the ulOutBufLen variable

				do
				{
					vBuf.resize(ulOutBufLen);
					pAdapterInfo = (IP_ADAPTER_INFO *)vBuf.data();

					if (pAdapterInfo == nullptr)
					{
						TRACE("Error allocating memory needed to call GetAdaptersinfo\n");
						return false;
					}

					dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
					Iterations++;
				}
				while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));

				if (dwRetVal == NO_ERROR)
				{
					while (pAdapterInfo)
					{
						const CString strName = MFCStrUtil::Str2WideStr(pAdapterInfo->AdapterName);

						if (strName == strAdapterName) // находим нужный адаптер
						{
							char *s = pAdapterInfo->IpAddressList.IpMask.String;
							char *e = nullptr;
							buf[4] = static_cast<uint8_t>(strtol(s, &e, 10));
							s = e + 1;
							buf[5] = static_cast<uint8_t>(strtol(s, &e, 10));
							s = e + 1;
							buf[6] = static_cast<uint8_t>(strtol(s, &e, 10));
							s = e + 1;
							buf[7] = static_cast<uint8_t>(strtol(s, &e, 10));
							vBuf.clear();
							return true;
						}

						pAdapterInfo = pAdapterInfo->Next;
					}
				}

				break;
			}

			pAddresses = pAddresses->Next;
		}
	}
	else
	{
		TRACE("Call to GetAdaptersAddresses failed with error: %d\n", dwRetVal);

		if (dwRetVal == ERROR_NO_DATA)
		{
			TRACE("\tNo addresses were found for the requested parameters\n");
		}
	}

	vBuf.clear();
	return false;
}

// получаем мак адрес выбранного адаптера
// вход: buf - массив куда сохраняется мак,
//      strAdapterName - имя выбранного адаптера
bool AZNetwork::getMACAddress(uint8_t *buf, CString &strAdapterName)
{
	memset(buf, 0, 24);

	if (strAdapterName.IsEmpty())
	{
		return false;
	}

	// Set the flags to pass to GetAdaptersAddresses
#if defined TARGET_WINXP
	const ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
	                    | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
#else
	const ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
	                    | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_INFO;
#endif
	// default to unspecified address family (both)
	const ULONG family = AF_INET;
	// Allocate a 15 KB buffer to start with.
	ULONG outBufLen = WORKING_BUFFER_SIZE;
	ULONG Iterations = 0;
	DWORD dwRetVal = 0;
	PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
	std::vector<uint8_t> vBuf;

	do
	{
		vBuf.resize(outBufLen);
		pAddresses = (IP_ADAPTER_ADDRESSES *)vBuf.data();

		if (pAddresses == nullptr)
		{
			TRACE("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
			return false;
		}

		dwRetVal = GetAdaptersAddresses(family, flags, nullptr, pAddresses, &outBufLen);
		Iterations++;
	}
	while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));

	bool bRet = false;

	if (dwRetVal == NO_ERROR)
	{
		// If successful, output some information from the data we received
		while (pAddresses)
		{
			const CString strName = MFCStrUtil::Str2WideStr(pAddresses->AdapterName);

			if (strName == strAdapterName) // находим нужный адаптер
			{
				// получим мак
				for (ULONG i = 0; i < pAddresses->PhysicalAddressLength; i++)
				{
					buf[i] = pAddresses->PhysicalAddress[i];
				}

				bRet = true;
				break;
			}

			pAddresses = pAddresses->Next;
		}
	}
	else
	{
		TRACE("Call to GetAdaptersAddresses failed with error: %d\n", dwRetVal);

		if (dwRetVal == ERROR_NO_DATA)
		{
			TRACE("No addresses were found for the requested parameters\n");
		}
	}

	vBuf.clear();
	return bRet;
}

