// slow_reader: un servidor TCP que lee poco a poco.
//
//   slow_reader <puerto> <rcvbuf> <bytes_por_lectura> <pausa_ms>
//
// Completen los TODO. Cuando ya no quede ninguno, make test pone a prueba este programa.
#define _POSIX_C_SOURCE 200809L
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>

static void pause_ms(long ms) {
    struct timespec ts = {ms / 1000, (ms % 1000) * 1000000L};
    while (nanosleep(&ts, &ts) < 0) {
        if (errno != EINTR) {
            perror("nanosleep");
            exit(1);
        }
    }
}

int main(int argc, char **argv) {
    if (argc != 5) {
        fprintf(stderr, "uso: %s <puerto> <rcvbuf> <bytes_por_lectura> <pausa_ms>\n", argv[0]);
        return 2;
    }
    int rcvbuf = atoi(argv[2]);
    size_t chunk = (size_t)atol(argv[3]);
    long pause = atol(argv[4]);
    char *buf = malloc(chunk);
    if (buf == NULL) {
        perror("malloc");
        return 1;
    }

    int listener = socket(AF_INET, SOCK_STREAM, 0);
    if (listener < 0) {
        perror("socket");
        return 1;
    }
    int one = 1;
    if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0) {
        perror("setsockopt SO_REUSEADDR");
        return 1;
    }

    // TODO 1: fijen SO_RCVBUF con el valor de rcvbuf. Piensen si debe ir antes o después de
    // listen: ¿en qué momento se decide la escala de la ventana?
    (void)rcvbuf;

    struct sockaddr_in addr = {.sin_family = AF_INET,
                               .sin_port = htons((uint16_t)atoi(argv[1])),
                               .sin_addr.s_addr = htonl(INADDR_ANY)};
    if (bind(listener, (struct sockaddr *)&addr, sizeof addr) < 0) {
        perror("bind");
        return 1;
    }
    if (listen(listener, 1) < 0) {
        perror("listen");
        return 1;
    }
    int fd = accept(listener, NULL, NULL);
    if (fd < 0) {
        perror("accept");
        return 1;
    }

    long long total = 0;

    // TODO 2: lean hasta que el otro extremo cierre, a lo sumo chunk bytes por vez, con una
    // pausa de pause milisegundos después de cada lectura. Sumen a total lo que lean. Revisen
    // los tres casos que puede devolver read.
    (void)pause_ms;
    (void)chunk;
    (void)pause;

    if (close(fd) < 0 || close(listener) < 0) {
        perror("close");
        return 1;
    }
    free(buf);
    printf("recibidos %lld bytes\n", total);
    return 0;
}
