// Envía por TCP varios pedazos de datos conocidos, para estudiar el stream.
//   chunk_send [-g pausa_ms] <ip> <puerto> <tamaño> <cantidad>
// Usa una dirección numérica, sin getaddrinfo, para poder compilarse de forma
// estática y correr en los nodos de la topología.
#define _POSIX_C_SOURCE 200809L

#include "io_util.h"

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

int main(int argc, char *argv[]) {
    long gap_ms = 0;
    int opt;
    while ((opt = getopt(argc, argv, "g:")) != -1) {
        if (opt != 'g') {
            return EXIT_FAILURE;
        }
        gap_ms = strtol(optarg, NULL, 10);
    }
    if (argc - optind != 4) {
        fprintf(stderr, "uso: %s [-g pausa_ms] <ip> <puerto> <tamaño> <cantidad>\n", argv[0]);
        return EXIT_FAILURE;
    }
    long size = strtol(argv[optind + 2], NULL, 10);
    long chunks = strtol(argv[optind + 3], NULL, 10);
    if (size <= 0 || chunks <= 0) {
        fprintf(stderr, "tamaño y cantidad deben ser positivos\n");
        return EXIT_FAILURE;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof addr);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(parse_port(argv[optind + 1]));
    if (inet_pton(AF_INET, argv[optind], &addr.sin_addr) != 1) {
        fprintf(stderr, "dirección no válida: %s\n", argv[optind]);
        return EXIT_FAILURE;
    }

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("socket");
        return EXIT_FAILURE;
    }
    if (connect(fd, (struct sockaddr *)&addr, sizeof addr) == -1) {
        perror("connect");
        return EXIT_FAILURE;
    }

    char *buf = malloc((size_t)size);
    if (buf == NULL) {
        perror("malloc");
        return EXIT_FAILURE;
    }
    // El byte en la posición p del stream vale p % 251. Así el receptor puede
    // comprobar que nada se perdió, se repitió ni cambió de lugar.
    unsigned long long offset = 0;
    for (long i = 0; i < chunks; i++) {
        for (long j = 0; j < size; j++) {
            buf[j] = (char)(offset++ % 251);
        }
        if (send_all(fd, buf, (size_t)size) == -1) {
            return EXIT_FAILURE;
        }
        if (gap_ms > 0) {
            struct timespec pause = {gap_ms / 1000, (gap_ms % 1000) * 1000000L};
            if (nanosleep(&pause, NULL) == -1) {
                perror("nanosleep");
            }
        }
    }
    free(buf);
    printf("enviados: %ld llamadas a send, %llu bytes\n", chunks, offset);
    close_checked(fd);
    return EXIT_SUCCESS;
}
