#include "String.h"
#include "Symbol.h"
#include "Value.h"

namespace AScript {

//-----------------------------------------------------------------------------
// String
//-----------------------------------------------------------------------------
const StringList StringList::Null;

int GetEscaped(int ch)
{
	static const struct {
		int ch;
		int chConv;
	} tbl[] = {
		{ _T('a'),	_T('\a')	},
		{ _T('b'),	_T('\b')	},
		{ _T('f'),	_T('\f')	},
		{ _T('n'),	_T('\n')	},
		{ _T('r'),	_T('\r')	},
		{ _T('t'),	_T('\t')	},
		{ _T('v'),	_T('\v')	},
		{ _T('\\'),	_T('\\')	},
		{ _T('\''),	_T('\'')	},
		{ _T('"'),	_T('"')		},
	};
	for (int i = 0; i < NUMBEROF(tbl); i++) {
		if (tbl[i].ch == ch) return tbl[i].chConv;
	}
	return ch;
}

String EscapeString(const TCHAR *str)
{
	String rtn;
	bool shiftFlag = false;
	for (const TCHAR *p = str; *p != _T('\0'); p++) {
		TCHAR ch = *p;
		bool sjisFirstFlag = IsSJISFirst(static_cast<unsigned char>(ch));
		if (ch == _T('\n')) {
			rtn += _T("\\n");
		} else if (ch == _T('\r')) {
			rtn += _T("\\r");
		} else if (ch == _T('\t')) {
			rtn += _T("\\t");
		} else if (ch == _T('"')) {
			rtn += _T("\\\"");
		} else if (ch == _T('\\') && !shiftFlag) {
			rtn += _T("\\\\");
		} else if (sjisFirstFlag || shiftFlag) {
			rtn += ch;
		} else if (ch < 0x20 || ch >= 0x7f) {
			TCHAR tmp[16];
			::_stprintf(tmp, _T("\\x%02x"), ch);
			rtn += tmp;
		} else {
			rtn += ch;
		}
		shiftFlag = shiftFlag? false : sjisFirstFlag;
	}
	return rtn;
}

const TCHAR *FindString(const TCHAR *str, const TCHAR *sub, bool ignoreCaseFlag)
{
	for ( ; *str != _T('\0'); str++) {
		const TCHAR *p1 = str, *p2 = sub;
		for ( ; *p2 != _T('\0'); p1++, p2++) {
			if (CompareChar(*p1, *p2, ignoreCaseFlag) != 0) break;
		}
		if (*p2 == _T('\0')) return str;
	}
	return NULL;
}

String Capitalize(const TCHAR *str)
{
	String rtn;
	const TCHAR *p = str;
	if (*p != _T('\0')) {
		rtn += ('a' <= *p && *p <= 'z')? *p - 'a' + 'A' : *p;
		p++;
	}
	for ( ; *p != _T('\0'); p++) {
		rtn += ('A' <= *p && *p <= 'Z')? *p - 'A' + 'a' : *p;
	}
	return rtn;
}

String Lower(const TCHAR *str)
{
	String rtn;
	for (const TCHAR *p = str; *p != _T('\0'); p++) {
		rtn += ('A' <= *p && *p <= 'Z')? *p - 'A' + 'a' : *p;
	}
	return rtn;
}

String Upper(const TCHAR *str)
{
	String rtn;
	for (const TCHAR *p = str; *p != _T('\0'); p++) {
		rtn += ('a' <= *p && *p <= 'z')? *p - 'a' + 'A' : *p;
	}
	return rtn;
}

String Strip(const TCHAR *str, const SymbolSet &attrs)
{
	bool stripLeftFlag = true, stripRightFlag = true;
	if (attrs.IsSet(AScript_Symbol(left)) && !attrs.IsSet(AScript_Symbol(right))) {
		stripRightFlag = false;
	} else if (!attrs.IsSet(AScript_Symbol(left)) && attrs.IsSet(AScript_Symbol(right))) {
		stripLeftFlag = false;
	}
	size_t len = ::_tcslen(str);
	if (len == 0) return String(_T(""));
	const TCHAR *p1 = str;
	const TCHAR *p2 = str + len - 1;
	if (stripLeftFlag) {
		for ( ; IsSpace(*p1); p1++) ;
	}
	if (stripRightFlag) {
		for ( ; p2 != p1 && IsSpace(*p2); p2--) ;
	}
	return String(p1, p2 - p1 + 1);
}

String Left(const TCHAR *str, size_t len)
{
	return (len < ::_tcslen(str))? String(str, 0, len) : String(str);
}

String Right(const TCHAR *str, size_t len)
{
	size_t lenSrc = ::_tcslen(str);
	return (len < lenSrc)? String(str + lenSrc - len) : String(str);
}

String Middle(const TCHAR *str, int start, int len)
{
	int lenSrc = static_cast<int>(::_tcslen(str));
	if (start < 0) {
		start += lenSrc;
		if (start < 0) start = 0;
	}
	if (start >= lenSrc || len == 0) {
		return String(_T(""));
	} else if (len > 0 && start + len < lenSrc) {
		return String(str + start, len);
	} else {
		return String(str + start);
	}
}

String Join(const ValueList &valList, const TCHAR *str)
{
	Signal sig;
	ValueList::const_iterator pValue = valList.begin();
	if (pValue == valList.end()) return _T("");
	String rtn = pValue->ToString(sig, false);
	pValue++;
	for ( ; pValue != valList.end(); pValue++) {
		rtn += str;
		rtn += pValue->ToString(sig, false);
	}
	return rtn;
}

Value FindString(Environment &env, Signal sig,
		const TCHAR *str, const TCHAR *sub, int start, const SymbolSet &attrs)
{
	bool ignoreCaseFlag = attrs.IsSet(AScript_Symbol(icase));
	do {
		int len = static_cast<int>(::_tcslen(str));
		if (start < 0) {
			start += len;
			if (start < 0) start = 0;
		}
		if (start > len) return Value::Null;
	} while (0);
	if (attrs.IsSet(AScript_Symbol(rev))) {
		const TCHAR *p = FindString(str + start, sub, ignoreCaseFlag);
		if (attrs.IsSet(AScript_Symbol(list))) {
			ValueList valListOrg;
			for ( ; p != NULL; p = FindString(p + 1, sub, ignoreCaseFlag)) {
				valListOrg.push_back(Value(static_cast<Number>(p - str)));
			}
			Value result;
			ValueList &valList = result.InitAsList(env);
			foreach_reverse (ValueList, pValue, valListOrg) {
				valList.push_back(*pValue);
			}
			return result;
		} else {
			const TCHAR *pLast = NULL;
			for ( ; p != NULL; p = FindString(p + 1, sub, ignoreCaseFlag)) {
				pLast = p;
			}
			return (pLast == NULL)? Value::Null :
							Value(static_cast<Number>(pLast - str));
		}
	} else {
		const TCHAR *p = FindString(str + start, sub, ignoreCaseFlag);
		if (attrs.IsSet(AScript_Symbol(list))) {
			Value result;
			ValueList &valList = result.InitAsList(env);
			for ( ; p != NULL; p = FindString(p + 1, sub, ignoreCaseFlag)) {
				valList.push_back(Value(static_cast<Number>(p - str)));
			}
			return result;
		} else {
			return (p == NULL)? Value::Null :
							Value(static_cast<Number>(p - str));
		}
	}
}

String Replace(const TCHAR *str, const TCHAR *sub, const TCHAR *replace,
										int nMaxReplace, const SymbolSet &attrs)
{
	bool ignoreCaseFlag = attrs.IsSet(AScript_Symbol(icase));
	String result;
	int lenSub = ::_tcslen(sub);
	if (lenSub == 0) {
		if (nMaxReplace != 0) {
			result += replace;
			nMaxReplace--;
		}
		const TCHAR *p = str;
		for ( ; *p != _T('\0') && nMaxReplace != 0; p++, nMaxReplace--) {
			result += *p;
			result += replace;
		}
		result += p;
	} else {
		const TCHAR *pLeft = str;
		const TCHAR *pRight = FindString(pLeft, sub, ignoreCaseFlag);
		for ( ; pRight != NULL && nMaxReplace != 0;
					pRight = FindString(pLeft, sub, ignoreCaseFlag), nMaxReplace--) {
			result.append(pLeft, pRight - pLeft);
			result += replace;
			pLeft = pRight + lenSub;
		}
		result += pLeft;
	}
	return result;
}

void SplitPathList(Environment &env, const TCHAR *str, ValueList &valList)
{
	enum {
		STAT_SkipSpace, STAT_Field,
	} stat = STAT_SkipSpace;
	TCHAR ch = _T('\0');
	String field;
	int cntSpace = 0;
	bool eatNextFlag = true;
	do {
		if (eatNextFlag) ch = *str++;
		eatNextFlag = true;
		if (stat == STAT_SkipSpace) {
			if (IsSpace(ch)) {
				// nothing to do
			} else {
				field.clear();
				cntSpace = 0;
				eatNextFlag = false;
				stat = STAT_Field;
			}
		} else if (stat == STAT_Field) {
			if (IsPathSeparator(ch) || ch == _T('\0')) {
				valList.push_back(Value(env, field.c_str()));
				stat = STAT_SkipSpace;
			} else if (ch == _T(' ')) {
				cntSpace++;
			} else {
				while (cntSpace-- > 0) field.push_back(_T(' '));
				field.push_back(ch);
			}
		}
	} while (ch != _T('\0'));
}

}
