#include "cString.hpp"

/*** ANSI C Header Files ***/
#include <stdlib.h>     /* strtol */

#ifdef _MSC_VER
// disable _s warnings
#	define _CRT_SECURE_NO_WARNINGS
// disable pragma warnings
#	pragma warning( disable : 4068 )
#	pragma warning( disable : 4996 )
#	include "MSUNIX/msunix.hpp"
#endif

using UtilityEngine::cString;

cString::cString() 
{
	clear();
}

cString::cString(const char* s) 
{
	copy_str(s);
}

cString::cString(const cString& copy) 
{
	copy_str(copy);
}


cString::~cString() 
{
	clear();
}

const char* cString::alloc_str(size_t sz) 
{
	if (mp_str) 
		clear();
	m_len = (sz > __cString__MAX_LEN) ? __cString__MAX_LEN : sz;
	mp_str = (char *)calloc(1, m_len + 1);
	return mp_str;
}

void cString::clear() 
{
	if (mp_str) 
		free((void *)mp_str);
	mp_str = nullptr;
	m_len = 0;
}

const char* cString::c_str() const 
{
	return mp_str;
}

const char* cString::copy_str(const char* copy) 
{
	if (copy) {
		size_t len = strnlen(copy, __cString__MAX_LEN);
		alloc_str(len);
		strncpy((char *)mp_str, copy, len);
		m_len = len;
	}
	return mp_str;
}

bool cString::have_value() const
{
	if (mp_str)
		return true;
	else
		return false;
}

size_t cString::length() const
{
	return m_len;
}

size_t cString::size() const
{
	return m_len;
}

// string format
cString& cString::format(const char* format, ...)
{
	char * buffer;

	va_list args;
	va_start(args, format);

	vasprintf(&buffer, format, args);
	copy_str(buffer);
	free(buffer);
	return *this;
}

// trim leading and trailing spaces
cString& cString::trim()
{
	const static char * whitespace = "\x20\x1b\t\r\n\v\b\f\a";

	if (!have_value())
		return *this;	// make sure we have a string

	size_t begin = 0;
	size_t end = length() - 1;

	for (begin = 0; begin <= end; ++begin) {
		if (strchr(whitespace, mp_str[begin]) == nullptr) {
			break;
		}
	}

	for (; end > begin; --end) {
		if (strchr(whitespace, mp_str[end]) == nullptr) {
			break;
		}
		else {
			mp_str[end] = '\0';
		}
	}

	if (begin) {
		for (size_t i = 0; mp_str[i]; ++i) {
			mp_str[i] = mp_str[begin++];
		}
	}

	m_len = strlen(mp_str);
	return *this;
}

cString cString::lower() const
{
	cString rs = *this;
	for (size_t i = 0; rs.mp_str[i]; ++i) {
		rs.mp_str[i] = (char)tolower(rs.mp_str[i]);
	}
	return rs;
}

cString cString::upper() const
{
	cString rs = *this;
	for (size_t i = 0; rs.mp_str[i]; ++i) {
		rs.mp_str[i] = (char)toupper(rs.mp_str[i]);
	}
	return rs;
}

const char& cString::first_char() const
{
	return mp_str[0];
}

const char& cString::last_char() const
{
	return mp_str[length() - 1];
}

// non-destructive split
const std::vector<cString> cString::split(const char match) const
{
	const char match_s[2] = { match, 0 };
	return split(match_s, -1);
}

const std::vector<cString> cString::split(const char* match) const
{
	return split(match, -1);
}

const std::vector<cString> cString::split(const char* match, int max_split) const
{
	std::vector<cString> rtn;
	if (length() < 1) 
		return rtn;
	size_t match_len = strnlen(match, __cString__MAX_LEN);
	if (match_len >= __cString__MAX_LEN) 
		return rtn;

	char* mi;				// match index
	char* pstr = mp_str;		// string pointer
	while ((mi = strstr(pstr, match)) && (max_split < 0 || --max_split)) {
		if (mi != pstr) {
			size_t lhsz = mi - pstr;
			char* cslhs = (char *)malloc(lhsz + 1);
			cslhs[lhsz] = '\0'; // strncpy doesn't terminate it
			rtn.emplace_back(strncpy(cslhs, pstr, lhsz)); // calls cString copy ctor
			pstr += lhsz;
			free(cslhs);
		}
		pstr += match_len;
	}

	if (*pstr != '\0') {
		rtn.emplace_back(pstr);
	}

	return rtn;
}

const cString& cString::char_repl(const char& match, const char& repl)
{
	for (size_t i = 0; mp_str[i]; ++i) {
		if (mp_str[i] == match) mp_str[i] = repl;
	}
	return *this;
}

