/* lnterposition for fork */
#include "ms_delay.h"

/* Performance parameters */
/* Maximum delay (in milliseconds) */
#define MAX_DELAY 100

/* Global data */
static int max_delay = MAX_DELAY;
static bool initialized = false;

void set_max_delay(int d) {
    max_delay = d;
}

static void initialize(void) {
    if (initialized)
        return;
    srandom(getpid());
    initialized = true;
}

/* Add more entropy to RNG */
static void entropy(void) {
    struct timespec t;
    clock_gettime(CLOCK_REALTIME, &t);
    int n = t.tv_nsec % 25;
    while (n--)
        random();
}

/* Hard delay.  Won't get interrupted by a signal */
void ms_spin(int msecs) {
    struct timespec t;
    long start_ms;
    long curr_ms;
    clock_gettime(CLOCK_REALTIME, &t);
    start_ms = t.tv_sec * 1000 + t.tv_nsec/1000000;
    curr_ms = start_ms;
    while (curr_ms - start_ms < msecs) {
        clock_gettime(CLOCK_REALTIME, &t);
        curr_ms = t.tv_sec * 1000 + t.tv_nsec/1000000;
    }
}

/* Version of sleep that works at millisecond time scale */
void ms_sleep(int msecs, bool spin) {
    if (msecs == 0)
        return;
    if (spin) {
        ms_spin(msecs);
        return;
    }

    struct timespec req;
    long secs = msecs / 1000;
    long nsecs = (msecs - secs * 1000) * 1000000;
    req.tv_sec = secs;
    req.tv_nsec = nsecs;
    if (nanosleep(&req, NULL) < 0) {
        fprintf(stderr, "Couldn't sleep for %d milliseconds\n", msecs);
    }
}

/* Uniform random number in [0.0,1.0] */
static double uniform(void) {
    initialize();
    return (double) random() / RAND_MAX;
}

int choose_delay() {
    entropy();
    return (int) (uniform() * max_delay);
}

bool env_flag(char *name) {
    return getenv(name) != NULL;
}

bool choose_with_probability(double prob) {
    return uniform() <=  prob;
}
