//
// Object_String
//

#include "AScript.h"

namespace AScript {

Object_String::Object_String(const Object_String &obj) : Object(obj), _str(obj._str)
{
}

Object_String::~Object_String()
{
}

Object *Object_String::Clone() const
{
	return new Object_String(*this);
}

String Object_String::ToString(Signal sig, bool exprFlag)
{
	if (exprFlag) {
		String str;
		str += _T('"');
		str += EscapeString(_str.c_str());
		str += _T('"');
		return str;
	}
	return _str;
}

// string#len()
AScript_DeclareMethod(String, len)
{
}

AScript_ImplementMethod(String, len)
{
	Object_String *pSelf = Object_String::GetSelfObj(context);
	return Value(static_cast<Number>(::_tcslen(pSelf->GetString())));
}

// string#capitalize()
AScript_DeclareMethod(String, capitalize)
{
}

AScript_ImplementMethod(String, capitalize)
{
	Object_String *pSelf = Object_String::GetSelfObj(context);
	return Value(env, Capitalize(pSelf->GetString()).c_str());
}

// string#lower()
AScript_DeclareMethod(String, lower)
{
}

AScript_ImplementMethod(String, lower)
{
	Object_String *pSelf = Object_String::GetSelfObj(context);
	return Value(env, Lower(pSelf->GetString()).c_str());
}

// string#upper()
AScript_DeclareMethod(String, upper)
{
}

AScript_ImplementMethod(String, upper)
{
	Object_String *pSelf = Object_String::GetSelfObj(context);
	return Value(env, Upper(pSelf->GetString()).c_str());
}

// string#strip():[left,right]
AScript_DeclareMethod(String, strip)
{
	DeclareAttr(AScript_Symbol(left));
	DeclareAttr(AScript_Symbol(right));
}

AScript_ImplementMethod(String, strip)
{
	Object_String *pSelf = Object_String::GetSelfObj(context);
	return Value(env, Strip(pSelf->GetString(), context.GetAttrs()).c_str());
}

// string#left()
AScript_DeclareMethod(String, left)
{
	DeclareArg(env, _T("len"), VTYPE_Number, false, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(String, left)
{
	if (!context.IsNumber(0)) return context.GetSelf();
	Object_String *pSelf = Object_String::GetSelfObj(context);
	return Value(env, Left(pSelf->GetString(),
						static_cast<size_t>(context.GetNumber(0))).c_str());
}

// string#right()
AScript_DeclareMethod(String, right)
{
	DeclareArg(env, _T("len"), VTYPE_Number, false, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(String, right)
{
	if (!context.IsNumber(0)) return context.GetSelf();
	Object_String *pSelf = Object_String::GetSelfObj(context);
	return Value(env, Right(pSelf->GetString(),
						static_cast<size_t>(context.GetNumber(0))).c_str());
}

// string#mid(start?, len?):map
AScript_DeclareMethod(String, mid)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("start"), VTYPE_Number, false, OCCUR_ZeroOrOnce);
	DeclareArg(env, _T("len"), VTYPE_Number, false, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(String, mid)
{
	Object_String *pSelf = Object_String::GetSelfObj(context);
	return Value(env, Middle(pSelf->GetString(),
		context.IsNumber(0)? static_cast<int>(context.GetNumber(0)) : 0,
		context.IsNumber(1)? static_cast<int>(context.GetNumber(1)) : -1).c_str());
}

// string#find(sub:string, start?:number):map
AScript_DeclareMethod(String, find)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("sub"),		VTYPE_String);
	DeclareArg(env, _T("start"),	VTYPE_Number, false, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(icase));
	DeclareAttr(AScript_Symbol(rev));
}

AScript_ImplementMethod(String, find)
{
	Object_String *pSelf = Object_String::GetSelfObj(context);
	return FindString(env, sig, pSelf->GetString(), context.GetString(0),
			context.IsNumber(1)? context.GetNumber(1) : 0, context.GetAttrs());
}

// string#replace(sub:string, new:string, maxreplace?:number):map
AScript_DeclareMethod(String, replace)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("sub"),			VTYPE_String);
	DeclareArg(env, _T("replace"),		VTYPE_String);
	DeclareArg(env, _T("maxreplace"),	VTYPE_Number, false, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(icase));
}

AScript_ImplementMethod(String, replace)
{
	Object_String *pSelf = Object_String::GetSelfObj(context);
	String result = Replace(pSelf->GetString(),
			context.GetString(0), context.GetString(1),
			context.IsNumber(2)? context.GetNumber(2) : -1, context.GetAttrs());
	return Value(env, result.c_str());
}

// string#split(sep?:string, maxnumber?:number):map {block?}
AScript_DeclareMethod(String, split)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("sep"), VTYPE_String, false, OCCUR_ZeroOrOnce);
	DeclareArg(env, _T("maxnumber"), VTYPE_Number, false, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(icase));
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(String, split)
{
	Object_String *pSelf = Object_String::GetSelfObj(context);
	const TCHAR *str = pSelf->GetString();
	const TCHAR *sep = context.IsString(0)? context.GetString(0) : NULL;
	int maxNumber = context.IsNumber(1)? static_cast<int>(context.GetNumber(1)) : -1;
	return Object_String::Split(env, sig, this, context, str, sep, maxNumber);
}