long int cString::char_find(const char& match) const
{
	for (size_t i = 0; mp_str[i]; ++i) {
		if (mp_str[i] == match) 
			return i;
	}
	return -1;
}

cString cString::substr(size_t start, size_t length) const
{
	cString rs;
	char * buf;
	if ((length + 1) > __cString__MAX_LEN)
		return rs;
	if ((start + length) > __cString__MAX_LEN)
		return rs;
	if (length > m_len - start)
		return rs;
	if (!mp_str)
		return rs;

	buf = (char *)calloc(sizeof(char), length + 1);
	memcpy(buf, mp_str + start, length);
	rs = buf;
	return rs;
}

long int cString::find(const cString& match)
{
	char * pos = strstr(mp_str, match.c_str());
	if (pos)
		return (long)(pos - mp_str);
	else
		return -1;
}

const cString cString::replace(const cString& match, const cString& repl)
{
	cString rs;
	long f1 = find(match);
	if (f1 >= 0) {
		size_t pos1 = (size_t)f1;
		size_t pos2 = pos1 + match.length();
		cString s1 = pos1 > 0 ? substr(0, pos1) : "";
		cString s2 = substr(pos2, length() - pos2);
		rs = s1 + repl + s2;
	}
	return rs;
}

const bool cString::IsInt() const
{
	bool rtn = false;
	cString tmp = c_str();
	tmp.trim();
	switch (tmp.first_char())
	{
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
		rtn = true;
		break;
	default:
		rtn = false;
		break;
	}
	return rtn;
}

const long int cString::ToInt() const
{
	return strtol(mp_str, NULL, 10);
}

cString& cString::operator = (const char* rhs) 
{
	copy_str(rhs);
	return *this;
}

cString& cString::operator = (const cString& rhs) 
{
	copy_str(rhs.c_str());
	return *this;
}

cString& cString::operator += (const char rhs)
{
	operator+=(&rhs);
	return *this;
}

cString& cString::operator += (const char* rhs) 
{
	if (rhs) {
		size_t newlen = m_len + strnlen(rhs, __cString__MAX_LEN);
		if (newlen > __cString__MAX_LEN) 
			newlen = __cString__MAX_LEN;
		char * buf = (char *)calloc(1, newlen + 1);
		if (!buf) 
			return *this;

		if (mp_str && m_len) 
			strncpy(buf, mp_str, m_len);
		strncpy(buf + m_len, rhs, newlen - m_len);

		buf[newlen] = '\0';
		copy_str(buf);
		free(buf);
	}
	return *this;
}

cString& cString::operator += (const cString& rhs) 
{
	operator+=(rhs.c_str());
	return *this;
}

bool cString::operator == (const char* rhs) const
{
	if (std::strncmp(this->c_str(), rhs, __cString__MAX_LEN) == 0)
		return true;
	else
		return false;
}

bool cString::operator == (const cString& rhs) const 
{
	if (std::strncmp(this->c_str(), rhs.c_str(), __cString__MAX_LEN) == 0) 
		return true;
	else 
		return false;
}

bool cString::operator != (const char* rhs) const
{
	if (std::strncmp(this->c_str(), rhs, __cString__MAX_LEN) != 0)
		return true;
	else
		return false;
}

bool cString::operator != (const cString& rhs) const 
{
	if (std::strncmp(this->c_str(), rhs.c_str(), __cString__MAX_LEN) != 0) 
		return true;
	else 
		return false;
}

bool cString::operator > (const cString& rhs) const 
{
	if (std::strncmp(this->c_str(), rhs.c_str(), __cString__MAX_LEN) > 0) 
		return true;
	else 
		return false;
}

bool cString::operator < (const cString& rhs) const 
{
	if (std::strncmp(this->c_str(), rhs.c_str(), __cString__MAX_LEN) < 0) 
		return true;
	else 
		return false;
}

bool cString::operator >= (const cString& rhs) const 
{
	if (std::strncmp(this->c_str(), rhs.c_str(), __cString__MAX_LEN) >= 0) 
		return true;
	else 
		return false;
}

bool cString::operator <= (const cString& rhs) const 
{
	if (std::strncmp(this->c_str(), rhs.c_str(), __cString__MAX_LEN) <= 0) 
		return true;
	else return false;
}

cString::operator const char* () const 
{
	return c_str();
}

cString::operator std::string () const 
{
	return std::string(c_str());
}

cString operator + (const cString& lhs, const cString& rhs) 
{
 	cString rs = lhs;
 	rs += rhs;
 	return rs;
}

