/*
 * Copyright (c) 2011 Petr Koupy
 * Copyright (c) 2011 Jiri Zarevucky
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @addtogroup libposix
 * @{
 */
/** @file Standard library definitions.
 */

#define LIBPOSIX_INTERNAL

#include "internal/common.h"
#include "stdlib.h"

#include "errno.h"
#include "fcntl.h"
#include "limits.h"
#include "string.h"
#include "sys/stat.h"
#include "unistd.h"

#include "libc/sort.h"
#include "libc/str.h"
#include "libc/vfs/vfs.h"
#include "libc/stats.h"

/**
 * 
 * @param array
 * @param count
 * @param size
 * @param compare
 */
int posix_atexit(void (*func)(void))
{
	// TODO: low priority, just a compile-time dependency of binutils
	not_implemented();
}

/**
 * Integer absolute value.
 * 
 * @param i Input value.
 * @return Absolute value of the parameter.
 */
int posix_abs(int i)
{
	return i < 0 ? -i : i;
}

/**
 * Long integer absolute value.
 * 
 * @param i Input value.
 * @return Absolute value of the parameter.
 */
long posix_labs(long i)
{
	return i < 0 ? -i : i;
}

/**
 * Long long integer absolute value.
 * 
 * @param i Input value.
 * @return Absolute value of the parameter.
 */
long long posix_llabs(long long i)
{
	return i < 0 ? -i : i;
}

/**
 * Compute the quotient and remainder of an integer division.
 *
 * @param numer Numerator.
 * @param denom Denominator.
 * @return Quotient and remainder packed into structure.
 */
posix_div_t posix_div(int numer, int denom)
{
	return (posix_div_t) { .quot = numer / denom, .rem = numer % denom };
}

/**
 * Compute the quotient and remainder of a long integer division.
 *
 * @param numer Numerator.
 * @param denom Denominator.
 * @return Quotient and remainder packed into structure.
 */
posix_ldiv_t posix_ldiv(long numer, long denom)
{
	return (posix_ldiv_t) { .quot = numer / denom, .rem = numer % denom };
}

/**
 * Compute the quotient and remainder of a long long integer division.
 *
 * @param numer Numerator.
 * @param denom Denominator.
 * @return Quotient and remainder packed into structure.
 */
posix_lldiv_t posix_lldiv(long long numer, long long denom)
{
	return (posix_lldiv_t) { .quot = numer / denom, .rem = numer % denom };
}

/**
 * Private helper function that serves as a compare function for qsort().
 *
 * @param elem1 First element to compare.
 * @param elem2 Second element to compare.
 * @param compare Comparison function without userdata parameter.
 * @return Relative ordering of the elements.
 */
static int sort_compare_wrapper(void *elem1, void *elem2, void *userdata)
{
	int (*compare)(const void *, const void *) = userdata;
	int ret = compare(elem1, elem2);
	
	/* Native qsort internals expect this. */
	if (ret < 0) {
		return -1;
	} else if (ret > 0) {
		return 1;
	} else {
		return 0;
	}
}

/**
 * Array sorting utilizing the quicksort algorithm.
 *
 * @param array Array of elements to sort.
 * @param count Number of elements in the array.
 * @param size Width of each element.
 * @param compare Decides relative ordering of two elements.
 */
void posix_qsort(void *array, size_t count, size_t size,
    int (*compare)(const void *, const void *))
{
	/* Implemented in libc with one extra argument. */
	qsort(array, count, size, sort_compare_wrapper, compare);
}

/**
 * Binary search in a sorted array.
 *
 * @param key Object to search for.
 * @param base Pointer to the first element of the array.
 * @param nmemb Number of elements in the array.
 * @param size Size of each array element.
 * @param compar Comparison function.
 * @return Pointer to a matching element, or NULL if none can be found.
 */
