/*
 * Created by Sander van Veen (UvA ID: 6167969)
 */

/* interval is a pointer to a struct used by the functions
 * in the file interval.c. As such it constitutes an opaque data type
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/times.h>
#include <limits.h>
#include "interval.h"
#include <math.h>

/*
 * Over the years the number of clock ticks per second has been called by many
 * names. The code give here appears to work on most machines. Use MY_CLK_TCK
 * to convert the output of times() to seconds.
 */

static clock_t MY_CLK_TCK = 0;

typedef struct interval_t {
    struct timeval last_wct;   /* Used for wall-clock time */
    struct tms last_cput;      /* Used for CPU time */
} interval_t;

/*
 * The function new_interval creates and initialises a new interval_t structure
 * and returns a pointer to this structure.
 */
interval interval_init(void) {
    interval nw = calloc(1, sizeof(interval_t));

    if( !nw )
        return NULL;

    gettimeofday(&(nw->last_wct), NULL);
    times(&(nw->last_cput));

    if( !MY_CLK_TCK )
        MY_CLK_TCK = sysconf(_SC_CLK_TCK);

    return nw;
}

/*
 * Free the memory used by the interval pointer and set the pointer to NULL. If
 * the pointer is valid, zero is returned, or 1 otherwise.
 */
int interval_destroy(interval *intervalPtr) {
    if( intervalPtr == NULL )
        return 1;

    free(*intervalPtr);
    *intervalPtr = NULL;

    return 0;
}

/*
 * Returns the wall clock time, user CPU time and system CPU time for the
 * calling process and its children consumed since the previous call for the
 * specified interval. Returns zero on success, -1 when an invalid pointer is
 * passed as argument.
 */
int interval_time(interval id, double *wct, double *ust, double *syt) {
    if( !id || wct == NULL || ust == NULL || syt == NULL )
        return -1;

    struct timeval last_wct = id->last_wct;
    struct tms last_cput = id->last_cput;

    if( interval_destroy(&id) )
        return -1;

    id = interval_init();

    *wct = (id->last_wct.tv_sec + id->last_wct.tv_usec / 1e6)
        - (last_wct.tv_sec + last_wct.tv_usec / 1e6);
    *ust = difftime(id->last_cput.tms_utime, last_cput.tms_utime) / MY_CLK_TCK;
    *syt = difftime(id->last_cput.tms_stime, last_cput.tms_stime) / MY_CLK_TCK;

    return 0;
}
