#include "tools/string.h"
#ifdef TOOLBOX_REGEX
#	include "tools/regex.h"
#endif

#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <errno.h>

const cTBString cTBString::Null;

cTBString::cTBString(): 
		cTBShared(),
		m_StringLen(0) {
}

cTBString::cTBString(const cTBString &src): 
		cTBShared(src),
		m_StringLen(src.m_StringLen) {
}

cTBString::cTBString(const char *src) {
	Set(src);
}

cTBString::cTBString(const uchar *src) {
	Set(src);
}

cTBString::cTBString(char src) {
	Set(src);
}

cTBString::~cTBString () {
}

void cTBString::Release(uint newsize) {
	m_StringLen = newsize;
	cTBShared::Release(m_StringLen + 1);
	Buffer()[m_StringLen] = 0;
}

void cTBString::Clear() {
	cTBShared::Clear();
	m_StringLen = 0;
}

void cTBString::Set(const cTBString &String) {
	cTBShared::Set(String);
	m_StringLen = String.m_StringLen;
}

void cTBString::Set (const char *String) {
	m_StringLen = strlen(String);
	Allocate(m_StringLen + 1);

	memcpy(Buffer(), String, m_StringLen);
	Buffer()[m_StringLen] = 0;
}

void cTBString::Set (const uchar *String) {
	Set((const char*)String);
}

void cTBString::Set (char Character) {
	m_StringLen = 1;
	Allocate(m_StringLen + 1);

	Buffer()[0] = Character;
	Buffer()[1] = 0;
}

void cTBString::Fill(char Character, int Length) {
	if (Length != -1) {
		m_StringLen = Length;
		Allocate(m_StringLen + 1);
	}
	memset(Buffer(), Character, m_StringLen);
	Buffer()[m_StringLen] = 0;
}

void cTBString::Append(const cTBString &src) {
	Allocate(m_StringLen + src.m_StringLen + 1, true);

	memcpy(Buffer() + m_StringLen, src.Buffer(), src.m_StringLen);
	m_StringLen += src.m_StringLen;
	Buffer()[m_StringLen] = 0;
}

void cTBString::Append(const char *src) {
	uint len = strlen(src);
	Allocate(m_StringLen + len + 1, true);

	memcpy(Buffer() + m_StringLen, src, len);
	m_StringLen += len;
	Buffer()[m_StringLen] = 0;
}

void cTBString::Append(char src) {
	Allocate(m_StringLen + 2, true);

	Buffer()[m_StringLen] = src;
	++m_StringLen;
	Buffer()[m_StringLen] = 0;
}

void cTBString::Prepend(const cTBString &src) {
	Allocate(m_StringLen + src.m_StringLen + 1, true);

	memmove(Buffer() + src.m_StringLen, Buffer(), m_StringLen);
	memcpy(Buffer(), src.Buffer(), src.m_StringLen);
	m_StringLen += src.m_StringLen;
	Buffer()[m_StringLen] = 0;
}

void cTBString::Prepend(const char *src) {
	uint len = strlen(src);
	Allocate(m_StringLen + len + 1, true);

	memmove(Buffer() + len, Buffer(), m_StringLen);
	memcpy(Buffer(), src, len);
	m_StringLen += len;
	Buffer()[m_StringLen] = 0;
}

void cTBString::Prepend(char src) {
	Allocate(m_StringLen + 2, true);

	memmove(Buffer() + 1, Buffer(), m_StringLen);
	Buffer()[0] = src;
	Buffer()[++m_StringLen] = 0;
}

void cTBString::Insert(uint Index, const cTBString &String) {
	Allocate(m_StringLen + String.m_StringLen + 1, true);

	memmove(Buffer() + Index + String.m_StringLen, Buffer() + Index, m_StringLen - Index);
	memcpy(Buffer() + Index, String.Buffer(), String.m_StringLen);
	m_StringLen += String.m_StringLen;
	Buffer()[m_StringLen] = 0;
}