void *posix_bsearch(const void *key, const void *base,
    size_t nmemb, size_t size, int (*compar)(const void *, const void *))
{
	while (nmemb > 0) {
		const void *middle = base + (nmemb / 2) * size;
		int cmp = compar(key, middle);
		if (cmp == 0) {
			return (void *) middle;
		}
		if (middle == base) {
			/* There is just one member left to check and it
			 * didn't match the key. Avoid infinite loop.
			 */
			break;
		}
		if (cmp < 0) {
			nmemb = nmemb / 2;
		} else if (cmp > 0) {
			nmemb = nmemb - (nmemb / 2);
			base = middle;
		}
	}
	
	return NULL;
}

/**
 * Retrieve a value of the given environment variable.
 *
 * Since HelenOS doesn't support env variables at the moment,
 * this function always returns NULL.
 *
 * @param name Name of the variable.
 * @return Value of the variable or NULL if such variable does not exist.
 */
char *posix_getenv(const char *name)
{
	return NULL;
}

/**
 * 
 * @param name
 * @param resolved
 * @return
 */
int posix_putenv(char *string)
{
	// TODO: low priority, just a compile-time dependency of binutils
	not_implemented();
}

/**
 * Issue a command.
 *
 * @param string String to be passed to a command interpreter or NULL.
 * @return Termination status of the command if the command is not NULL,
 *     otherwise indicate whether there is a command interpreter (non-zero)
 *     or not (zero).
 */
int posix_system(const char *string) {
	// TODO: does nothing at the moment
	return 0;
}

/**
 * Resolve absolute pathname.
 * 
 * @param name Pathname to be resolved.
 * @param resolved Either buffer for the resolved absolute pathname or NULL.
 * @return On success, either resolved (if it was not NULL) or pointer to the
 *     newly allocated buffer containing the absolute pathname (if resolved was
 *     NULL). Otherwise NULL.
 *
 */
char *posix_realpath(const char *restrict name, char *restrict resolved)
{
	#ifndef PATH_MAX
		assert(resolved == NULL);
	#endif
	
	if (name == NULL) {
		errno = EINVAL;
		return NULL;
	}
	
	// TODO: symlink resolution
	
	/* Function absolutize is implemented in libc and declared in vfs.h.
	 * No more processing is required as HelenOS doesn't have symlinks
	 * so far (as far as I can tell), although this function will need
	 * to be updated when that support is implemented.
	 */
	char* absolute = absolutize(name, NULL);
	
	if (absolute == NULL) {
		/* POSIX requires some specific errnos to be set
		 * for some cases, but there is no way to find out from
		 * absolutize().
		 */
		errno = EINVAL;
		return NULL;
	}
	
	if (resolved == NULL) {
		return absolute;
	} else {
		#ifdef PATH_MAX
			str_cpy(resolved, PATH_MAX, absolute);
		#endif
		free(absolute);
		return resolved;
	}
}

/**
 * Converts a string representation of a floating-point number to
 * its native representation. See posix_strtold().
 *
 * @param nptr String representation of a floating-point number.
 * @return Double-precision number resulting from the string conversion.
 */
double posix_atof(const char *nptr)
{
	return posix_strtod(nptr, NULL);
}

/**
 * Converts a string representation of a floating-point number to
 * its native representation. See posix_strtold().
 *
 * @param nptr String representation of a floating-point number.
 * @param endptr Pointer to the final part of the string which
 *     was not used for conversion.
 * @return Single-precision number resulting from the string conversion.
 */
float posix_strtof(const char *restrict nptr, char **restrict endptr)
{
	return (float) posix_strtold(nptr, endptr);
}

/**
 * Converts a string representation of a floating-point number to
 * its native representation. See posix_strtold().
 *
 * @param nptr String representation of a floating-point number.
 * @param endptr Pointer to the final part of the string which
 *     was not used for conversion.
 * @return Double-precision number resulting from the string conversion.
 */
double posix_strtod(const char *restrict nptr, char **restrict endptr)
{
	return (double) posix_strtold(nptr, endptr);
}

