/*
 * Copyright (C) 2021 Simon Brummer <simon.brummer@posteo.de>
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @{
 *
 * @file
 * @brief GNRC implementation of @ref net_sock_tcp
 *
 * @author Simon Brummer <simon.brummer@posteo.de>
 */

#include <assert.h>

#include "net/gnrc/tcp.h"
#include "net/sock/tcp.h"
#include "sock_types.h"

int sock_tcp_connect(sock_tcp_t *sock, const sock_tcp_ep_t *remote,
                     uint16_t local_port, uint16_t flags)
{
    /* Asserts defined by API. */
    assert(sock != NULL);
    assert(remote != NULL);
    assert(remote->port != 0);

    /* Asserts to protect GNRC_TCP. Flags are not supported. */
    assert(flags == 0);
    (void) flags;

    /* Initialize given TCB and try to open a connection */
    gnrc_tcp_tcb_init(sock);

    /* Forward call to gnrc_tcp_open. Return codes are identical except those that are
     * not generated by gnrc_tcp_open: -ENETUNREACH and -EPERM */
    return gnrc_tcp_open(sock, remote, local_port);
}

int sock_tcp_listen(sock_tcp_queue_t *queue, const sock_tcp_ep_t *local,
                    sock_tcp_t *queue_array, unsigned queue_len, uint16_t flags)
{
    /* Asserts defined by API. */
    assert(queue != NULL);
    assert(local != NULL);
    assert(local->port != 0);
    assert(queue_array != NULL);
    assert(queue_len != 0);

    /* Asserts to protect GNRC_TCP. Flags are not supported. */
    assert(flags == 0);
    (void) flags;

    /* Initialize given TCB queue, all given tcbs and forward call */
    gnrc_tcp_tcb_queue_init(queue);
    for (unsigned i = 0; i < queue_len; ++i) {
        gnrc_tcp_tcb_init(&queue_array[i]);
    }
    return gnrc_tcp_listen(queue, queue_array, queue_len, local);
}

void sock_tcp_disconnect(sock_tcp_t *sock)
{
    /* Asserts defined by API. */
    assert(sock != NULL);
    gnrc_tcp_close(sock);
}

void sock_tcp_stop_listen(sock_tcp_queue_t *queue)
{
    /* Asserts defined by API. */
    assert(queue != NULL);
    gnrc_tcp_stop_listen(queue);
}

int sock_tcp_get_local(sock_tcp_t *sock, sock_tcp_ep_t *ep)
{
    /* Asserts defined by API. */
    assert(sock != NULL);
    assert(ep != NULL);
    return gnrc_tcp_get_local(sock, ep);
}

int sock_tcp_get_remote(sock_tcp_t *sock, sock_tcp_ep_t *ep)
{
    /* Asserts defined by API. */
    assert(sock != NULL);
    assert(ep != NULL);
    return gnrc_tcp_get_remote(sock, ep);
}

int sock_tcp_queue_get_local(sock_tcp_queue_t *queue, sock_tcp_ep_t *ep)
{
    /* Asserts defined by API. */
    assert(queue != NULL);
    assert(ep != NULL);
    return gnrc_tcp_queue_get_local(queue, ep);
}

int sock_tcp_accept(sock_tcp_queue_t *queue, sock_tcp_t **sock, uint32_t timeout)
{
    /* Asserts defined by API. */
    assert(queue != NULL);
    assert(sock != NULL);

    /* Map SOCK_NO_TIMEOUT to GNRC_TCP_NO_TIMEOUT */
    if (timeout == SOCK_NO_TIMEOUT) {
        timeout = GNRC_TCP_NO_TIMEOUT;
    }

    /* Forward call to gnrc_tcp_accept.
     * NOTE: Errorcodes -ECONNABORTED, -EPERM are not returned by
     * gnrc_tcp_accept. All other error codes share the same semantics. */
    return gnrc_tcp_accept(queue, sock, timeout);
}

ssize_t sock_tcp_read(sock_tcp_t *sock, void *data, size_t max_len, uint32_t timeout)
{
    /* Asserts defined by API. */
    assert(sock != NULL);
    assert(data != NULL);

    /* Map SOCK_NO_TIMEOUT to GNRC_TCP_NO_TIMEOUT */
    if (timeout == SOCK_NO_TIMEOUT) {
        timeout = GNRC_TCP_NO_TIMEOUT;
    }

    /* Forward call to gnrc_tcp_recv: All error codes share the same semantics */
    return gnrc_tcp_recv(sock, data, max_len, timeout);
}

ssize_t sock_tcp_write(sock_tcp_t *sock, const void *data, size_t len)
{
    /* Asserts defined by API. */
    assert(sock != NULL);
    assert(data != NULL);

    /* Forward call to gnrc_tcp_send.
     * NOTE: gnrc_tcp_send offers a timeout. By setting it to 0, the call blocks
     * until at least some data was transmitted. */
    return gnrc_tcp_send(sock, data, len, 0);
}