void cTBString::Insert(uint Index, const char *String) {
	uint len = strlen(String);
	Allocate(m_StringLen + len + 1, true);

	memmove(Buffer() + Index + len, Buffer() + Index, m_StringLen - Index);
	memcpy(Buffer() + Index, String, len);
	m_StringLen += len;
	Buffer()[m_StringLen] = 0;
}

void cTBString::Insert(uint Index, char Character) {
	Allocate(m_StringLen + 2, true);

	memmove(Buffer() + Index + 1, Buffer() + Index, m_StringLen - Index);
	Buffer()[Index] = Character;
	Buffer()[++m_StringLen] = 0;
}

RETURNS(cTBString, cTBString::Left(uint count) const, ret)
	if (count > m_StringLen)
		count = m_StringLen;

	ret.Allocate(count + 1);
	memcpy(ret.Buffer(), Buffer(), count);
	ret.Buffer()[count] = 0;
	ret.m_StringLen = count;
RETURN(ret)

RETURNS(cTBString, cTBString::Right(uint count) const, ret)
	if (count > m_StringLen)
		count = m_StringLen;

	ret.Allocate(count + 1);
	memcpy(ret.Buffer(), Buffer() + m_StringLen - count, count);
	ret.Buffer()[count] = 0;
	ret.m_StringLen = count;
RETURN(ret)

RETURNS(cTBString, cTBString::Mid(int idx, int count) const, ret)
	if (idx < 0)
		idx = m_StringLen + idx;

	if ((count < 0) || (count > (int)m_StringLen - idx))
		count = m_StringLen - idx;

	ret.Allocate(count + 1);
	memcpy(ret.Buffer(), Buffer() + idx, count);
	ret.Buffer()[count] = 0;
	ret.m_StringLen = count;
RETURN(ret)

int cTBString::Find (const cTBString &String, uint Offset) const {
	if (Offset >= m_StringLen)
		return -1;

	char *pos = strstr(Buffer() + Offset, String.Buffer());
	if (pos) return (pos - Buffer());
	else     return -1;
}

int cTBString::Find (const char *String, uint Offset) const {
	if (Offset >= m_StringLen)
		return -1;

	char *pos = strstr(Buffer() + Offset, String);
	if (pos) return (pos - Buffer());
	else     return -1;
}

int cTBString::Find (char Character, uint Offset) const {
	if (Offset >= m_StringLen)
		return -1;

	char *pos = strchr(Buffer() + Offset, Character);
	if (pos) return (pos - Buffer());
	else     return -1;
}

#ifdef TOOLBOX_REGEX
bool cTBString::Find (cTBRegEx &Regex, uint Offset) const {
	return Regex.Match(Buffer(), Offset);
}
#endif

cTBString &cTBString::Format (const char *fmt, ...) {
	int n, size = 128;
	va_list ap;

	char *buf = Buffer(size);

	while (1) {
		va_start(ap, fmt);
		n = vsnprintf(buf, size, fmt, ap);
		va_end(ap);

		if ((n > -1) && (n < size))
			break;

		if (n > -1)
			size = n + 1;
		else
			size *= 2;

		buf = Buffer(size);
	}
	Release(n);
	return *this;
}

cTBString &cTBString::Format(const cTBString &fmt, ...) {
	int n, size = 128;
	va_list ap;

	char *buf = Buffer(size);

	while (1) {
		va_start(ap, &fmt);
		n = vsnprintf(buf, size, fmt, ap);
		va_end(ap);

		if ((n > -1) && (n < size))
			break;

		if (n > -1)
			size = n + 1;
		else
			size *= 2;

		buf = Buffer(size);
	}
	Release(n);
	return *this;
}

template<cTBString::TOFUNC F>
cTBString cTBString::ToAnything(void) const {
	const char *src;
	char *dest;
	cTBString ret;

	src = Buffer();
	dest = ret.Buffer(m_StringLen + 1);

	for (; src < Buffer() + m_StringLen; ++src, ++dest)
		*dest = F(*src);

	*dest = '\0';

	ret.Release(m_StringLen);
	return ret;
}

