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

AScript_BeginModule(time)

AScript_DeclarePrivSymbol(utc);

//-----------------------------------------------------------------------------
// AScript module functions: time
//-----------------------------------------------------------------------------
// time.sleep(secs)
AScript_DeclareFunction(sleep)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "secs", VTYPE_Number);
}

AScript_ImplementFunction(sleep)
{
	OAL::Sleep(args.GetNumber(0));
	return Value::Null;
}

// time.clock()
AScript_DeclareFunction(clock)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementFunction(clock)
{
	return Value(OAL::GetTickTime());
}

// time.monthdays(year:number, month:number):map
AScript_DeclareFunction(monthdays)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "year", VTYPE_Number);
	DeclareArg(env, "month", VTYPE_Number);
}

AScript_ImplementFunction(monthdays)
{
	int year = args.GetInt(0);
	int month = args.GetInt(1);
	return Value(static_cast<Number>(DateTime::_GetDaysOfMonth(year, month)));
}

// time.weekday(year:number, month:number, day:number):map
AScript_DeclareFunction(weekday)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "year", VTYPE_Number);
	DeclareArg(env, "month", VTYPE_Number);
	DeclareArg(env, "day", VTYPE_Number);
}

AScript_ImplementFunction(weekday)
{
	int year = args.GetInt(0);
	int month = args.GetInt(1);
	int day = args.GetInt(2);
	return Value(static_cast<Number>(DateTime::_GetDayOfWeek(year, month, day)));
}

// time.now():[utc]
AScript_DeclareFunction(now)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareAttr(AScript_PrivSymbol(utc));
}

AScript_ImplementFunction(now)
{
	DateTime dateTime = OAL::GetCurDateTime(args.IsSet(AScript_PrivSymbol(utc)));
	return Value(env, dateTime);
}

// time.today():[utc]
AScript_DeclareFunction(today)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareAttr(AScript_PrivSymbol(utc));
}

AScript_ImplementFunction(today)
{
	DateTime dateTime = OAL::GetCurDateTime(args.IsSet(AScript_PrivSymbol(utc)));
	dateTime.ClearTime();
	return Value(env, dateTime);
}

// time.datetime(year:number => 0, month:number => 1, day:number => 1,
//           hour:number => 0, min:number => 0, sec:number => 0, usec:number => 0,
//           minsoff?:number):map
AScript_DeclareFunction(datetime)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "year", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "month", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(1));
	DeclareArg(env, "day", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(1));
	DeclareArg(env, "hour", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "min", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "sec", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "usec", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "minsoff", VTYPE_Number, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(datetime)
{
	short year = static_cast<short>(args.GetLong(0));
	char month = static_cast<char>(args.GetLong(1));
	char day = static_cast<char>(args.GetLong(2));
	long sec = static_cast<long>(args.GetLong(3) * 3600 +
						args.GetLong(4) * 60 + args.GetLong(5));
	long usec = args.GetLong(6);
	if (args.IsNumber(7)) {
		long secsOffset = args.GetLong(7) * 60;
		return Value(env, DateTime(year, month, day, sec, usec, secsOffset));
	} else {
		return Value(env, DateTime(year, month, day, sec, usec));
	}
}

// time.time(hour:number => 0, minute:number => 0, sec:number => 0, usec:number => 0):map
AScript_DeclareFunction(time)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "hour", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "minute", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "sec", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "usec", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
}

AScript_ImplementFunction(time)
{
	short year = 0;
	char month = 1;
	char day = 1;
	long sec = static_cast<long>(args.GetNumber(0) * 3600 +
						args.GetNumber(1) * 60 + args.GetNumber(2));
	long usec = args.GetLong(3);
	return Value(env, DateTime(year, month, day, sec, usec));
}

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

AScript_ImplementFunction(parse)
{
	DateTime dateTime;
	if (!dateTime.Parse(args.GetString(0))) {
		sig.SetError(ERR_FormatError, "invalid time format");
		return Value::Null;
	}
	return Value(env, dateTime);
}

// time.delta(days:number => 0, secs:number => 0, usecs:number => 0):map
AScript_DeclareFunction(delta)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "days", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "secs", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "usecs", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "msecs", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "mins", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "hours", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "weeks", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	SetHelp(
	"Returns a timedelta instance with specified values. The instance actually\n"
	"holds properties of days, secs and usecs. Other values are added to them like follow\n"
	"- adds (msecs * 1000) to usecs\n"
	"- adds (mins * 60) to secs\n"
	"- adds (hours * 3600) to secs\n"
	"- adds (weeks * 7) to days\n");
}

AScript_ImplementFunction(delta)
{
	long days = static_cast<long>(args.GetNumber(0));
	long secs = static_cast<long>(args.GetNumber(1));
	long usecs = static_cast<long>(args.GetNumber(2));
	usecs += static_cast<long>(args.GetNumber(3) * 1000);
	secs += static_cast<long>(args.GetNumber(4) * 60);
	secs += static_cast<long>(args.GetNumber(5) * 3600);
	days += static_cast<long>(args.GetNumber(6) * 7);
	return Value(env, TimeDelta(days, secs, usecs));
}

// time.isleap(year:number):map
AScript_DeclareFunction(isleap)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "year", VTYPE_Number);
}

AScript_ImplementFunction(isleap)
{
	return Value(DateTime::_IsLeapYear(args.GetShort(0)));
}

AScript_ModuleEntry()
{
	AScript_RealizePrivSymbol(utc);
	// value assignment
	AScript_AssignValue(Sunday,		Value(0));
	AScript_AssignValue(Monday,		Value(1));
	AScript_AssignValue(Tuesday,	Value(2));
	AScript_AssignValue(Wednesday,	Value(3));
	AScript_AssignValue(Thursday,	Value(4));
	AScript_AssignValue(Friday,		Value(5));
	AScript_AssignValue(Saturday,	Value(6));
	// function assignment
	AScript_AssignFunction(sleep);
	AScript_AssignFunction(clock);
	AScript_AssignFunction(monthdays);
	AScript_AssignFunction(weekday);
	AScript_AssignFunction(now);
	AScript_AssignFunction(today);
	AScript_AssignFunction(datetime);
	AScript_AssignFunction(time);
	AScript_AssignFunction(parse);
	AScript_AssignFunction(delta);
	AScript_AssignFunction(isleap);
}

AScript_ModuleTerminate()
{
}


AScript_EndModule(time, time)

AScript_RegisterModule(time)
