//
// Object_Expr
//

#include "ascript/Object_Expr.h"
#include "ascript/Expr.h"

#define ImplementTypeChecker(funcName, func) \
AScript_DeclareMethod(Expr, funcName) {} \
AScript_ImplementMethod(Expr, funcName) { \
	return Value(Object_Expr::GetSelfObj(args)->GetExpr()->func()); \
}

namespace AScript {

//-----------------------------------------------------------------------------
// Object_Expr
//-----------------------------------------------------------------------------
Object_Expr::Object_Expr(const Object_Expr &obj) :
								Object(obj), _pExpr(obj._pExpr->IncRef())
{
}

Object_Expr::~Object_Expr()
{
	Expr::Delete(_pExpr);
}

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

String Object_Expr::ToString(Signal sig, bool exprFlag)
{
	String str;
	if (_pExpr->IsValue() || _pExpr->IsSymbol() || _pExpr->IsCaller()) {
		if (exprFlag) str += '`';
		str += _pExpr->ToString();
	} else if (exprFlag) {
		if (_pExpr->IsUnary() || _pExpr->IsBinary()) {
			str += "`(";
			str += _pExpr->ToString();
			str += ")";
		} else {
			str += "`";
			str += _pExpr->ToString();
		}
	} else {
		str += _pExpr->ToString();
	}
	return str;
}

//-----------------------------------------------------------------------------
// Object_Expr::Iterator_Each
//-----------------------------------------------------------------------------
Object_Expr::Iterator_Each::~Iterator_Each()
{
	Expr::Delete(_pExprContainer);
}

bool Object_Expr::Iterator_Each::DoNext(Environment &env, Signal sig, Value &value)
{
	if (_ppExpr == _pExprContainer->GetExprList().end()) return false;
	Object_Expr *pObj = new Object_Expr(_env, (*_ppExpr)->IncRef());
	value = Value(pObj);
	_ppExpr++;
	return true;
}

String Object_Expr::Iterator_Each::ToString(Signal sig) const
{
	String rtn = "<iterator:expr:each>";
	return rtn;
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Expr
//-----------------------------------------------------------------------------
// expr#child()
AScript_DeclareMethod(Expr, child)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, child)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsUnary()) {
		sig.SetError(ERR_ValueError, "not a unary expression");
		return Value::Null;
	}
	Value result;
	result.InitAsExpr(env,
			dynamic_cast<const Expr_Unary *>(pExpr)->GetChild()->IncRef());
	return result;
}

// expr#left()
AScript_DeclareMethod(Expr, left)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, left)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsBinary()) {
		sig.SetError(ERR_ValueError, "not a binary expression");
		return Value::Null;
	}
	Value result;
	result.InitAsExpr(env,
			dynamic_cast<const Expr_Binary *>(pExpr)->GetLeft()->IncRef());
	return result;
}

// expr#right()
AScript_DeclareMethod(Expr, right)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, right)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsBinary()) {
		sig.SetError(ERR_ValueError, "not a binary expression");
		return Value::Null;
	}
	Value result;
	result.InitAsExpr(env,
			dynamic_cast<const Expr_Binary *>(pExpr)->GetRight()->IncRef());
	return result;
}

// expr#each() {block?}
AScript_DeclareMethod(Expr, each)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Expr, each)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsContainer()) {
		sig.SetError(ERR_ValueError, "not a container expression");
		return Value::Null;
	}
	Iterator *pIterator = new Object_Expr::Iterator_Each(env, 
							dynamic_cast<Expr_Container *>(pExpr->IncRef()));
	return ReturnIterator(env, sig, args, pIterator);
}

// expr#car()
AScript_DeclareMethod(Expr, car)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, car)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsCompound()) {
		sig.SetError(ERR_ValueError, "not a compound expression");
		return Value::Null;
	}
	Value result;
	result.InitAsExpr(env,
			dynamic_cast<const Expr_Compound *>(pExpr)->GetCar()->IncRef());
	return result;
}

// expr#cdr()
AScript_DeclareMethod(Expr, cdr)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, cdr)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsCompound()) {
		sig.SetError(ERR_ValueError, "not a compound expression");
		return Value::Null;
	}
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ExprList, ppExpr,
					dynamic_cast<const Expr_Compound *>(pExpr)->GetExprList()) {
		const Expr *pExpr = *ppExpr;
		Value value;
		value.InitAsExpr(env, pExpr->IncRef());
		valList.push_back(value);
	}
	return result;
}

// expr#unquote()
AScript_DeclareMethod(Expr, unquote)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, unquote)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	Value result;
	result.InitAsExpr(env, pExpr->Unquote()->IncRef());
	return result;
}

// expr#block()
AScript_DeclareMethod(Expr, block)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, block)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsCaller()) {
		sig.SetError(ERR_ValueError, "not a caller expression");
		return Value::Null;
	}
	const Expr_Block *pExprBlock =
						dynamic_cast<const Expr_Caller *>(pExpr)->GetBlock();
	if (pExprBlock == NULL) return Value::Null;
	Value result;
	result.InitAsExpr(env, pExprBlock->IncRef());
	return result;
}

