//-----------------------------------------------------------------------------
// AScript string module
//-----------------------------------------------------------------------------
#include <ascript.h>

AScript_BeginModule(string)

//-----------------------------------------------------------------------------
// AScript module functions: string
//-----------------------------------------------------------------------------
// n = string.len(str:string):map
AScript_DeclareFunction(len)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
}

AScript_ImplementFunction(len)
{
	return Value(static_cast<Number>(Length(args.GetString(0))));
}

// str = string.capitalize(str:string):map
AScript_DeclareFunction(capitalize)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
}

AScript_ImplementFunction(capitalize)
{
	return Value(env, Capitalize(args.GetString(0)).c_str());
}

// str = string.upper(str:string):map
AScript_DeclareFunction(upper)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
}

AScript_ImplementFunction(upper)
{
	return Value(env, Upper(args.GetString(0)).c_str());
}

// str = string.lower(str:string):map
AScript_DeclareFunction(lower)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
}

AScript_ImplementFunction(lower)
{
	return Value(env, Lower(args.GetString(0)).c_str());
}

// str = string.strip(str:string):map:[both,left,right]
AScript_DeclareFunction(strip)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
	DeclareAttr(AScript_Symbol(both));
	DeclareAttr(AScript_Symbol(left));
	DeclareAttr(AScript_Symbol(right));
}

AScript_ImplementFunction(strip)
{
	return Value(env, Strip(args.GetString(0), args.GetAttrs()).c_str());
}

// str = string.align(str:string, len:number, padding:string => " "):map
AScript_DeclareFunction(align)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
	DeclareArg(env, "len", VTYPE_Number);
	DeclareArg(env, "padding", VTYPE_String, OCCUR_Once, false, false, new Expr_String(" "));
	DeclareAttr(AScript_Symbol(left));
	DeclareAttr(AScript_Symbol(right));
	DeclareAttr(AScript_Symbol(center));
}

AScript_ImplementFunction(align)
{
	size_t len = args.GetSizeT(1);
	const char *padding = args.GetString(2);
	if (Length(padding) != 1) {
		sig.SetError(ERR_ValueError, "padding must consist of a single character");
		return Value::Null;
	}
	String str;
	if (args.IsSet(AScript_Symbol(right))) {
		str = RJust(args.GetString(0), len, padding);
	} else if (args.IsSet(AScript_Symbol(center))) {
		str = Center(args.GetString(0), len, padding);
	} else {
		str = LJust(args.GetString(0), len, padding);
	}
	return Value(env, str.c_str());
}

// str = string.left(str:string, len?:number):map
AScript_DeclareFunction(left)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
	DeclareArg(env, "len", VTYPE_Number, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(left)
{
	if (args.IsInvalid(1)) return args.GetValue(0);
	return Value(env, Left(args.GetString(0), args.GetSizeT(1)).c_str());
}

// str = string.right(str, len?):map
AScript_DeclareFunction(right)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
	DeclareArg(env, "len", VTYPE_Number, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(right)
{
	if (args.IsInvalid(1)) return args.GetValue(0);
	return Value(env, Right(args.GetString(0), args.GetSizeT(1)).c_str());
}

// str = string.mid(str:string, start:number => 0, len?):map
AScript_DeclareFunction(mid)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
	DeclareArg(env, "start", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "len", VTYPE_Number, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(mid)
{
	return Value(env, Middle(args.GetString(0),
		args.GetInt(1), args.IsNumber(2)? args.GetInt(2) : -1).c_str());
}

// result = string.find(str:string, sub:string, start:number => 0):map
// attributes: list, rev, icase
AScript_DeclareFunction(find)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str",		VTYPE_String);
	DeclareArg(env, "sub",		VTYPE_String);
	DeclareArg(env, "start",	VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareAttr(AScript_Symbol(icase));
	DeclareAttr(AScript_Symbol(rev));
}

AScript_ImplementFunction(find)
{
	return FindString(env, sig, args.GetString(0), args.GetString(1),
										args.GetInt(2), args.GetAttrs());
}

// result = string.replace(str:string, sub:string, new:string, maxreplace?:number):map
AScript_DeclareFunction(replace)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str",			VTYPE_String);
	DeclareArg(env, "sub",			VTYPE_String);
	DeclareArg(env, "replace",		VTYPE_String);
	DeclareArg(env, "maxreplace",	VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(icase));
}

AScript_ImplementFunction(replace)
{
	String result = Replace(args.GetString(0),
			args.GetString(1), args.GetString(2),
			args.IsNumber(3)? args.GetInt(3) : -1, args.GetAttrs());
	return Value(env, result.c_str());
}

// str = string.join(strs[]:string, sep:string => "")
AScript_DeclareFunction(join)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "strs", VTYPE_String, OCCUR_Once, true);
	DeclareArg(env, "sep", VTYPE_String, OCCUR_Once, false, false, new Expr_String(""));
}

AScript_ImplementFunction(join)
{
	const ValueList &valList = args.GetList(0);
	return Value(env, Join(valList, args.GetString(1)).c_str());
}

// string.eachline(str:string, nlines?:number):map:[chop] {block?}
AScript_DeclareFunction(eachline)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
	DeclareArg(env, "nlines", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(chop));
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(eachline)
{
	Object_String *pSelf = args.GetStringObj(0);
	Object_String *pObj = Object_String::Reference(pSelf);
	int maxSplit = args.IsNumber(1)? args.GetInt(1) : -1;
	bool includeEOLFlag = !args.IsSet(AScript_Symbol(chop));
	return ReturnIterator(env, sig, args,
				new Object_String::IteratorLine(pObj, maxSplit, includeEOLFlag));
}

// string.split(str:string, sep?:string, maxnumber?:number):map {block?}
AScript_DeclareFunction(split)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
	DeclareArg(env, "sep", VTYPE_String, OCCUR_ZeroOrOnce);
	DeclareArg(env, "maxnumber", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(icase));
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(split)
{
	Object_String *pSelf = args.GetStringObj(0);
	Object_String *pObj = Object_String::Reference(pSelf);
	const char *str = pSelf->GetString();
	int maxSplit = args.IsNumber(2)? args.GetInt(2) : -1;
	Iterator *pIterator = NULL;
	if (args.IsString(1)) {
		const char *sep = args.GetString(1);
		bool ignoreCaseFlag = args.IsSet(AScript_Symbol(icase));
		pIterator = new Object_String::IteratorSplit(
										pObj, sep, maxSplit, ignoreCaseFlag);
	} else {
		pIterator = new Object_String::IteratorEach(pObj, maxSplit, false);
	}
	return ReturnIterator(env, sig, args, pIterator);
}

// Module entry
AScript_ModuleEntry()
{
	// function assignment
	AScript_AssignFunction(len);
	AScript_AssignFunction(capitalize);
	AScript_AssignFunction(lower);
	AScript_AssignFunction(upper);
	AScript_AssignFunction(strip);
	AScript_AssignFunction(align);
	AScript_AssignFunction(left);
	AScript_AssignFunction(right);
	AScript_AssignFunction(mid);
	AScript_AssignFunction(join);
	AScript_AssignFunction(split);
	AScript_AssignFunction(eachline);
	AScript_AssignFunction(find);
	AScript_AssignFunction(replace);
}

AScript_ModuleTerminate()
{
}

AScript_EndModule(string, string)

AScript_RegisterModule(string)
