#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>

#include "dyn_conf.h"
#include "dyn_core.h"
#include "dyn_node_snitch.h"
#include "dyn_string.h"
#include "dyn_util.h"

static unsigned char *broadcast_address = NULL;
static char *public_hostname = NULL;
static char *public_ip4 = NULL;
static char *private_ip4 = NULL;

static bool is_aws_env(struct server_pool *sp) {
  return dn_strncmp(&sp->env.data, CONF_DEFAULT_ENV, 3);
}

static unsigned char *hostname_to_ip(char *hostname) {
  struct hostent *he;
  struct in_addr **addr_list;
  int i;

  if ((he = gethostbyname(hostname)) == NULL) {
    return NULL;
  }

  addr_list = (struct in_addr **)he->h_addr_list;
  for (i = 0; addr_list[i] != NULL; i++)
    ;

  unsigned char *ip = dn_alloc(i);

  for (i = 0; addr_list[i] != NULL; i++) {
    // Return the first one;
    strcpy(ip, inet_ntoa(*(0 + addr_list[i])));
    return ip;
  }

  return NULL;
}

unsigned char *get_broadcast_address(struct server_pool *sp) {
  if (broadcast_address != NULL) return broadcast_address;

  if (is_aws_env(sp)) {
    broadcast_address = getenv("EC2_PUBLIC_HOSTNAME");
    if (broadcast_address != NULL) return broadcast_address;
  } else {
    broadcast_address = getenv("PUBLIC_HOSTNAME");
    if (broadcast_address != NULL) return broadcast_address;
  }

  struct node *peer = *(struct node **)array_get(&sp->peers, 0);
  broadcast_address = (char *)peer->name.data;
  return broadcast_address;
}

char *get_public_hostname(struct server_pool *sp) {
  if (public_hostname != NULL) return public_hostname;

  if (is_aws_env(sp)) {
    public_hostname = getenv("EC2_PUBLIC_HOSTNAME");
    if (public_hostname != NULL) return public_hostname;
  } else {
    public_hostname = getenv("PUBLIC_HOSTNAME");
    if (public_hostname != NULL) return public_hostname;
  }

  struct node *peer = *(struct node **)array_get(&sp->peers, 0);
  if ((peer != NULL) && (peer->name.data != NULL)) {
    char c = (char)peer->name.data[0];
    if (!isdigit(c)) {
        public_hostname = (char *)peer->name.data;
        return public_hostname;
    }
  }
  return NULL;
}

char *get_public_ip4(struct server_pool *sp) {
  if (public_ip4 != NULL) return public_ip4;

  if (is_aws_env(sp)) {
    public_ip4 = getenv("EC2_PUBLIC_IPV4");
    if (public_ip4 != NULL) return public_ip4;
  } else {
    public_ip4 = getenv("PUBLIC_IPV4");
    if (public_ip4 != NULL) return public_ip4;
  }

  struct node *peer = *(struct node **)array_get(&sp->peers, 0);
  if ((peer != NULL) && (peer->name.data != NULL)) {
    char c = (char)peer->name.data[0];
    if (isdigit(c)) return (char *)peer->name.data;
  }
  return NULL;
}

char *get_private_ip4(struct server_pool *sp) {
  if (private_ip4 != NULL) return private_ip4;

  if (is_aws_env(sp)) {
    private_ip4 = getenv("EC2_LOCAL_IPV4");
    if (private_ip4 != NULL) return private_ip4;
  } else {
    private_ip4 = getenv("LOCAL_IPV4");
    if (private_ip4 != NULL) return private_ip4;
  }
  return NULL;
}

unsigned char *hostname_to_private_ip4(char *hostname) {
  return hostname_to_ip(hostname);
}
