#include <../source/TAL/objbase.h>
#include <ctype.h>

/*
 *	We declare guard string quite large because default text 
 *	'[NULL]' can later be replaced by:
 *	1. Explicit call: strcpy(strSafe(NULL),"New text");
 *	2. Implicit: due to caller bugs.
 */

#ifdef	CHECKED_BUILD
static char libv_string_guard[STRING_GUARD_SIZE];
#endif

char_p	V_GLOBAL
strSafe(char_p string_which_can_be_NULL)
{
#ifdef	CHECKED_BUILD
	if(string_which_can_be_NULL)
		return string_which_can_be_NULL;
	else
	{
		/* Avoid infinite recursion if TRASHED_BUFFER_SIZE==0 */
		strCopy(libv_string_guard,"[NULL]",STRING_GUARD_SIZE);
		return libv_string_guard;
	}
#else
	return string_which_can_be_NULL;
#endif	/* CHECKED_BUILD */
}

/*
 *	strCopy - analog to strncpy.
 *	Differences:
 *	1. Third parameter is AMOUNT OF MEMORY allocated for 'dst'.
 *	2. 'Dst' will be ALWAYS terminated by null-character ('\0').
 *	3. Strings 'dst' and 'src' can be overlapped.
 *	4. Ifdef CHECHED_BUILD and dst==NULL, default static buffer returned.
 *  Returns: 'dst' / default static buffer.
 */
char_p V_GLOBAL
strCopy(char_p dst,char_p src,int dest_storage_length)
{
	int len;
	if(!dst || !src || dest_storage_length<=0)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"strCopy('%s','%s',%d): incorrect parameters.\n",strSafe(dst),
			strSafe(src),dest_storage_length);
#endif	/* CHECKED_BUILD */
		if(dst && dest_storage_length>0)
			dst[0]=0;
		return strSafe(dst);
	}
	len=strlen(src);
	if(len>=dest_storage_length)
		len=dest_storage_length-1;
	memmove(dst,src,len);
	dst[len]=0;
	return dst;
}

/*
 *	strCat - analog to strncat.
 *	Differences:
 *	1. Strings 'dst' and 'src' can be overlapped.
 *	2. Ifdef CHECHED_BUILD and dst==NULL, default static buffer returned.
 *  Returns: 'dst' / default static buffer.
 */
char_p	V_GLOBAL
strCat(char_p dst,char_p src,int max_chars_to_be_added)
{
	if(!dst || !src || max_chars_to_be_added<=0)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"strCat('%s','%s',%d): incorrect parameters.\n",strSafe(dst),
			strSafe(src),max_chars_to_be_added);
#endif	/* CHECKED_BUILD */
		return strSafe(dst);
	}
	return strCopy(dst+strlen(dst),src,max_chars_to_be_added);
}

/*
 * Takes lenght of dest buffer as third parameter.
 */
char_p V_GLOBAL
strCat2(char *dst,char *adden,vsize_t dst_buf_len)
{
	vsize_t dst_len, adden_len;
	if(!dst || !adden || !dst_buf_len)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"strCat2('%s','%s',%u): incorrect parameter(s).\n",
			strSafe(dst),strSafe(adden),dst_buf_len);
#endif	/* CHECKED_BUILD */
		return strSafe(dst);
	}
	dst_len = (vsize_t)strlen(dst);
	adden_len = (vsize_t)strlen(adden);
	if(dst_len >= (dst_buf_len-1))
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"strCat2('%s','%s',%u): no more space in dst buffer.\n",
			dst,adden,dst_buf_len);
#endif	/* CHECKED_BUILD */
		return dst;
	}
	if(adden_len)
		strCopy(dst + dst_len,adden,dst_buf_len - dst_len);
	return dst;
}

/*
 *	Strips leading whitespace from string. Returns str.
 *	Note. Ifdef CHECHED_BUILD and str==NULL, default static buffer returned.
 */
char_p	V_GLOBAL
strStripLead(char_p str)
{
	char *ptr;
	if(!str)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"strStripLead('%s'): incorrect parameter.\n",strSafe(str));
#endif	/* CHECKED_BUILD */
		return strSafe(str);
	}
	ptr=str;
	while(*ptr && *ptr<' ')
		ptr++;
	if(ptr != str)
		memmove(str,ptr,strlen(ptr)+1);
	return str;
}

/*
 *	Strips trailing whitespace from string. Returns str.
 *	Note. Ifdef CHECHED_BUILD and str==NULL, default static buffer returned.
 */
char_p	V_GLOBAL
strStripTrail(char_p str)
{
	char *ptr;
	if(!str)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"strStripTrail('%s'): incorrect parameter.\n",strSafe(str));
#endif	/* CHECKED_BUILD */
		return strSafe(str);
	}
	ptr=str+strlen(str)-1;
	for(;ptr>str && *ptr<' ';ptr--)
		*ptr=0;
	return str;
}