// expr#exprname()
AScript_DeclareMethod(Expr, exprname)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, exprname)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	return Value(env, pExpr->GetTypeName());
}

// expr#getvalue()
AScript_DeclareMethod(Expr, getvalue)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, getvalue)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsValue()) {
		sig.SetError(ERR_ValueError, "expression is not a value");
		return Value::Null;
	}
	return dynamic_cast<const Expr_Value *>(pExpr)->GetValue();
}

// expr#getstring()
AScript_DeclareMethod(Expr, getstring)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, getstring)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsString()) {
		sig.SetError(ERR_ValueError, "expression is not a string");
		return Value::Null;
	}
	return Value(env, dynamic_cast<const Expr_String *>(pExpr)->GetString());
}

// expr#getsymbol()
AScript_DeclareMethod(Expr, getsymbol)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Expr, getsymbol)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	if (!pExpr->IsSymbol()) {
		sig.SetError(ERR_ValueError, "expression is not a symbol");
		return Value::Null;
	}
	return Value(dynamic_cast<const Expr_Symbol *>(pExpr)->GetSymbol());
}

// expr#tofunction(`args*)
AScript_DeclareMethod(Expr, tofunction)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "args", VTYPE_Quote, OCCUR_ZeroOrMore);
}

AScript_ImplementMethod(Expr, tofunction)
{
	const Expr *pExpr = Object_Expr::GetSelfObj(args)->GetExpr();
	Function *pFunc = pExpr->ToFunction(env, sig,
								args.GetList(0), args.GetAttrs());
	return Value(env, pFunc, Value::Null);
}

// type chekers
ImplementTypeChecker(isunary,		IsUnary)
ImplementTypeChecker(isunaryop,		IsUnaryOp)
ImplementTypeChecker(isquote,		IsQuote)
ImplementTypeChecker(isforce,		IsForce)
ImplementTypeChecker(isprefix,		IsPrefix)
ImplementTypeChecker(issuffix,		IsSuffix)

ImplementTypeChecker(isbinary,		IsBinary)
ImplementTypeChecker(isbinaryop,	IsBinaryOp)
ImplementTypeChecker(isassign,		IsAssign)
ImplementTypeChecker(isdictassign,	IsDictAssign)
ImplementTypeChecker(isfield,		IsField)

ImplementTypeChecker(iscontainer,	IsContainer)
ImplementTypeChecker(isblockparam,	IsBlockParam)
ImplementTypeChecker(isblock,		IsBlock)
ImplementTypeChecker(islister,		IsLister)

ImplementTypeChecker(isvalue,		IsValue)
ImplementTypeChecker(isstring,		IsString)
ImplementTypeChecker(issymbol,		IsSymbol)
ImplementTypeChecker(isindexer,		IsIndexer)
ImplementTypeChecker(iscaller,		IsCaller)

// Assignment
Class_Expr::Class_Expr(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(Expr, child);
	AScript_AssignMethod(Expr, left);
	AScript_AssignMethod(Expr, right);
	AScript_AssignMethod(Expr, each);
	AScript_AssignMethod(Expr, car);
	AScript_AssignMethod(Expr, cdr);
	AScript_AssignMethod(Expr, unquote);
	AScript_AssignMethod(Expr, block);
	AScript_AssignMethod(Expr, exprname);
	AScript_AssignMethod(Expr, getvalue);
	AScript_AssignMethod(Expr, getstring);
	AScript_AssignMethod(Expr, getsymbol);
	AScript_AssignMethod(Expr, tofunction);
	AScript_AssignMethod(Expr, isunary);
	AScript_AssignMethod(Expr, isunaryop);
	AScript_AssignMethod(Expr, isquote);
	AScript_AssignMethod(Expr, isforce);
	AScript_AssignMethod(Expr, isprefix);
	AScript_AssignMethod(Expr, issuffix);
	AScript_AssignMethod(Expr, isbinary);
	AScript_AssignMethod(Expr, isbinaryop);
	AScript_AssignMethod(Expr, isassign);
	AScript_AssignMethod(Expr, isdictassign);
	AScript_AssignMethod(Expr, isfield);
	AScript_AssignMethod(Expr, iscontainer);
	AScript_AssignMethod(Expr, isblockparam);
	AScript_AssignMethod(Expr, isblock);
	AScript_AssignMethod(Expr, islister);
	AScript_AssignMethod(Expr, isvalue);
	AScript_AssignMethod(Expr, isstring);
	AScript_AssignMethod(Expr, issymbol);
	AScript_AssignMethod(Expr, isindexer);
	AScript_AssignMethod(Expr, iscaller);
}

bool Class_Expr::CastFrom(Environment &env, Signal sig, Value &value)
{
	if (value.IsSymbol()) {		// cast Symbol to Expr
		const Symbol *pSymbol = value.GetSymbol();
		value.InitAsExpr(env, new Expr_Symbol(pSymbol));
		return true;
	}
	return false;
}

Object *Class_Expr::CreateDescendant(Environment &env, Signal sig, Class *pClass)
{
	ERROREND(env, "this function must not be called");
	return NULL;
}

}
