// Recibe datagramas numerados y cuenta perdidos, desordenados y repetidos.
//   seq_recv <puerto> <cantidad_esperada> <espera_ms>
// Termina cuando pasan espera_ms sin que llegue nada, después del primero.
#define _POSIX_C_SOURCE 200809L

#include "io_util.h"

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

int main(int argc, char *argv[]) {
    if (argc != 4) {
        fprintf(stderr, "uso: %s <puerto> <cantidad_esperada> <espera_ms>\n", argv[0]);
        return EXIT_FAILURE;
    }
    long expected = strtol(argv[2], NULL, 10);
    long wait_ms = strtol(argv[3], NULL, 10);
    if (expected <= 0 || wait_ms <= 0) {
        fprintf(stderr, "valores fuera de rango\n");
        return EXIT_FAILURE;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd == -1) {
        perror("socket");
        return EXIT_FAILURE;
    }
    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(parse_port(argv[1]));
    if (bind(fd, (struct sockaddr *)&addr, sizeof addr) == -1) {
        perror("bind");
        return EXIT_FAILURE;
    }
    if (print_bound_port(fd) == -1) {
        return EXIT_FAILURE;
    }
    bool *seen = calloc((size_t)expected, sizeof *seen);
    if (seen == NULL) {
        perror("calloc");
        return EXIT_FAILURE;
    }

    long received = 0, duplicates = 0, reordered = 0, other_size = 0;
    long first_size = 0;
    long highest = -1;
    for (;;) {
        char packet[2048];
        ssize_t n = recv(fd, packet, sizeof packet, 0);
        if (n == -1) {
            if (errno == EINTR) {
                continue;
            }
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                break;
            }
            perror("recv");
            return EXIT_FAILURE;
        }
        if (received == 0 && duplicates == 0) {
            // Ya empezó el envío: desde aquí, un silencio largo significa el fin.
            struct timeval limit = {wait_ms / 1000, (wait_ms % 1000) * 1000};
            if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &limit, sizeof limit) == -1) {
                perror("setsockopt");
                return EXIT_FAILURE;
            }
            first_size = n;
        }
        if (n < (ssize_t)sizeof(uint32_t)) {
            continue;
        }
        if (n != first_size) {
            other_size++;
        }
        uint32_t net_seq;
        memcpy(&net_seq, packet, sizeof net_seq);
        long seq = (long)ntohl(net_seq);
        if (seq >= expected) {
            continue;
        }
        if (seen[seq]) {
            duplicates++;
            continue;
        }
        seen[seq] = true;
        received++;
        if (seq < highest) {
            reordered++;
        } else {
            highest = seq;
        }
    }
    printf("recibidos: %ld de %ld\n", received, expected);
    printf("perdidos: %ld\n", expected - received);
    printf("fuera de orden: %ld\n", reordered);
    printf("repetidos: %ld\n", duplicates);
    printf("tamaño de cada recv: %ld bytes, %ld distintos\n", first_size, other_size);
    free(seen);
    close_checked(fd);
    return EXIT_SUCCESS;
}
