#if defined(__MINGW32__) && !defined(_WIN64)
/*
 * Contains routines missing from WS2_32.DLL until 2006, if yer using
 * Microsoft C/C++, then this code is irrelevant, as the official
 * Platform SDK already links against these routines in the correct
 * libraries.
 *
 * -despair86 30/07/18
 */

// these need to be in a specific order
#include <assert.h>
#include <net/net.h>
#include <windows.h>
#include <iphlpapi.h>
#if WINNT_CROSS_COMPILE && !NTSTATUS
typedef LONG NTSTATUS;
#endif
#include "win32_intrnl.h"

const char *
inet_ntop(int af, const void *src, char *dst, size_t size)
{
  int address_length;
  DWORD string_length = size;
  struct sockaddr_storage sa;
  struct sockaddr_in *sin   = (struct sockaddr_in *)&sa;
  struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa;

  memset(&sa, 0, sizeof(sa));
  switch(af)
  {
    case AF_INET:
      address_length  = sizeof(struct sockaddr_in);
      sin->sin_family = af;
      memcpy(&sin->sin_addr, src, sizeof(struct in_addr));
      break;

    case AF_INET6:
      address_length    = sizeof(struct sockaddr_in6);
      sin6->sin6_family = af;
      memcpy(&sin6->sin6_addr, src, sizeof(struct in6_addr));
      break;

    default:
      return NULL;
  }

  if(WSAAddressToString((LPSOCKADDR)&sa, address_length, NULL, dst,
                        &string_length)
     == 0)
  {
    return dst;
  }

  return NULL;
}

int
inet_pton(int af, const char *src, void *dst)
{
  int address_length;
  struct sockaddr_storage sa;
  struct sockaddr_in *sin   = (struct sockaddr_in *)&sa;
  struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa;

  switch(af)
  {
    case AF_INET:
      address_length = sizeof(struct sockaddr_in);
      break;

    case AF_INET6:
      address_length = sizeof(struct sockaddr_in6);
      break;

    default:
      return -1;
  }

  if(WSAStringToAddress((LPTSTR)src, af, NULL, (LPSOCKADDR)&sa, &address_length)
     == 0)
  {
    switch(af)
    {
      case AF_INET:
        memcpy(dst, &sin->sin_addr, sizeof(struct in_addr));
        break;

      case AF_INET6:
        memcpy(dst, &sin6->sin6_addr, sizeof(struct in6_addr));
        break;
    }
    return 1;
  }

  return 0;
}

typedef struct _InterfaceIndexTable
{
  DWORD numIndexes;
  IF_INDEX indexes[1];
} InterfaceIndexTable;

