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

struct addrinfo *uc_addrinfo(const char *addr, const char *port,
                             struct addrinfo *hints)
{
    int status;
    struct addrinfo *result;

    if ((status = getaddrinfo(addr, port, hints, &result)))
    {
        perror("getaddrinfo");
        exit(1);
    }

    return result;
}

int uc_socket(struct addrinfo *result, struct addrinfo *hints)
{
    int s = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    return s;
}

int uc_connect(int socket, struct addrinfo *result)
{
    int status;
    if ((status = connect(socket, result->ai_addr, result->ai_addrlen)) == -1)
    {
        perror("connect");
        exit(1);
    }

    return status;
}

void uc_reuseaddr(int socket)
{
    int yes = 1;
    if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1)
    {
        perror("setsockopt");
        exit(1);
    }
}

int uc_server_socket(const char *addr, const char *port)
{
    struct addrinfo hints = {.ai_family   = AF_INET,
                             .ai_socktype = SOCK_DGRAM,
                             .ai_flags    = AI_PASSIVE};
    struct addrinfo *res  = uc_addrinfo(addr, port, &hints);
    int sock              = uc_socket(res, &hints);

    bind(sock, res->ai_addr, res->ai_addrlen);
    uc_reuseaddr(sock);

    listen(sock, 5);
    return sock;
}

int uc_client_socket(const char *addr, const char *port)
{
    struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_DGRAM };
    struct addrinfo *res = uc_addrinfo(addr, port, &hints);
    int sock = uc_socket(res, &hints);
    struct timeval tv;
    tv.tv_sec  = 0;
    tv.tv_usec = 100000;

    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv);
    uc_connect(sock, res);
    return sock;
}

uc_header get_header(uint8_t *buffer)
{
    return *((uc_header *)buffer);
}