template<cTBString::ISFUNC F>
bool cTBString::IsAnything(void) const {
	const char *ptr = Buffer();

	for (; ptr < Buffer() + m_StringLen; ++ptr)
		if (!F(*ptr)) return false;

	return true;
}

short cTBString::ToShort(bool *Ok) const {
	long ret;
	char *endptr;
	bool res = false;

	ret = strtol(Buffer(), &endptr, 0);

	if (!IsEmpty() && *endptr == '\0' && ret >= SHRT_MIN && ret <= SHRT_MAX)
		res = true;

	if (Ok) *Ok = res;
	return (short)ret;
}

ushort cTBString::ToUShort(bool *Ok) const {
	ulong ret;
	char *endptr;
	bool res = false;

	ret = strtoul(Buffer(), &endptr, 0);

	if (!IsEmpty() && *endptr == '\0' && ret <= USHRT_MAX)
		res = true;

	if (Ok) *Ok = res;
	return (ushort)ret;
}

int cTBString::ToInt(bool *Ok) const {
	long ret;
	char *endptr;
	bool res = false;

	ret = strtol(Buffer(), &endptr, 0);

	if (!IsEmpty() && *endptr == '\0' && ret >= INT_MIN && ret <= INT_MAX)
		res = true;

	if (Ok) *Ok = res;
	return (int)ret;
}

uint cTBString::ToUInt(bool *Ok) const {
	ulong ret;
	char *endptr;
	bool res = false;

	ret = strtoul(Buffer(), &endptr, 0);

	if (!IsEmpty() && *endptr == '\0' && ret <= UINT_MAX)
		res = true;

	if (Ok) *Ok = res;
	return (uint)ret;
}

long cTBString::ToLong(bool *Ok) const {
	long ret;
	char *endptr;
	bool res = false;

	errno = 0;
	ret = strtol(Buffer(), &endptr, 0);

	if (!IsEmpty() && *endptr == '\0' && errno != ERANGE)
		res = true;

	if (Ok) *Ok = res;
	return (long)ret;
}

ulong cTBString::ToULong(bool *Ok) const {
	ulong ret;
	char *endptr;
	bool res = false;

	errno = 0;
	ret = strtoul(Buffer(), &endptr, 0);

	if (!IsEmpty() && *endptr == '\0' && errno != ERANGE)
		res = true;

	if (Ok) *Ok = res;
	return (ulong)ret;
}

float cTBString::ToFloat(bool *Ok) const {
	double ret;
	char *endptr;
	bool res = false;

	ret = strtod(Buffer(), &endptr);

	if (!IsEmpty() && *endptr == '\0' && errno != ERANGE)
		res = true;

	if (Ok) *Ok = res;
	return (float)ret;
}

double cTBString::ToDouble(bool *Ok) const {
	double ret;
	char *endptr;
	bool res = false;

	errno = 0;
	ret = strtol(Buffer(), &endptr, 0);
	
	if (!IsEmpty() && *endptr == '\0' && errno != ERANGE)
		res = true;

	if (Ok) *Ok = res;
	return (double)ret;
}

RETURNS(cTBString, cTBString::Number(short Num), ret)
	ret.Format("%hd", Num);
RETURN(ret)

RETURNS(cTBString, cTBString::Number(ushort Num), ret)
	ret.Format("%hu", Num);
RETURN(ret)

RETURNS(cTBString, cTBString::Number(int Num), ret)
	ret.Format("%d", Num);
RETURN(ret)

RETURNS(cTBString, cTBString::Number(uint Num), ret)
	ret.Format("%u", Num);
RETURN(ret)

RETURNS(cTBString, cTBString::Number(long Num), ret)
	ret.Format("%ld", Num);
RETURN(ret)

RETURNS(cTBString, cTBString::Number(ulong Num), ret)
	ret.Format("%lu", Num);
RETURN(ret)

RETURNS(cTBString, cTBString::Number(float Num), ret)
	ret.Format("%f", Num);
RETURN(ret)

RETURNS(cTBString, cTBString::Number(double Num), ret)
	ret.Format("%f", Num);
RETURN(ret)