// windows 2000
// todo(despair86): implement IPv6 detection using
// the ipv6 preview stack/adv net pack from 1999/2001
DWORD FAR PASCAL
_GetAdaptersAddresses(ULONG Family, ULONG Flags, PVOID Reserved,
                      PIP_ADAPTER_ADDRESSES pAdapterAddresses,
                      PULONG pOutBufLen)
{
  InterfaceIndexTable *indexTable;
  IFInfo ifInfo;
  int i;
  ULONG ret, requiredSize = 0;
  PIP_ADAPTER_ADDRESSES currentAddress;
  PUCHAR currentLocation;
  HANDLE tcpFile;

  (void)(Family);
  if(!pOutBufLen)
    return ERROR_INVALID_PARAMETER;
  if(Reserved)
    return ERROR_INVALID_PARAMETER;

  indexTable = getInterfaceIndexTable();
  if(!indexTable)
    return ERROR_NOT_ENOUGH_MEMORY;

  ret = openTcpFile(&tcpFile, FILE_READ_DATA);
  if(!NT_SUCCESS(ret))
    return ERROR_NO_DATA;

  for(i = indexTable->numIndexes; i >= 0; i--)
  {
    if(NT_SUCCESS(
           getIPAddrEntryForIf(tcpFile, NULL, indexTable->indexes[i], &ifInfo)))
    {
      /* The whole struct */
      requiredSize += sizeof(IP_ADAPTER_ADDRESSES);

      /* Friendly name */
      if(!(Flags & GAA_FLAG_SKIP_FRIENDLY_NAME))
        requiredSize +=
            strlen((char *)ifInfo.if_info.ent.if_descr) + 1;  // FIXME

      /* Adapter name */
      requiredSize += strlen((char *)ifInfo.if_info.ent.if_descr) + 1;

      /* Unicast address */
      if(!(Flags & GAA_FLAG_SKIP_UNICAST))
        requiredSize += sizeof(IP_ADAPTER_UNICAST_ADDRESS);

      /* FIXME: Implement multicast, anycast, and dns server stuff */

      /* FIXME: Implement dns suffix and description */
      requiredSize += 2 * sizeof(WCHAR);

      /* We're only going to implement what's required for XP SP0 */
    }
  }
#ifdef DEBUG
  fprintf(stderr, "size: %ld, requiredSize: %ld\n", *pOutBufLen, requiredSize);
#endif
  if(!pAdapterAddresses || *pOutBufLen < requiredSize)
  {
    *pOutBufLen = requiredSize;
    closeTcpFile(tcpFile);
    free(indexTable);
    return ERROR_BUFFER_OVERFLOW;
  }

  RtlZeroMemory(pAdapterAddresses, requiredSize);

  /* Let's set up the pointers */
  currentAddress = pAdapterAddresses;
  for(i = indexTable->numIndexes; i >= 0; i--)
  {
    if(NT_SUCCESS(
           getIPAddrEntryForIf(tcpFile, NULL, indexTable->indexes[i], &ifInfo)))
    {
      currentLocation =
          (PUCHAR)currentAddress + (ULONG_PTR)sizeof(IP_ADAPTER_ADDRESSES);

      /* FIXME: Friendly name */
      if(!(Flags & GAA_FLAG_SKIP_FRIENDLY_NAME))
      {
        currentAddress->FriendlyName = (PVOID)currentLocation;
        currentLocation += sizeof(WCHAR);
      }

      /* Adapter name */
      currentAddress->AdapterName = (PVOID)currentLocation;
      currentLocation += strlen((char *)ifInfo.if_info.ent.if_descr) + 1;

      /* Unicast address */
      if(!(Flags & GAA_FLAG_SKIP_UNICAST))
      {
        currentAddress->FirstUnicastAddress = (PVOID)currentLocation;
        currentLocation += sizeof(IP_ADAPTER_UNICAST_ADDRESS);
        currentAddress->FirstUnicastAddress->Address.lpSockaddr =
            (PVOID)currentLocation;
        currentLocation += sizeof(struct sockaddr);
      }

      /* FIXME: Implement multicast, anycast, and dns server stuff */

      /* FIXME: Implement dns suffix and description */
      currentAddress->DnsSuffix = (PVOID)currentLocation;
      currentLocation += sizeof(WCHAR);

      currentAddress->Description = (PVOID)currentLocation;
      currentLocation += sizeof(WCHAR);

      currentAddress->Next = (PVOID)currentLocation;
      /* Terminate the last address correctly */
      if(i == 0)
        currentAddress->Next = NULL;

      /* We're only going to implement what's required for XP SP0 */

      currentAddress = currentAddress->Next;
    }
  }

  /* Now again, for real this time */

  currentAddress = pAdapterAddresses;
  for(i = indexTable->numIndexes; i >= 0; i--)
  {
    if(NT_SUCCESS(
           getIPAddrEntryForIf(tcpFile, NULL, indexTable->indexes[i], &ifInfo)))
    {
      /* Make sure we're not looping more than we hoped for */
      assert(currentAddress);

      /* Alignment information */
      currentAddress->Length  = sizeof(IP_ADAPTER_ADDRESSES);
      currentAddress->IfIndex = indexTable->indexes[i];

      /* Adapter name */
      strcpy(currentAddress->AdapterName, (char *)ifInfo.if_info.ent.if_descr);

      if(!(Flags & GAA_FLAG_SKIP_UNICAST))
      {
        currentAddress->FirstUnicastAddress->Length =
            sizeof(IP_ADAPTER_UNICAST_ADDRESS);
        currentAddress->FirstUnicastAddress->Flags = 0;  // FIXME
        currentAddress->FirstUnicastAddress->Next =
            NULL;  // FIXME: Support more than one address per adapter
        currentAddress->FirstUnicastAddress->Address.lpSockaddr->sa_family =
            AF_INET;
        memcpy(currentAddress->FirstUnicastAddress->Address.lpSockaddr->sa_data,
               &ifInfo.ip_addr.iae_addr, sizeof(ifInfo.ip_addr.iae_addr));
        currentAddress->FirstUnicastAddress->Address.iSockaddrLength =
            sizeof(ifInfo.ip_addr.iae_addr) + sizeof(USHORT);
        currentAddress->FirstUnicastAddress->PrefixOrigin =
            IpPrefixOriginOther;  // FIXME
        currentAddress->FirstUnicastAddress->SuffixOrigin =
            IpSuffixOriginOther;  // FIXME
        currentAddress->FirstUnicastAddress->DadState =
            IpDadStatePreferred;  // FIXME
        currentAddress->FirstUnicastAddress->ValidLifetime =
            0xFFFFFFFF;  // FIXME
        currentAddress->FirstUnicastAddress->PreferredLifetime =
            0xFFFFFFFF;  // FIXME
        currentAddress->FirstUnicastAddress->LeaseLifetime =
            0xFFFFFFFF;  // FIXME
      }

      /* FIXME: Implement multicast, anycast, and dns server stuff */
      currentAddress->FirstAnycastAddress   = NULL;
      currentAddress->FirstMulticastAddress = NULL;
      currentAddress->FirstDnsServerAddress = NULL;

      /* FIXME: Implement dns suffix, description, and friendly name */
      currentAddress->DnsSuffix[0]    = UNICODE_NULL;
      currentAddress->Description[0]  = UNICODE_NULL;
      currentAddress->FriendlyName[0] = UNICODE_NULL;

      /* Physical Address */
      memcpy(currentAddress->PhysicalAddress, ifInfo.if_info.ent.if_physaddr,
             ifInfo.if_info.ent.if_physaddrlen);
      currentAddress->PhysicalAddressLength = ifInfo.if_info.ent.if_physaddrlen;

      /* Flags */
      currentAddress->Flags = 0;  // FIXME

      /* MTU */
      currentAddress->Mtu = ifInfo.if_info.ent.if_mtu;

      /* Interface type */
      currentAddress->IfType = ifInfo.if_info.ent.if_type;

      /* Operational status */
      if(ifInfo.if_info.ent.if_operstatus >= IF_OPER_STATUS_CONNECTING)
        currentAddress->OperStatus = IfOperStatusUp;
      else
        currentAddress->OperStatus = IfOperStatusDown;

      /* We're only going to implement what's required for XP SP0 */

      /* Move to the next address */
      currentAddress = currentAddress->Next;
    }
  }

  closeTcpFile(tcpFile);
  free(indexTable);

  return NO_ERROR;
}
#endif
