/* timer.c
 *
 * $Id: timer.c,v 1.8 2000/12/05 03:53:59 mdiallo Exp $
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <values.h>
#include <sys/time.h>
#include "timer.h"

/* The purpose of the base_tv structure is to provide
 * an earliest time before which nothing should start.
 */
static struct timespec base_tv;

static int ctr, errctr;
static long chunk_ms, start_ms;

long tv_to_msec(struct timespec *t_tv) {
        long since;
        long usince;

        since = t_tv->tv_sec - base_tv.tv_sec;
        usince = (t_tv->tv_nsec - base_tv.tv_nsec)/1000000;
        usince += since * 1000;
        return usince;
}

/* Return the number of milliseconds between chunks given
 * a chunk size in bytes, and a chunk rate in bits per second.
 */
long chunk_time(long chunk_size, long chunk_rate) {
        double rv;
        rv = 8.0 * chunk_size / chunk_rate;
        rv = rv * 1000.0;
        return (long)rv;
}

/* Initialize the static variables in the timer. */
int init_timer(int blockSize, int bitRate) {
	struct timespec now_tv;

	chunk_ms = chunk_time(blockSize, bitRate);
	ctr = 0;
	errctr = 0;
        clock_gettime(CLOCK_REALTIME, &base_tv);
        base_tv.tv_nsec = 0;
#ifdef DEBUGVERBOSE
	fprintf(stderr, "%d milliseconds per chunk with block size %d and rate %d.\n",
                        (int)chunk_ms, blockSize, bitRate);
#endif /* DEBUG */
        clock_gettime(CLOCK_REALTIME, &now_tv);
        start_ms = tv_to_msec(&now_tv);
	return(0);
}

/* Increments the quantum and returns the time in ms to the end. */
int end_quantum_increment() {
	++ctr;
	return end_quantum();
}

/* Return milliseconds to end of current quantum */
int end_quantum() {
	struct timespec now_tv;
	int sleeplen;
	long now, period_end;
	
	clock_gettime(CLOCK_REALTIME, &now_tv);
        now = tv_to_msec(&now_tv);
	period_end = start_ms + chunk_ms * ctr;
	sleeplen = period_end - now;

#ifdef DEBUGVERBOSE
	fprintf(stderr, "End: %ld Now: %ld\n", period_end, now);
        if(sleeplen < 0) {
                printf("Done: time exceeded!  End: %ld Now: %ld\n",period_end, now);
        } else {
                printf("Done; starting sleep for %d msec\n", sleeplen);
        }
	printf("sleeplen: %d\n",sleeplen);
#endif

	return sleeplen;
}

/* Sleep to end of current quantum.  Returns 0 only if time to end of quantum is positive. */
int sleep_end_quantum() {
	int sleeplen = end_quantum();
        if(sleeplen < 0) {
        	++errctr;
        } else {
                usleep(1000*sleeplen);
        }
#ifdef DEBUGVERBOSE
        printf("Period %d, %d errors\n",ctr,errctr);
#endif
	return (sleeplen <= 0);
}

/* Increment to next quantum and sleep to end.  Returns 0 only if time to end of quantum is positive. */
int sleep_end_quantum_increment() {
	++ctr;
	return sleep_end_quantum();
}
	
