#include <string.h>

#include "../src/TCP/tcp.h"
#include "../src/thread_utils.h"
#include "test_common.h"

typedef struct {
    int port;
    volatile int ready;
    volatile int done;
    int result;
} loopback_ctx_t;

static void server_fn(void* arg)
{
    loopback_ctx_t* ctx = (loopback_ctx_t*)arg;
    int server = TcpServer_Listen((uint16_t)ctx->port);
    char buf[32] = {0};
    int client;
    int n;

    if (server == TCP_INVALID_SOCKET) {
        ctx->result = 1;
        ctx->done = 1;
        return;
    }

    ctx->ready = 1;
    client = TcpServer_Accept(server);
    if (client == TCP_INVALID_SOCKET) {
        ctx->result = 2;
        TcpServer_Close(server);
        ctx->done = 1;
        return;
    }

    n = TcpClient_Recv(client, buf, sizeof(buf));
    if (n <= 0 || strncmp(buf, "ping", 4) != 0) {
        ctx->result = 3;
    } else {
        TcpClient_Send(client, "pong", 4);
        ctx->result = 0;
    }

    TcpClient_Close(client);
    TcpServer_Close(server);
    ctx->done = 1;
}

int main(void)
{
    loopback_ctx_t ctx;
    thread_handle_t th;
    int client;
    char recv_buf[8] = {0};
    int n;

    memset(&ctx, 0, sizeof(ctx));
    ctx.port = 7013;

    ASSERT_EQ_INT(0, Tcp_Init());
    ASSERT_EQ_INT(0, Thread_Create(server_fn, &ctx, &th));

    while (!ctx.ready) {
        /* wait */
    }

    client = TcpClient_Connect("127.0.0.1", (uint16_t)ctx.port);
    ASSERT_TRUE(client != TCP_INVALID_SOCKET);
    ASSERT_EQ_INT(4, TcpClient_Send(client, "ping", 4));

    n = TcpClient_Recv(client, recv_buf, sizeof(recv_buf));
    ASSERT_EQ_INT(4, n);
    ASSERT_TRUE(strncmp(recv_buf, "pong", 4) == 0);
    TcpClient_Close(client);

    Thread_Join(th);
    ASSERT_EQ_INT(0, ctx.result);
    Tcp_Cleanup();
    return 0;
}