/*
 *	Strips both leading and trailing whitespace. Returns str.
 *	Note. Ifdef CHECHED_BUILD and str==NULL, default static buffer returned.
 */
char_p	V_GLOBAL
strStripLT(char_p str)
{
	if(!str)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"strStripLT('%s'): incorrect parameter.\n",strSafe(str));
#endif	/* CHECKED_BUILD */
		return strSafe(str);
	}
	strStripLead(str);
	strStripTrail(str);
	return str;
}

/*
 *	Optionally converts dos path to unix path and appends '/' at end
 *	if no one; result+\0 either feet in _MAX_PATH, or NULL returned.
 */
char_p	V_GLOBAL
strAppendSlash(char_p path_to_check)
{
	char_p ptr;
	int len;
	if(!path_to_check)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"strAppendSlash('[null]'): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	len=-1;
	for(ptr=path_to_check;*ptr;ptr++)
		if(*ptr=='\\')
		{
			len=strlen(path_to_check);
			break;
		}
	if(len==-1)
		len=ptr-path_to_check;
	if(len>=_MAX_PATH)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"strAppendSlash('%s'): path too long.\n",path_to_check);
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	if(!len || path_to_check[len-1]!='/')
	{
		if(len>(_MAX_PATH-1))
		{
#ifdef		CHECKED_BUILD
			conlPrintf(LOG_ERROR,
				"strAppendSlash('%s'): path too long to append '/'.\n",
				path_to_check);
#endif		/* CHECKED_BUILD */
		}
		path_to_check[len]='/';
		path_to_check[len+1]=0;
	}
	return path_to_check;
}

/*
 * Returns integer value of given hexadecimal number.
 * Mimics strtoX() C RTL calls.
 * FIXME: can we use strtol(..., ..., 16) instead ?
 */
u32_t	V_GLOBAL
strtoh(char *str,char **errchar)
{
	char buf[64],*ptr=buf;
	u32_t i,sum=0,e=1,mult;
	if(!str)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"strtoh(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		if(errchar)
			*errchar="(null)";
		return 0;
	}
	if(str[0]=='0' && (str[1]=='x' || str[1]=='X'))
		str+=2;	/* skip prefix */
	for(i=0;i<63 && *str;ptr++,str++,i++)
	{
		if(!isxdigit(*str))
			break;
		if(*str>='a' && *str<='f')
			*ptr=(char)(*str-'a'+'A');
		else
			*ptr=*str;
	}
	if(errchar)
		*errchar=str;
	ptr--;
	while(ptr>=buf)
	{
		mult=*ptr < '9' ? *ptr-'0' : 10 + *ptr-'A';
		sum+=mult * e;
		e<<=4;
		ptr--;
	}
	return sum;
}

/*
 *	Duplicates given string, returns memAlloc()'ed block.
 */
char_p	V_GLOBAL
strDup(char *src)
{
	u32_t len, mem_size;
	char *ret;
	if(!src)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"strDup(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	if(!*src)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,
			"strDup(""): nothing to duplicate.\n");
#endif	/* CHECKED_BUILD */
	}
	len=strlen(src)+1;
	mem_size=INT_ALIGN(len);
	ret=(char*)memAlloc(mem_size);
	memCopy(ret,src,len);
	return ret;
}

/*
 * In-place converts string to lower case.
 * FIXME: use locale to do this !
 */
char_p	V_GLOBAL
strLower(char *str)
{
	char *ptr;
	if(!str)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"strLower(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	for(ptr=str;*ptr;ptr++)
		*ptr = (char)tolower(*ptr);
	return str;
}

/*
 * Last revised: 03.01.2005
 */
vboolean V_GLOBAL
strToUInt(char *str,u32_t *ret_val)
{
	CALLER_NAME(strToUInt)
	DEBUG_MESSAGE(uint_overflow,"unsigned integer overflow while parsing: ")
	u32_t val = 0, order = 0, next_order = 1;
	u32_t test;
	char *startptr, *endptr;
	if(!str)
		return false;
	startptr = str;
	if(*startptr == '+')
		startptr++;
	endptr = startptr + strlen(startptr);
	if(endptr == startptr)
		return false;
	while(--endptr >= startptr)
	{
		if(*endptr < '0' || *endptr > '9')
			return false;
		if(next_order / 10 != order)
		{
			ERROR_GENERIC_2ARG(uint_overflow,str);
			return false;
		}
		order = next_order;
		next_order = order * 10;
		test = val + (u32_t)(*endptr - '0') * order;
		if(test < val)
		{
			ERROR_GENERIC_2ARG(uint_overflow,str);
			return false;
		}
		val = test;
	}
	if(ret_val)
		*ret_val = val;
	return true;
}