/**
 * Allocate memory chunk.
 *
 * @param size Size of the chunk to allocate.
 * @return Either pointer to the allocated chunk or NULL if not possible.
 */
void *posix_malloc(size_t size)
{
	return malloc(size);
}

/**
 * Allocate memory for an array of elements.
 *
 * @param nelem Number of elements in the array.
 * @param elsize Size of each element.
 * @return Either pointer to the allocated array or NULL if not possible.
 */
void *posix_calloc(size_t nelem, size_t elsize)
{
	return calloc(nelem, elsize);
}

/**
 * Reallocate memory chunk to a new size.
 *
 * @param ptr Memory chunk to reallocate. Might be NULL.
 * @param size Size of the reallocated chunk. Might be zero.
 * @return Either NULL or the pointer to the newly reallocated chunk.
 */
void *posix_realloc(void *ptr, size_t size)
{
	if (ptr != NULL && size == 0) {
		/* Native realloc does not handle this special case. */
		free(ptr);
		return NULL;
	} else {
		return realloc(ptr, size);
	}
}

/**
 * Free allocated memory chunk.
 *
 * @param ptr Memory chunk to be freed.
 */
void posix_free(void *ptr)
{
	if (ptr) {
		free(ptr);
	}
}

/**
 * Creates and opens an unique temporary file from template.
 *
 * @param tmpl Template. Last six characters must be XXXXXX.
 * @return The opened file descriptor or -1 on error.
 */
int posix_mkstemp(char *tmpl)
{
	int fd = -1;
	
	char *tptr = tmpl + posix_strlen(tmpl) - 6;
	
	while (fd < 0) {
		if (*posix_mktemp(tmpl) == '\0') {
			/* Errno set by mktemp(). */
			return -1;
		}
		
		fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
		
		if (fd == -1) {
			/* Restore template to it's original state. */
			snprintf(tptr, 7, "XXXXXX");
		}
	}
	
	return fd;
}

/**
 * Creates an unique temporary file name from template.
 *
 * @param tmpl Template. Last six characters must be XXXXXX.
 * @return The value of tmpl. The template is modified in place.
 *    If no temporary file name can be created, template is
 *    reduced to an empty string.
 */
char *posix_mktemp(char *tmpl)
{
	int tmpl_len = posix_strlen(tmpl);
	if (tmpl_len < 6) {
		errno = EINVAL;
		*tmpl = '\0';
		return tmpl;
	}
	
	char *tptr = tmpl + tmpl_len - 6;
	if (posix_strcmp(tptr, "XXXXXX") != 0) {
		errno = EINVAL;
		*tmpl = '\0';
		return tmpl;
	}
	
	static int seq = 0;
	
	for (; seq < 1000000; ++seq) {
		snprintf(tptr, 7, "%06d", seq);
		
		int orig_errno = errno;
		errno = 0;
		/* Check if the file exists. */
		if (posix_access(tmpl, F_OK) == -1) {
			if (errno == ENOENT) {
				errno = orig_errno;
				break;
			} else {
				/* errno set by access() */
				*tmpl = '\0';
				return tmpl;
			}
		}
	}
	
	if (seq == 10000000) {
		errno = EEXIST;
		*tmpl = '\0';
		return tmpl;
	}
	
	return tmpl;
}

/**
 * Get system load average statistics.
 *
 * @param loadavg Array where the load averages shall be placed.
 * @param nelem Maximum number of elements to be placed into the array.
 * @return Number of elements placed into the array on success, -1 otherwise.
 */
int bsd_getloadavg(double loadavg[], int nelem)
{
	assert(nelem > 0);
	
	size_t count;
	load_t *loads = stats_get_load(&count);
	
	if (loads == NULL) {
		return -1;
	}
	
	if (((size_t) nelem) < count) {
		count = nelem;
	}
	
	for (size_t i = 0; i < count; ++i) {
		loadavg[i] = (double) loads[i];
	}
	
	free(loads);
	return count;
}

/** @}
 */
