/*******************************************/
/* INCLUDES */

#include "util.h"


/*******************************************/
/* GLOBALS */

my_str MSG;


/*******************************************/
/* CLASSES */

my_str::my_str()
	{
	// This function creates a empty string.
	buf_size = 1;
	
	str_ptr = (char *) my_malloc(buf_size);
	str_ptr[0] = 0;
	}

my_str::my_str(char *init_str)
	{
	// This function creates a string initialized to init_str.
	buf_size = strlen(init_str) + 1;
	
	str_ptr = (char *) my_malloc(buf_size);
	strcpy(str_ptr, init_str);
	}

my_str::~my_str()
	{
	clean_up();
	}

my_str::my_str(my_str &other)
	{
	str_ptr = NULL;
	
	*this = other;
	}
	
my_str & my_str::operator=(my_str &other)
	{
	// This function copy the string.
	if(&other == this)
		{
		return *this;
		}
		
	clean_up();

	buf_size = strlen(other.str_ptr) + 1;
	str_ptr = (char *) my_malloc(buf_size);
	strcpy(str_ptr, other.str_ptr);

	return *this;
	}

void my_str::clean_up()
	{
	free((void *) str_ptr);
	}

void my_str::grow(int size)
	{
	// This functions realloc str_ptr so that it can contains at least size
	// characters (including the NULL).
	assert(size >= 0);
	
	if(buf_size < size)
		{
		buf_size = size;
		str_ptr = (char *) my_realloc((void *) str_ptr, buf_size);
		}
	}
	

char * my_str::str()
	{
	// This function returns a 'char *' to the string. You can use the
	// pointer to read the string and to modify it, but don't realloc it.
	// (Don't write past the terminating NULL).
	return str_ptr;
	}
		
char * my_str::re(char *replace_str)
	{
	// This function replaces this string by the string passed in the
	// arguments. It returns a pointer to the newly copied string.
	grow(strlen(replace_str) + 1);
	strcpy(str_ptr, replace_str);

	return str_ptr;
	}

char * my_str::af(char *after_str)
	{
	// This function concatenates after_str at the end of the current
	// string. It returns the resulting string.
	int new_size = strlen(after_str) + strlen(str_ptr) + 1;
	grow(new_size);
	strcat(str_ptr, after_str);

	return str_ptr;
	}

char * my_str::bf(char *before_str)
	{
	// This function concatenates the current string at the end of
	// before_str. It returns the resulting string.
	int bf_size = strlen(before_str);
	int cr_size = strlen(str_ptr);
	int new_size = cr_size + bf_size + 1;

	grow(new_size);
	
	// Move the string, with the NULL.
	memmove(str_ptr + bf_size, str_ptr, cr_size + 1);
	
	// Copy before_str.
	memcpy(str_ptr, before_str, bf_size);
	
	return str_ptr;
	}

char * my_str::sf(int where, char *format, ...)
	{
	// This function allow you to sprintf directly inside the string. 
	// The where argument specify the location to put the string
	// resulting from the printf: STRA will put the string After the
	// current string, STRB will put it before, and STRR will Replace
	// it entirely. Format is the usual printf format, and the following
	// args are the args that printf will take. The function will return
	// the resulting string.
	va_list ap;
	int print_size = -2;
	char *tmp_str;

	// Determine the size of the resulting string.
	va_start(ap, format);
	print_size = vsnprintf(NULL, 0, format, ap);
	va_end(ap);
	
	// Glibc 2.0. Oops.
	if(print_size == -1)
		{
		printf("This program requires Glibc 2.1 or later.\n");
		exit(1);
		}
	
	assert(print_size > 0);
	
	// Alloc the print string for printf().
	tmp_str = (char *) my_malloc(print_size + 1);
	
	// Do the printf().
	va_start(ap, format);
	vsnprintf(tmp_str, print_size + 1, format, ap);
	va_end(ap);
	
	// Do the adjustments.
	if(where == STRB)
		{
		this->bf(tmp_str);
		free((void *) tmp_str);
		}

	else if(where == STRA)
		{
		this->af(tmp_str);
		free((void *) tmp_str);
		}

	else
		{
		free((void *) str_ptr);
		str_ptr = tmp_str;
		buf_size = print_size + 1;
		}
	
	return str_ptr;
	}
	