char_p V_GLOBAL
strUInt64fmt(vsize_t val,char_p str_ret)
{
	static char buf[32];
	char *ptr;
	if(!str_ret)
		str_ret = buf;
	str_ret[0] = '0';
	str_ret[1] = 'x';
	str_ret[18] = 0;
	for(ptr = str_ret + 17;*ptr != 'x';--ptr,val = val >> 4)
	{
		unsigned char dig = val & 0xF;
		*ptr = (dig < 10) ? dig + '0' : dig - 10 + 'A';
	}
	return str_ret;
}

vboolean V_GLOBAL
strToInt(char *str,s32_t *ret_val)
{
	CALLER_NAME(strToInt)
	DEBUG_MESSAGE(int_overflow,"signed integer overflow while parsing: ")
	u32_t val;
	char *startptr;
	vboolean negative;
	if(!str)
		return false;
	startptr = str;
	if(*startptr == '-')
	{
		negative = true;
		startptr++;
	}
	else
		negative = false;
	if(!strToUInt(startptr,&val))
		return false;
	if(negative)
	{
		if(val > ((u32_t)MAXLONG + 1))
		{
			ERROR_GENERIC_2ARG(int_overflow,str);
			return false;
		}
		if(ret_val)
			*ret_val = - val;
		return true;
	}
	if(val > MAXLONG)
	{
		ERROR_GENERIC_2ARG(int_overflow,str);
		return false;
	}
	if(ret_val)
		*ret_val = val;
	return true;
}

vboolean V_GLOBAL
strToHex(char *str,u32_t *ret_val)
{
	u32_t val;
	char *endptr;
	val = strtoh(str,&endptr);
	if(*endptr)
		return false;
	if(ret_val)
		*ret_val = val;
	return true;
}

vboolean V_GLOBAL
strToFloat(char *str,vec_t *ret_val)
{
	vec_t val;
	char *endptr = str;
	if(!str)
		return false;
	val = (vec_t)strtod((const char*)str,&endptr);
	if(endptr && (*endptr == '.' || *endptr == ','))
	{
		char convstr[256], c_old, c_new;
		strCopy(convstr, str, 256);
		if(*endptr == ',')
		{
			c_old = ',';
			c_new = '.';
		}
		else
		{
			c_old = '.';
			c_new = ',';
		}
		strReplaceChar(convstr,c_old,c_new);
		val = (vec_t)strtod((const char*)convstr,&endptr);
	}
	if(endptr && *endptr)
		return false;
	if(ret_val)
		*ret_val = val;
	return true;
}

vboolean V_GLOBAL
strReplaceChar(char *str,char ch_old,char replace_to)
{
	if(!str)
		return false;
	for(;*str;str++)
		if(*str == ch_old)
			*str = replace_to;
	return true;
}

vboolean V_GLOBAL
strToFloatEx(char *str,vec_t *ret_val,char dot_char)
{
	if(dot_char != '.')
	{
		u32_t slen;
		vboolean result;
		char buf[64], *str2 = buf;
		if(!str)
			return false;
		slen = strlen(str);
		if(slen < 63)
			memCopy(str2,str,slen+1);
		else
			str2 = strDup(str);
		strReplaceChar(str2,dot_char,'.');
		result = strToFloat(str2,ret_val);
		if(str2 != buf)
			memFree(str2);
		return result;
	}
	else
		return strToFloat(str,ret_val);
}

vboolean V_GLOBAL
floatToStrEx(char *dest,vsize_t dest_len,vec_t val,char dot_char)
{
	vsize_t len;
	char buf[128];
	if(!dest || !dest_len)
		return false;
	sprintf(buf,"%g",val);
	if(dot_char != '.')
		strReplaceChar(buf,'.',dot_char);
	len = strlen(buf);
	strCopy(dest,buf,dest_len);
	return ((len+1) < dest_len) ? true : false;
}

#if	defined(__WATCOMC__) || defined(__BORLANDC__)

int	LIBC_CALL strcasecmp(const char *s1,const char *s2)
{
	char c1, c2;
	if(!s1 || !s2)
		return -1;
	for(;*s1 && *s2;s1++, s2++)
	{
		c1=tolower(*s1);
		c2=tolower(*s2);
		if(c1 < c2)
			return -1;
		else
			if(c1 > c2)
				return 1;
	}
	if(!*s1 && !*s2)
		return 0;
	return *s1 ? -1 : 1;
}

int LIBC_CALL strncasecmp(const char *s1,const char *s2,unsigned n)
{
	char c1, c2;
	if(!s1 || !s2)
		return -1;
	if(!s1 || !s2)
		return -1;
	for(;*s1 && *s2 && n;s1++, s2++, n--)
	{
		c1=tolower(*s1);
		c2=tolower(*s2);
		if(c1 < c2)
			return -1;
		else
			if(c1 > c2)
				return 1;
	}
	if(!n || (!*s1 && !*s2))
		return 0;
	return *s1 ? -1 : 1;
}

#endif	/* defined(__WATCOMC__) || defined(__BORLANDC__) */
