// trace.h — utilidades para las demostraciones de la semana 11.
//
// TRACE(...)       imprime un evento con el tiempo (ms) desde trace_start().
// progress_bump()  cada hilo avisa que avanzó.
// watchdog_start() un hilo vigila: si nadie avanza en `secs` segundos,
//                  imprime `msg` y termina el programa con código 0
//                  (el error es lo que se quería mostrar).
#ifndef TRACE_H
#define TRACE_H

#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

static struct timespec trace_t0;
static atomic_long progress;
static atomic_int finished;
static int watch_secs;
static const char *watch_msg;
static void (*watch_report)(void);

static inline void trace_start(void) {
    clock_gettime(CLOCK_MONOTONIC, &trace_t0);
}

static inline long now_ms(void) {
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC, &t);
    return (t.tv_sec - trace_t0.tv_sec) * 1000 +
           (t.tv_nsec - trace_t0.tv_nsec) / 1000000;
}

#define TRACE(...)                                  \
    do {                                            \
        flockfile(stdout);                          \
        printf("[%4ld ms] ", now_ms());             \
        printf(__VA_ARGS__);                        \
        printf("\n");                               \
        fflush(stdout);                             \
        funlockfile(stdout);                        \
    } while (0)

static inline void msleep(int ms) { usleep(ms * 1000); }

static inline void progress_bump(void) { atomic_fetch_add(&progress, 1); }

static inline void *watchdog(void *arg) {
    (void)arg;
    long last = atomic_load(&progress);
    int quiet = 0;                      // décimas de segundo sin avance
    while (!atomic_load(&finished)) {
        msleep(100);
        long p = atomic_load(&progress);
        if (p != last) {
            last = p;
            quiet = 0;
        } else if (++quiet >= watch_secs * 10) {
            if (atomic_load(&finished))
                break;
            TRACE("WATCHDOG: %d s sin avance.", watch_secs);
            printf("          %s\n", watch_msg);
            if (watch_report)
                watch_report();
            exit(0);                    // mata a los hilos dormidos
        }
    }
    return NULL;
}

static inline void watchdog_start(int secs, const char *msg,
                                  void (*report)(void)) {
    pthread_t w;
    watch_secs = secs;
    watch_msg = msg;
    watch_report = report;
    pthread_create(&w, NULL, watchdog, NULL);
    pthread_detach(w);
}

static inline void watchdog_done(void) { atomic_store(&finished, 1); }

#endif
