#define DBG_TAG "[LFT_INET]"

#include "private/lftpd_inet.h"
#include "dbg_log.h"
#include "net_socket.h"
#include <string.h>

int lftpd_inet_listen(int port)
{
    int retry;
    int s;

    for (retry = 0; retry < 5; retry++) {
        s = net_socket(NET_AF_INET, NET_SOCK_STREAM, 0);
        if (s < 0) {
            if (retry == 0) DBG_ERROR("create socket");
            vTaskDelay(pdMS_TO_TICKS(500));
            continue;
        }

        struct net_sockaddr_in addr;
        addr.sin_family = NET_AF_INET;
        addr.sin_port = net_htons((uint16_t)port);
        addr.sin_addr.s_addr = 0;

        if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) < 0) {
            DBG_ERROR("bind port %d", port);
            net_close(s);
            return -1;
        }

        if (net_listen(s) >= 0) {
            return s;
        }

        net_close(s);
        if (retry < 4) {
            vTaskDelay(pdMS_TO_TICKS(2000));
        }
    }

    DBG_ERROR("listen failed after 5 retries (total 10s wait)");
    return -1;
}

int lftpd_inet_get_socket_port(int socket)
{
    struct net_sockaddr_in addr;
    int addrlen = sizeof(addr);

    if (net_getsockname(socket, (struct net_sockaddr *)&addr, &addrlen) < 0) {
        DBG_ERROR("error getting socket port number");
        return -1;
    }

    return (int)net_ntohs(addr.sin_port);
}

int lftpd_inet_accept(int socket)
{
    while (1) {
        int s = net_accept(socket, NULL, NULL);
        if (s >= 0) {
            return s;
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

int lftpd_inet_accept_timeout(int socket, int timeout_ms)
{
    int elapsed = 0;

    while (elapsed < timeout_ms) {
        int s = net_accept(socket, NULL, NULL);
        if (s >= 0) {
            return s;
        }
        /* 数据连接已被对端关闭时提前退出，不等待超时 */
        net_sock_t *p_sock = net_get_sock(socket);
        if (p_sock == NULL || p_sock->state != NET_SOCK_STATE_LISTENING) {
            DBG_ERROR("data socket closed, accept aborted");
            return -1;
        }
        vTaskDelay(pdMS_TO_TICKS(100));
        elapsed += 100;
    }

    DBG_ERROR("accept timeout (%dms)", timeout_ms);
    return -1;
}

int lftpd_inet_read_line(int socket, char *buffer, size_t buffer_len)
{
    memset(buffer, 0, buffer_len);
    int total_read_len = 0;

    while (total_read_len < (int)buffer_len) {
        int read_len = net_recv(socket,
                                buffer + total_read_len,
                                buffer_len - total_read_len - 1,
                                0);
        if (read_len == 0) {
            return -1;
        }
        if (read_len < 0) {
            return read_len;
        }

        total_read_len += read_len;

        char *p = strstr(buffer, "\r\n");
        if (p != NULL) {
            *p = '\0';
            return 0;
        }
    }

    return -1;
}

int lftpd_inet_write_string(int socket, const char *message)
{
    const char *p = message;
    int length = (int)strlen(message);

    while (length > 0) {
        int write_len = net_send(socket, p, length, 0);
        if (write_len < 0) {
            DBG_ERROR("write error");
            return write_len;
        }
        p += write_len;
        length -= write_len;
    }

    return 0;
}

int lftpd_inet_write(int socket, const void *buf, int len)
{
    const char *p = (const char *)buf;
    int remaining = len;

    while (remaining > 0) {
        int n = net_send(socket, p, remaining, 0);
        if (n < 0) {
            return n;
        }
        p += n;
        remaining -= n;
    }

    return len;
}

int lftpd_inet_read(int socket, void *buf, int len)
{
    int n = net_recv(socket, buf, len, 0);
    return n;
}

int lftpd_inet_close(int socket)
{
    return net_close(socket);
}
