#include "wj_lanip.h"
#include <cstring>
#include <cstdio>
#include <cstdint>

#if defined(__WINDOWS__) || defined(__NT__) || defined(_MSC_VER) || defined(_WIN32)
#ifndef __WIN32__
#define __WIN32__
#endif
#endif

static bool IsPrivateIPv4(uint32_t hostOrderAddr)
{
	uint8_t a = uint8_t(hostOrderAddr >> 24);
	uint8_t b = uint8_t(hostOrderAddr >> 16);
	if (a == 10) return true;											// 10.0.0.0/8
	if (a == 172 && (b & 0xF0) == 16) return true;						// 172.16.0.0/12
	if (a == 192 && b == 168) return true;								// 192.168.0.0/16
	return false;
}

#ifdef __WIN32__

#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")

std::vector<std::string> WJ_GetLocalIPv4Addresses()
{
	std::vector<std::string> result;

	ULONG bufLen = 16384;
	std::vector<uint8_t> buf(bufLen);
	PIP_ADAPTER_ADDRESSES addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buf.data());

	ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
	DWORD ret = GetAdaptersAddresses(AF_INET, flags, nullptr, addresses, &bufLen);
	if (ret == ERROR_BUFFER_OVERFLOW)
	{
		buf.resize(bufLen);
		addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buf.data());
		ret = GetAdaptersAddresses(AF_INET, flags, nullptr, addresses, &bufLen);
	}
	if (ret != NO_ERROR)
		return result;

	for (auto adapter = addresses; adapter != nullptr; adapter = adapter->Next)
	{
		if (adapter->OperStatus != IfOperStatusUp)
			continue;
		if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
			continue;

		for (auto ua = adapter->FirstUnicastAddress; ua != nullptr; ua = ua->Next)
		{
			sockaddr_in *sin = reinterpret_cast<sockaddr_in *>(ua->Address.lpSockaddr);
			if (sin->sin_family != AF_INET)
				continue;

			uint32_t hostOrder = ntohl(sin->sin_addr.s_addr);
			if (!IsPrivateIPv4(hostOrder))
				continue;

			char text[INET_ADDRSTRLEN];
			if (inet_ntop(AF_INET, &sin->sin_addr, text, sizeof(text)))
				result.push_back(text);
		}
	}
	return result;
}

#else // POSIX

#include <ifaddrs.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>

std::vector<std::string> WJ_GetLocalIPv4Addresses()
{
	std::vector<std::string> result;

	ifaddrs *ifaddr = nullptr;
	if (getifaddrs(&ifaddr) != 0)
		return result;

	for (ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
	{
		if (ifa->ifa_addr == nullptr || ifa->ifa_addr->sa_family != AF_INET)
			continue;
		if (!(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_RUNNING))
			continue;
		if (ifa->ifa_flags & IFF_LOOPBACK)
			continue;

		auto sin = reinterpret_cast<sockaddr_in *>(ifa->ifa_addr);
		uint32_t hostOrder = ntohl(sin->sin_addr.s_addr);
		if (!IsPrivateIPv4(hostOrder))
			continue;

		char text[INET_ADDRSTRLEN];
		if (inet_ntop(AF_INET, &sin->sin_addr, text, sizeof(text)))
			result.push_back(text);
	}

	freeifaddrs(ifaddr);
	return result;
}

#endif