Value Object_String::Split(Environment &env, Signal sig, const Function *pFunc,
		ContextBase &contextBase, const TCHAR *str, const TCHAR *sep, int maxNumber)
{
	bool ignoreCaseFlag = contextBase.IsSet(AScript_Symbol(icase));
	Value result;
	Function::ResultListComposer resultListComposer(env, contextBase, result);
	// pFuncBlock is set to NULL without error if block is not specified.
	const Function *pFuncBlock = pFunc->GetBlockFunction(env, sig, contextBase);
	if (sig.IsSignalled()) return Value::Null;
	Environment envBlock(&env, ENVTYPE_Block);
	if (sep != NULL && sep[0] != _T('\0')) {
		int lenSep = ::_tcslen(sep);
		const TCHAR *pLeft = str;
		const TCHAR *pRight = FindString(pLeft, sep, ignoreCaseFlag);
		int idx = 0;
		for ( ; pRight != NULL && idx != maxNumber;
					pRight = FindString(pLeft, sep, ignoreCaseFlag), idx++) {
			Value value(env, pLeft, pRight - pLeft);
			if (pFuncBlock != NULL) {
				value = pFuncBlock->Eval(envBlock, sig,
					Context(ValueList(value, Value(static_cast<Number>(idx)))));
				AScript_BlockSignalHandlerInLoop(sig, value, Value::Null)
			}
			resultListComposer.Store(value);
			pLeft = pRight + lenSep;
		}
		do {
			Value value(env, pLeft);
			if (pFuncBlock != NULL) {
				value = pFuncBlock->Eval(envBlock, sig,
						Context(ValueList(value, Value(static_cast<Number>(idx)))));
				AScript_BlockSignalHandlerInLoop(sig, value, Value::Null)
			}
			resultListComposer.Store(value);
		} while (0);
	} else {
		const TCHAR *p = str;
		int idx = 0;
		for ( ; *p != _T('\0') && idx != maxNumber; idx++) {
			int ch = static_cast<unsigned char>(*p);
			int len = (IsSJISFirst(ch) && *(p + 1) != _T('\0'))? 2 : 1;
			Value value(env, p, len);
			if (pFuncBlock != NULL) {
				value = pFuncBlock->Eval(envBlock, sig,
					Context(ValueList(value, Value(static_cast<Number>(idx)))));
				AScript_BlockSignalHandlerInLoop(sig, value, Value::Null)
			}
			resultListComposer.Store(value);
			p += len;
		}
		if (*p != _T('\0')) {
			do {
				Value value(env, p);
				if (pFuncBlock != NULL) {
					value = pFuncBlock->Eval(envBlock, sig,
						Context(ValueList(value, Value(static_cast<Number>(idx)))));
					AScript_BlockSignalHandlerInLoop(sig, value, Value::Null)
				}
				resultListComposer.Store(value);
			} while (0);
		}
	}
	return result;
}

// Assignment
Class_String::Class_String(Class *pClassSuper, const Symbol *pSymbol) :
												Class(pClassSuper, pSymbol)
{
	AScript_AssignMethod(String, len);
	AScript_AssignMethod(String, capitalize);
	AScript_AssignMethod(String, lower);
	AScript_AssignMethod(String, upper);
	AScript_AssignMethod(String, strip);
	AScript_AssignMethod(String, left);
	AScript_AssignMethod(String, right);
	AScript_AssignMethod(String, mid);
	AScript_AssignMethod(String, find);
	AScript_AssignMethod(String, replace);
	AScript_AssignMethod(String, split);
}

Object *Class_String::CreateDescendant(Environment &env, Signal sig, Class *pClass)
{
	return new Object_String((pClass == NULL)? this : pClass);
}

}
