/*
Author: G.D. van Albada
Date:   August 26, 2009
(c) Universiteit van Amsterdam

In this file the data types and some of the functions declared
in the file interval.h for the first assignment in the OS course
for 2009 are defined.
*/

/*
 * Altered 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 intervalData
{
    struct timeval last_wct;   /* Used for wall-clock time */
    struct tms last_cput;      /* Used for CPU time */
} intervalData;

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

    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 delInterval(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 timeInterval(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( delInterval(&id) )
        return -1;

    id = newInterval();

    *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;
}
