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