#include <stdint.h>
#include <limits.h>
#ifdef WINNT
#include <windows.h>
#else
#include <time.h>
#include <unistd.h>
#endif

#include "error.h"
#include "memory.h"

#define timer_IMPORT
#include "timer.h"


struct timer_Type {
	int isRunning;
	int64_t start;
	int64_t total;
};


/** Ticks counter frequency (Hz). */
static int64_t frequency;


timer_Type * timer_new()
{
	if( frequency == 0 ){
#ifdef WINNT
		LARGE_INTEGER f;
		if( ! QueryPerformanceFrequency(&f) )
			error_internal("QueryPerformanceFrequency() failed", 0);
		frequency = f.QuadPart;
#else
		frequency = 1000000000;
#endif
	}
	
	timer_Type *t = memory_allocate(sizeof(timer_Type), NULL);
	t->isRunning = 0;
	t->total = 0;
	return t;
}


static int64_t timer_getTicks()
{
#ifdef WINNT
	LARGE_INTEGER ticks;
	if( ! QueryPerformanceCounter(&ticks) )
		error_internal("QueryPerformanceCounter() failed", 0);
	return ticks.QuadPart;
#else
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC, &t);
	return 1000000000LL * t.tv_sec + t.tv_nsec;
#endif
}


void timer_start(timer_Type *t)
{
	if( t->isRunning )
		return;
	t->isRunning = 1;
	t->start = timer_getTicks();
}


void timer_stop(timer_Type *t)
{
	if( ! t->isRunning )
		return;
	t->isRunning = 0;
	t->total += timer_getTicks() - t->start;
}


int64_t timer_getElapsedNanoseconds(timer_Type *t)
{
	int64_t elapsed_ticks = t->total;
	if( t->isRunning )
		elapsed_ticks += timer_getTicks() - t->start;
	return elapsed_ticks * 1000000000 / frequency;
}


int64_t timer_getElapsedMicroseconds(timer_Type *t)
{
	return (timer_getElapsedNanoseconds(t) + 500) / 1000;
}


int timer_getElapsedMilliseconds(timer_Type *t)
{
	int64_t elapsed_us = (timer_getElapsedNanoseconds(t) + 500000) / 1000000;
	if( elapsed_us <= INT_MAX )
		return elapsed_us;
	else
		return INT_MAX;
}


void timer_reset(timer_Type *t)
{
	t->isRunning = 0;
	t->total = 0;
}


void timer_sleepMilliseconds(int ms)
{
	if( ms <= 0 )
		return;
#ifdef WINNT
	Sleep(ms);
#else
	usleep(1000LL * ms);
#endif
}
