// Servidor TCP que muestra qué socket atiende cada conexión.
//   tcp_demux <puerto>
// El socket que escucha nunca recibe datos: cada conexión aceptada tiene el suyo.
#include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define MAX_CONNECTIONS 16
#define MAX_MESSAGE 1500

static void describe_peer(int fd, char *text, size_t size) {
    struct sockaddr_in peer;
    socklen_t len = sizeof peer;
    char ip[INET_ADDRSTRLEN] = "?";
    if (getpeername(fd, (struct sockaddr *)&peer, &len) == -1) {
        perror("getpeername");
        snprintf(text, size, "?");
        return;
    }
    if (inet_ntop(AF_INET, &peer.sin_addr, ip, sizeof ip) == NULL) {
        perror("inet_ntop");
    }
    snprintf(text, size, "%s:%u", ip, ntohs(peer.sin_port));
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "uso: %s <puerto>\n", argv[0]);
        return EXIT_FAILURE;
    }
    int port = atoi(argv[1]);
    if (port <= 0 || port > 65535) {
        fprintf(stderr, "puerto no válido: %s\n", argv[1]);
        return EXIT_FAILURE;
    }
    if (setvbuf(stdout, NULL, _IOLBF, 0) != 0) {
        perror("setvbuf");
    }
    int listener = socket(AF_INET, SOCK_STREAM, 0);
    if (listener == -1) {
        perror("socket");
        return EXIT_FAILURE;
    }
    int yes = 1;
    if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
        perror("setsockopt");
        return EXIT_FAILURE;
    }
    struct sockaddr_in local;
    memset(&local, 0, sizeof local);
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htonl(INADDR_ANY);
    local.sin_port = htons((uint16_t)port);
    if (bind(listener, (struct sockaddr *)&local, sizeof local) == -1) {
        perror("bind");
        return EXIT_FAILURE;
    }
    if (listen(listener, 8) == -1) {
        perror("listen");
        return EXIT_FAILURE;
    }
    printf("socket %d  escuchando en el puerto %d\n", listener, port);

    struct pollfd watched[MAX_CONNECTIONS + 1];
    watched[0] = (struct pollfd){.fd = listener, .events = POLLIN};
    nfds_t count = 1;

    for (;;) {
        if (poll(watched, count, -1) == -1) {
            perror("poll");
            return EXIT_FAILURE;
        }
        char peer[64];
        if ((watched[0].revents & POLLIN) && count <= MAX_CONNECTIONS) {
            int fd = accept(listener, NULL, NULL);
            if (fd == -1) {
                perror("accept");
            } else {
                describe_peer(fd, peer, sizeof peer);
                printf("socket %d  conexión nueva de %s\n", fd, peer);
                watched[count++] = (struct pollfd){.fd = fd, .events = POLLIN};
            }
        }
        for (nfds_t i = 1; i < count; i++) {
            if (!(watched[i].revents & (POLLIN | POLLHUP))) {
                continue;
            }
            char data[MAX_MESSAGE];
            ssize_t got = recv(watched[i].fd, data, sizeof data - 1, 0);
            if (got == -1) {
                perror("recv");
            }
            if (got <= 0) {
                if (close(watched[i].fd) == -1) {
                    perror("close");
                }
                watched[i] = watched[--count];
                i--;
                continue;
            }
            data[got] = '\0';
            describe_peer(watched[i].fd, peer, sizeof peer);
            printf("socket %d  de %s  \"%s\"\n", watched[i].fd, peer, data);
            char reply[64];
            int len = snprintf(reply, sizeof reply, "recibido en el socket %d", watched[i].fd);
            if (send(watched[i].fd, reply, (size_t)len, 0) == -1) {
                perror("send");
            }
        }
    }
}
