// Servidor TCP de conteo: atiende a un cliente a la vez.
//   wc_server <puerto>     (con el puerto 0, el sistema elige uno libre)
#define _POSIX_C_SOURCE 200809L

#include "io_util.h"
#include "wc_service.h"

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "uso: %s <puerto>\n", argv[0]);
        return EXIT_FAILURE;
    }
    uint16_t port = parse_port(argv[1]);

    // 1. Un socket TCP sobre IPv4.
    int listener = socket(AF_INET, SOCK_STREAM, 0);
    if (listener == -1) {
        perror("socket");
        return EXIT_FAILURE;
    }

    // 2. Permitir reusar el puerto aunque queden conexiones en TIME_WAIT.
    int on = 1;
    if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) == -1) {
        perror("setsockopt");
        return EXIT_FAILURE;
    }

    // 3. Asociarlo a un puerto, en todas las interfaces. Orden de red.
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof addr);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(port);
    if (bind(listener, (struct sockaddr *)&addr, sizeof addr) == -1) {
        perror("bind");
        return EXIT_FAILURE;
    }

    // 4. Marcarlo como pasivo: desde aquí el kernel completa handshakes.
    if (listen(listener, 16) == -1) {
        perror("listen");
        return EXIT_FAILURE;
    }
    if (print_bound_port(listener) == -1) {
        return EXIT_FAILURE;
    }

    // 5. Un socket nuevo por cada conexión; listener sigue escuchando.
    for (;;) {
        struct sockaddr_in peer;
        socklen_t peer_len = sizeof peer;
        int conn = accept(listener, (struct sockaddr *)&peer, &peer_len);
        if (conn == -1) {
            perror("accept");
            continue;
        }
        char ip[INET_ADDRSTRLEN];
        if (inet_ntop(AF_INET, &peer.sin_addr, ip, sizeof ip) == NULL) {
            perror("inet_ntop");
            close_checked(conn);
            continue;
        }
        printf("cliente %s:%u\n", ip, ntohs(peer.sin_port));
        fflush(stdout);

        // Mientras se atiende a este cliente, nadie más es atendido.
        if (wc_serve_client(conn) == -1) {
            fprintf(stderr, "se abandona al cliente %s\n", ip);
        }
        close_checked(conn);
    }
}
