/*
 * 跨平台 TCP 实现：Windows Winsock2 / Linux BSD socket
 */
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include "tcp.h"
#include <stdio.h>
#include <string.h>

#ifdef _WIN32
#pragma comment(lib, "ws2_32.lib")
static int g_wsa_started = 0;
#endif

int Tcp_Init(void)
{
#ifdef _WIN32
    WSADATA wsa;
    if (g_wsa_started)
        return 0;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        return -1;
    g_wsa_started = 1;
#endif
    return 0;
}

void Tcp_Cleanup(void)
{
#ifdef _WIN32
    if (g_wsa_started) {
        WSACleanup();
        g_wsa_started = 0;
    }
#endif
}

static void close_sock(int sock)
{
    if (sock == TCP_INVALID_SOCKET)
        return;
#ifdef _WIN32
    closesocket((SOCKET)sock);
#else
    close(sock);
#endif
}

int TcpClient_Connect(const char *ip, uint16_t port)
{
    struct sockaddr_in addr;
    SOCKET s;

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
#ifdef _WIN32
    addr.sin_addr.s_addr = inet_addr(ip);
    if (addr.sin_addr.s_addr == INADDR_NONE)
        return TCP_INVALID_SOCKET;
#else
    if (inet_pton(AF_INET, ip, &addr.sin_addr) <= 0)
        return TCP_INVALID_SOCKET;
#endif

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == (SOCKET)INVALID_SOCKET)
        return TCP_INVALID_SOCKET;
    if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
        close_sock((int)s);
        return TCP_INVALID_SOCKET;
    }
    return (int)s;
}

int TcpClient_Send(int sock, const void *buf, size_t len)
{
    int n;
    if (sock == TCP_INVALID_SOCKET || !buf)
        return -1;
#ifdef _WIN32
    n = send((SOCKET)sock, (const char *)buf, (int)len, 0);
#else
    n = (int)send(sock, buf, len, 0);
#endif
    return (n == SOCKET_ERROR) ? -1 : n;
}

int TcpClient_Recv(int sock, void *buf, size_t len)
{
    int n;
    if (sock == TCP_INVALID_SOCKET || !buf || len == 0)
        return -1;
#ifdef _WIN32
    n = recv((SOCKET)sock, (char *)buf, (int)len, 0);
#else
    n = (int)recv(sock, buf, len, 0);
#endif
    if (n == SOCKET_ERROR)
        return -1;
    return n;
}

void TcpClient_Close(int sock)
{
    close_sock(sock);
}

int TcpServer_Listen(uint16_t port)
{
    struct sockaddr_in addr;
    SOCKET s;
    int opt = 1;

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == (SOCKET)INVALID_SOCKET)
        return TCP_INVALID_SOCKET;
#ifdef _WIN32
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)) != 0) {
        close_sock((int)s);
        return TCP_INVALID_SOCKET;
    }
#else
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0) {
        close_sock(s);
        return TCP_INVALID_SOCKET;
    }
#endif
    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
        close_sock((int)s);
        return TCP_INVALID_SOCKET;
    }
    if (listen(s, TCP_DEFAULT_BACKLOG) != 0) {
        close_sock((int)s);
        return TCP_INVALID_SOCKET;
    }
    return (int)s;
}

int TcpServer_Accept(int server_sock)
{
    SOCKET client;
    struct sockaddr_in peer;
    socklen_t len = sizeof(peer);

    if (server_sock == TCP_INVALID_SOCKET)
        return TCP_INVALID_SOCKET;
#ifdef _WIN32
    client = accept((SOCKET)server_sock, (struct sockaddr *)&peer, &len);
#else
    client = accept(server_sock, (struct sockaddr *)&peer, &len);
#endif
    if (client == (SOCKET)INVALID_SOCKET)
        return TCP_INVALID_SOCKET;
    return (int)client;
}

void TcpServer_Close(int server_sock)
{
    close_sock(server_sock);
}
