#include <net.h>
#include <net_private.h>
#include <net_serial_private.h>
#include <stddef.h>
#include <stdlib.h>

struct net_host *net_connect(const union net_connect *const c)
{
    struct net_host *const h = malloc(sizeof *h);

    if (!h)
        goto failure;

    static struct net_host_domain *(*const f[])(const union net_connect *) =
    {
        [NET_DOMAIN_IPV4] = net_connect_ipv4,
        [NET_DOMAIN_SERIAL] = net_connect_serial
    };

    *h = (const struct net_host)
    {
        .d = c->common.domain
    };

    if (!(h->h = f[h->d](c)))
        goto failure;

    return h;

failure:
    free(h);
    return NULL;
}

int net_write(struct net_host *const h, const net_peer p,
    const void *const buf, const size_t n)
{
    static int (*const f[])(struct net_host_domain *, net_peer,
        const void *, size_t) =
    {
        [NET_DOMAIN_IPV4] = net_write_ipv4,
        [NET_DOMAIN_SERIAL] = net_write_serial
    };

    return f[h->d](h->h, p, buf, n);
}

int net_close(struct net_host *const h)
{
    if (!h)
        return 0;

    static int (*const f[])(struct net_host_domain *) =
    {
        [NET_DOMAIN_IPV4] = net_close_ipv4,
        [NET_DOMAIN_SERIAL] = net_close_serial
    };

    const int res = f[h->d](h->h);

    free(h);
    return res;
}

int net_update(struct net_host *const h)
{
    static int (*const f[])(struct net_host_domain *) =
    {
        [NET_DOMAIN_IPV4] = net_update_ipv4,
        [NET_DOMAIN_SERIAL] = net_update_serial
    };

    return f[h->d](h->h);
}

struct net_host *net_server(const union net_server *const srv)
{
    struct net_host *const h = malloc(sizeof *h);

    if (!h)
        goto failure;

    static struct net_host_domain *(*const f[])(const union net_server *) =
    {
        [NET_DOMAIN_IPV4] = net_server_ipv4,
        [NET_DOMAIN_SERIAL] = net_server_serial
    };

    *h = (const struct net_host)
    {
        .d = srv->common.domain
    };

    if (!(h->h = f[h->d](srv)))
        goto failure;

    return h;

failure:
    net_close(h);
    return NULL;
}

enum net_role net_role(const struct net_host *const h)
{
    static enum net_role (*const f[])(const struct net_host_domain *) =
    {
        [NET_DOMAIN_IPV4] = net_role_ipv4,
        [NET_DOMAIN_SERIAL] = net_role_serial
    };

    return f[h->d](h->h);
}

int net_set_event(struct net_host *const h,
    const struct net_event *const ev)
{
    static int (*const f[])(struct net_host_domain *, const struct net_event *) =
    {
        [NET_DOMAIN_IPV4] = net_set_event_ipv4,
        [NET_DOMAIN_SERIAL] = net_set_event_serial
    };

    return f[h->d](h->h, ev);
}

int net_init(void)
{
    return net_init_ipv4() || net_init_serial();
}

void net_deinit(void)
{
    net_deinit_ipv4();
    net_deinit_serial();
}

bool net_available(const enum net_domain d)
{
    return true;
}