/*******************************************/
/* FUNCTIONS */

void display_error(char *str)
	{
	fprintf(stdout, "%s\n", str);
	exit(1);
	}


/*******************************************/
void display_warning(char *str)
	{
	fprintf(stdout, "%s\n", str);
	}


/*******************************************/
void *my_malloc(unsigned int count)
	{
	void *ptr;
	
	ptr = malloc(count);
	
	if(ptr == NULL)
		{
		display_error("Out of memory.");
		}
	
	return ptr;
	}


/*******************************************/
void *my_calloc(unsigned int count)
	{
	void *ptr;
	
	ptr = calloc(1, count);
	
	if(ptr == NULL)
		{
		display_error("Out of memory.");
		}
	
	return ptr;
	}
	

/*******************************************/
void *my_realloc(void *ptr, unsigned int count)
	{
	ptr = realloc(ptr, count);
	
	if(ptr == NULL)
		{
		display_error("Out of memory.");
		}
	
	return ptr;
	}


/*********************************************/
void timeval_subtract(struct timeval *result, struct timeval *x, 
			struct timeval *y)
	{
	// Subtract y from x and put the result in result.
	// Arguments:
	// Result.
	// Timeval to subtract from.
	// Timeval containing the time to subtract.
	int real_sec = x->tv_sec - y->tv_sec;
	int real_usec = x->tv_usec - y->tv_usec;
	
	// Perform the carry for the later subtraction.
	if(real_usec < 0)
		{
		real_sec -= 1;
		real_usec += 1000000;
		}
		
	result->tv_sec = real_sec;
	result->tv_usec = real_usec;
	}


/*********************************************/
void timeval_add(struct timeval *result, struct timeval *x, 
		 struct timeval *y)
	{
	// Add x to y and put the result in result.
	// Arguments:
	// Result.
	// Timeval X.
	// Timeval Y.
	int real_sec = x->tv_sec + y->tv_sec;
	int real_usec =  x->tv_usec + y->tv_usec;
	
	// Perform the carry for the later addition.
	if(real_usec >= 1000000)
		{
		real_sec += 1;
		real_usec -= 1000000;
		}
		
	result->tv_sec = real_sec;
	result->tv_usec = real_usec;
	}
	
	
/*********************************************/
void print_time(struct timeval *tv)
	{
	// Print the time of the timeval passed in the parameters.
	// Arguments:
	// Timeval.
	printf("Time is %d sec, %d usec.\n", (int) tv->tv_sec, 
						(int) tv->tv_usec);
	}
		

/*********************************************/
int timeval_cmp(struct timeval *first, struct timeval *second)
	{
	// This function returns -1 is first comes before second, 0 if the 
	// times are the same, 1 if first comes after second.
	// Arguments:
	// Timeval 1.
	// Timeval 2.
	
	// First is before.
	if(first->tv_sec < second->tv_sec)
		{
		return -1;
		}
	
	// First is after.
	else if(first->tv_sec > second->tv_sec)
		{
		return 1;
		}
	
	// Same seconds, first is before.
	else if(first->tv_usec < second->tv_usec)
		{
		return -1;
		}
	
	// Same seconds, first is after.
	else if(first->tv_usec > second->tv_usec)
		{
		return 1;
		}
	
	// Same time.
	else
		{
		return 0;
		}
	}


/*********************************************/
void get_current_time(timeval *tv_ptr)
	{
	// The function puts the current time in the timeval passed in the 
	// parameters. The function aborts the program if it fails.
	// Arguments:
	// Timeval.
	int error = gettimeofday(tv_ptr, NULL);
	
	if(error)
		{
		perror("Cannot get current time");
		exit(1);
		}
	}
	

/*********************************************/
void get_elapsed_time(timeval *start_ptr, timeval *result_ptr)
	{
	// The function calculates the elapsed time since start was initialized
	// and put the result in result. The function aborts the 
	// program if it fails.
	// Arguments:
	// Start timeval.
	// Result timeval.
	timeval current_tv;
	
	// Get curren time.	
	get_current_time(&current_tv);
	
	// Do the subtraction.
	timeval_subtract(result_ptr, &current_tv, start_ptr);
	}
