#include "animone/util.h"

#include <string.h>

#include <regex> // FIXME use a C library for this

int animone_internal_util_ReadFile(const char *path, char **data, size_t *size)
{
	// whatever
	size_t sz_local;
	if (!size)
		size = &sz_local;

	FILE *f = fopen(path, "r");
	if (!f)
		return 0;

	fseek(f, 0, SEEK_END);
	long end = ftell(f);
	if (end < 0)
		return 0;
	fseek(f, 0, SEEK_SET);

	*size = end;

	if (data) {
		// add a NUL terminator anyway
		*data = (char *)malloc(end + 1);
		if (!*data) {
			*size = 0;
			return 0;
		}

		*size = fread(*data, 1, *size, f);
		(*data)[*size] = '\0';
	}

	return 1;
}

static inline unsigned char _animone_internal_tolower(unsigned char c)
{
	return (c >= 'A' && c <= 'Z') ? (c | 0x20) : c;
}

/* this is, in essence, strcasecmp. */
int animone_internal_util_EqualStrings(const char *str1, const char *str2)
{
	unsigned char c1, c2;

	do {
		c1 = _animone_internal_tolower(*str1++);
		c2 = _animone_internal_tolower(*str2++);
	} while (c1 == c2 && c1);

	return !!(c1 - c2);
}

char *animone_internal_Stem(const char *filename)
{
	const char *ptr = strrchr(filename, '.');
	if (!ptr)
		return NULL;

	size_t len = ptr - filename;

	char *stem = (char *)malloc(len + 1);
	if (!stem)
		return NULL;

	memcpy(stem, filename, len);
	stem[len] = '\0';

	return stem;
}

int animone_internal_util_CheckPattern(const char *pattern, const char *str)
{
	switch (*pattern) {
	case '\0': return 0;
	case '^':
		// FIXME: Convert to C
		if (std::regex_match(std::string(str), std::regex(pattern)))
			return 1;
	default:
		break;
	}

	return animone_internal_util_EqualStrings(pattern, str);
}

/* Modifies `str` inplace */
int animone_internal_util_TrimLeft(char *str, const char* chars)
{
	if (!str || !*str)
		return 0;

	const size_t found = strcspn(str, chars);
	const size_t len = strlen(str);

	if (found == len)
		return 1; // nothing to do

	memmove(str, str + found, len - found);
	str[len - found] = '\0';

	return 1;
}

// reverse version of strcspn
static inline size_t _animone_internal_strrcspn(const char *str1, const char *str2)
{
	const size_t str1len = strlen(str1);
	ptrdiff_t i; /* FIXME: this should be using size_t */

	for (i = str1len - 1; i >= 0; i--) {
		size_t found = strcspn(str1 + i, str2);
		if (found != str1len - i)
            return i;
	}

    return str1len;
}

int animone_internal_util_TrimRight(char *str, const char* chars)
{
	if (!str || !*str)
		return 0;

	// We can get away without really moving memory around here.
	// The old version simply used std::string::find_last_not_of,
	// which I'm fairly sure isn't the intention of this function
	// (for example find_last_not_of("gb    ") returns "gb   ").
	const size_t found = _animone_internal_strrcspn(str, chars);
	str[found] = '\0';

	return 1;
}
