#include "ascript/Declaration.h"
#include "ascript/Expr.h"
#include "ascript/Environment.h"
#include "ascript/Object.h"
#include "ascript/Object_Matrix.h"
#include "ascript/Object_Binary.h"
#include "ascript/Function.h"

namespace AScript {

inline bool IsSuffixed(const Expr *pExpr, const Symbol *pSymbol)
{
	return pExpr->IsSuffix() &&
		dynamic_cast<const Expr_Suffix *>(pExpr)->GetSymbol()->IsIdentical(pSymbol);
}

//-----------------------------------------------------------------------------
// Declaration
//-----------------------------------------------------------------------------
Declaration::Declaration(const Declaration &decl) :
	_pSymbol(decl._pSymbol), _valType(decl._valType),
	_occurPattern(decl._occurPattern), _listFlag(decl._listFlag), _nomapFlag(decl._nomapFlag),
	_pExprDefault((decl._pExprDefault == NULL)? NULL : decl._pExprDefault->IncRef())
{
}

Declaration::Declaration(const Symbol *pSymbol, ValueType valType) :
	_pSymbol(pSymbol), _valType(valType),
	_occurPattern(OCCUR_Once), _listFlag(false), _nomapFlag(false),
	_pExprDefault(NULL)
{
}

Declaration::Declaration(const Symbol *pSymbol, ValueType valType,
	OccurPattern occurPattern, bool listFlag, bool nomapFlag, Expr *pExprDefault) :
	_pSymbol(pSymbol), _valType(valType),
	_occurPattern(occurPattern), _listFlag(listFlag), _nomapFlag(nomapFlag),
	_pExprDefault(pExprDefault)
{
}

Declaration::~Declaration()
{
	Expr::Delete(_pExprDefault);
}

Declaration *Declaration::Create(Environment &env, Signal sig, const Expr *pExpr)
{
	bool listFlag = false;
	bool nomapFlag = false;
	OccurPattern occurPattern = OCCUR_Once;
	ValueType valType = VTYPE_Any;
	Expr *pExprDefault = NULL;
	if (pExpr->IsDictAssign()) {
		const Expr_DictAssign *pExprDictAssign =
								dynamic_cast<const Expr_DictAssign *>(pExpr);
		pExpr = pExprDictAssign->GetLeft();
		const Expr *pExprRight = pExprDictAssign->GetRight();
		if (pExprRight->IsForce()) {
			Value value = pExprRight->Exec(env, sig);
			if (sig.IsSignalled()) return NULL;
			pExprDefault = new Expr_Value(value);
		} else {
			pExprDefault = pExprRight->IncRef();
		}
	}
	if (pExpr->IsSuffix()) {
		const Expr_Suffix *pExprSuffix = dynamic_cast<const Expr_Suffix *>(pExpr);
		pExpr = pExprSuffix->GetChild();
		occurPattern = pExprSuffix->GetOccurPattern();
		if (occurPattern == OCCUR_Invalid) {
			sig.SetError(ERR_SyntaxError, "invalid argument expression");
			return NULL;
		}
		if (pExprDefault) {
			sig.SetError(ERR_SyntaxError, "optional arguments cannot take default value");
			return NULL;
		}
	}
	if (pExpr->IsQuote()) {
		pExpr = pExpr->Unquote();
		valType = VTYPE_Quote;
	}
	if (pExpr->IsIndexer()) {
		pExpr = dynamic_cast<const Expr_Indexer *>(pExpr)->GetCar();
		listFlag = true;
	}
	if (!pExpr->IsSymbol()) {
		sig.SetError(ERR_SyntaxError, "invalid argument expression");
		return NULL;
	}
	const Expr_Symbol *pExprSym = dynamic_cast<const Expr_Symbol *>(pExpr);
	const SymbolList &attrFront = pExprSym->GetAttrFront();
	const Symbol *pSymbolForType = NULL;
	if (!attrFront.empty()) {
		if (valType != VTYPE_Any) {
			sig.SetError(ERR_SyntaxError, "incompatible type declaration");
			return NULL;
		}
		const ValueTypeInfo *pValueTypeInfo = env.LookupValueType(attrFront);
		if (pValueTypeInfo != NULL) {
			pSymbolForType = attrFront.front();
			valType = pValueTypeInfo->GetValueType();
		}
	}
	foreach_const (SymbolSet, ppSymbol, pExprSym->GetAttrs()) {
		const Symbol *pSymbol = *ppSymbol;
		if (pSymbol->IsIdentical(AScript_Symbol(nomap))) {
			nomapFlag = true;
		} else if (pSymbolForType == NULL || !pSymbol->IsIdentical(pSymbolForType)) {
			sig.SetError(ERR_SyntaxError,
				"cannot accept a symbol %s in argument declaration", pSymbol->GetName());
		}
	}
	return new Declaration(pExprSym->GetSymbol(), valType,
							occurPattern, listFlag, nomapFlag, pExprDefault);
}

// value will be casted only when that is valid for declaration.
// it will not be casted if validation fails.
bool Declaration::ValidateAndCast(Environment &env, Signal sig,
									Value &value, bool listElemFlag) const
{
	if (!listElemFlag && GetListFlag()) {
		env.LookupClass(VTYPE_List)->CastFrom(env, sig, value);
		if (sig.IsSignalled()) return false;
		if (value.IsList()) {
			foreach (ValueList, pValue, value.GetList()) {
				if (!ValidateAndCast(env, sig, *pValue, true)) {
					SetError_ArgumentType(sig, *pValue);
					return false;
				}
			}
			return true;
		} else if (IsOptional() && value.IsInvalid()) {
			return true;
		} else {
			SetError_ArgumentMustBeList(sig, value);
			return false;
		}
	}
	if ((IsOptional() && value.IsInvalid()) || GetValueType() == VTYPE_Quote) {
		return true;
	} else if (GetValueType() == VTYPE_Any || value.IsInstanceOf(GetValueType())) {
		if (value.IsIterator()) {
			// make a clone of the iterator
			Iterator *pIterator = value.CreateIterator(sig);
			if (pIterator == NULL) return false;
			value = Value(env, pIterator);
		}
		return true;
	}
	Class *pClass;
	pClass = env.LookupClass(GetValueType());
	if (pClass != NULL && pClass->CastFrom(env, sig, value)) {
		return true;
	}
	if (sig.IsSignalled()) return false;
	pClass = env.LookupClass(value.GetType());
	if (pClass != NULL && pClass->CastTo(env, sig, value, GetValueType())) {
		return true;
	}
	SetError_ArgumentType(sig, value);
	return false;
}

void Declaration::SetError_ArgumentType(Signal sig, const Value &value) const
{
	sig.SetError(ERR_TypeError, "variable '%s' cannot take %s value in '%s'",
				GetSymbol()->GetName(), value.GetTypeName(), ToString().c_str());
}

void Declaration::SetError_ArgumentMustBeList(Signal sig, const Value &value) const
{
	sig.SetError(ERR_TypeError, "variable '%s' can only take a list in '%s'",
				GetSymbol()->GetName(), ToString().c_str());
}

String Declaration::ToString() const
{
	String str;
	if (_valType == VTYPE_Quote) {
		str += "`";
	}
	str += _pSymbol->GetName();
	if (_listFlag) str += "[]";
	str += GetOccurPatternSymbol(_occurPattern)->GetName();
	if (_valType != VTYPE_Nil && _valType != VTYPE_Undefined &&
					_valType != VTYPE_Any && _valType != VTYPE_Quote) {
		str += ":";
		str += GetValueTypeSymbol(_valType)->GetName();
	}
	if (_nomapFlag) {
		str += ":";
		str += AScript_Symbol(nomap)->GetName();
	}
	if (_pExprDefault != NULL) {
		str += " ";
		str += "=>";
		str += " ";
		str += _pExprDefault->ToString();
	}
	return str;
}

//-----------------------------------------------------------------------------
// DeclarationList
//-----------------------------------------------------------------------------
const DeclarationList DeclarationList::Null;

DeclarationList::~DeclarationList()
{
}

bool DeclarationList::IsVariableLength() const
{
	foreach_const (DeclarationList, ppDecl, *this) {
		if ((*ppDecl)->IsVariableLength()) return true;
	}
	return false;
}

bool DeclarationList::ShouldImplicitMap(const ValueList &valList) const
{
	ValueList::const_iterator pValue = valList.begin();
	const_iterator ppDecl = begin();
	for ( ; pValue != valList.end() && ppDecl != end(); pValue++) {
		const Declaration *pDecl = *ppDecl;
		if (pDecl->ShouldImplicitMap(*pValue)) return true;
		if (!pDecl->IsVariableLength()) ppDecl++;
	}
	return false;
}

bool DeclarationList::ShouldImplicitMap(const Args &args) const
{
	return args.IsSelfIterable() || ShouldImplicitMap(args.GetArgs());
}

void DeclarationList::SetAsLoose()
{
	foreach (DeclarationList, ppDecl, *this) {
		Declaration *pDecl = *ppDecl;
		if (pDecl->GetOccurPattern() == OCCUR_Once) {
			pDecl->SetOccurPattern(OCCUR_ZeroOrOnce);
		} else if (pDecl->GetOccurPattern() == OCCUR_OnceOrMore) {
			pDecl->SetOccurPattern(OCCUR_ZeroOrMore);
		}
	}
}

bool DeclarationList::Compensate(Environment &env, Signal sig, ValueList &valList) const
{
	if (valList.size() >= size()) return true;
	DeclarationList::const_iterator ppDecl = begin() + valList.size();
	for ( ; ppDecl != end(); ppDecl++) {
		const Declaration *pDecl = *ppDecl;
		const Expr *pExprArg = pDecl->GetExprDefault();
		Value value;
		if (pExprArg == NULL) {
			if (pDecl->GetOccurPattern() == OCCUR_ZeroOrOnce) {
				//value = Value::Null;
				value = Value::Undefined;
			} else if (pDecl->GetOccurPattern() == OCCUR_ZeroOrMore) {
				break;
			} else {
				Declaration::SetError_NotEnoughArguments(sig);
				return false;
			}
		} else if (pDecl->IsQuote()) {
			value.InitAsExpr(env, pExprArg->IncRef());
			valList.push_back(value);
		} else if (pDecl->IsType(VTYPE_Symbol)) {
			const Expr *pExpr = pExprArg;
			if (pExpr->IsQuote()) {
				pExpr = dynamic_cast<const Expr_Quote *>(pExpr)->GetChild();
			}
			if (!pExpr->IsSymbol()) {
				sig.SetError(ERR_TypeError, "symbol is expected");
				return false;
			}
			const Symbol *pSymbol =
						dynamic_cast<const Expr_Symbol *>(pExpr)->GetSymbol();
			value = Value(pSymbol);
		} else {
			value = pExprArg->Exec(env, sig);
			if (sig.IsSignalled()) return false;
		}
		valList.push_back(value);
	}
	return true;
}

String DeclarationList::ToString() const
{
	String str;
	foreach_const (DeclarationList, ppDecl, *this) {
		if (ppDecl != begin()) str += ", ";
		str += (*ppDecl)->ToString();
	}
	return str;
}

void Declaration::SetError_InvalidArgument(Signal sig)
{
	sig.SetError(ERR_SyntaxError, "invalid argument");
}

void Declaration::SetError_NotEnoughArguments(Signal sig)
{
	sig.SetError(ERR_TypeError, "not enough arguments");
}

void Declaration::SetError_TooManyArguments(Signal sig)
{
	sig.SetError(ERR_TypeError, "too many arguments");
}

void DeclarationList::SetError_InvalidArgumentName(Signal sig, const ExprMap &exprMap)
{
	String str;
	str = "invalid argument named ";
	foreach_const (ExprMap, iter, exprMap) {
		if (iter != exprMap.begin()) str += ", ";
		str += iter->first->GetName();
	}
	sig.SetError(ERR_ValueError, "%s", str.c_str());
}

//-----------------------------------------------------------------------------
// DeclarationOwner
//-----------------------------------------------------------------------------
DeclarationOwner::DeclarationOwner(const DeclarationOwner &declOwner) :
			_pSymbolDict(declOwner._pSymbolDict),
			_allowTooManyArgsFlag(declOwner._allowTooManyArgsFlag)
{
	foreach_const (DeclarationList, ppDecl, declOwner) {
		push_back((*ppDecl)->Clone());
	}
}

DeclarationOwner::~DeclarationOwner()
{
	foreach (DeclarationOwner, ppDecl, *this) {
		Declaration::Delete(*ppDecl);
	}
}

void DeclarationOwner::operator=(const DeclarationOwner &declOwner)
{
	_pSymbolDict = declOwner._pSymbolDict;
	_allowTooManyArgsFlag = declOwner._allowTooManyArgsFlag;
	foreach_const (DeclarationList, ppDecl, declOwner) {
		push_back((*ppDecl)->Clone());
	}
}

bool DeclarationOwner::Declare(Environment &env, Signal sig, const ExprList &exprList)
{
	foreach_const (ExprList, ppExpr, exprList) {
		const Expr *pExpr = *ppExpr;
		if (pExpr->IsSuffix()) {
			const Expr_Suffix *pExprSuffix =
									dynamic_cast<const Expr_Suffix *>(pExpr);
			const Symbol *pSymbol = pExprSuffix->GetSymbol();
			if (pSymbol->IsIdentical(AScript_Symbol(Char_Modulo))) {
				const Expr *pExprChild = pExprSuffix->GetChild();
				if (!pExprChild->IsSymbol()) {
					sig.SetError(ERR_SyntaxError,
									"invalid expression for declaration");
					return false;
				}
				SetSymbolDict(
						dynamic_cast<const Expr_Symbol *>(pExprChild)->GetSymbol());
				continue;
			}
		}
		Declaration *pDecl = Declaration::Create(env, sig, pExpr);
		if (pDecl == NULL) return false;
		if (IsVariableLength()) {
			sig.SetError(ERR_TypeError,
				"any parameters cannot follow after a parameter with variable length");
			Declaration::Delete(pDecl);
			return false;
		}
		if (pDecl->IsMandatory() && pDecl->GetExprDefault() == NULL &&
											!empty() && back()->IsOptional()) {
			sig.SetError(ERR_TypeError,
				"mandatory parameters cannot follow after a parameter with variable length");
			Declaration::Delete(pDecl);
			return false;
		}
		push_back(pDecl);
	}
	return true;
}

bool DeclarationOwner::PrepareArgsForUnary(Environment &env, Signal sig,
		const ExprList &exprListArg, ValueList &valListArg, Value &valueWithDict) const
{
	Value value;
	ValueList &valList = value.InitAsList(env);
	ValueDict &valDict = valueWithDict.GetDict();
	const Declaration *pDecl = front();
	bool quoteFlag = pDecl->IsQuote();
	foreach_const (ExprList, ppExprArg, exprListArg) {
		const Expr *pExprArg = *ppExprArg;
		size_t nElems = 0;
		if (!quoteFlag && pExprArg->IsDictAssign()) {
			if (GetSymbolDict() == NULL) {
				sig.SetError(ERR_ValueError, "dictionary assignment is not effective");
				return false;
			}
			const Expr_DictAssign *pExprDictAssign =
						dynamic_cast<const Expr_DictAssign *>(pExprArg);
			Value valueKey = pExprDictAssign->GetKey(env, sig);
			if (sig.IsSignalled()) return false;
			Value value = pExprDictAssign->GetRight()->Exec(env, sig);
			if (sig.IsSignalled()) return false;
			valDict[valueKey] = value;
		} else if (!quoteFlag && IsSuffixed(pExprArg, AScript_Symbol(Char_Modulo))) {
			if (GetSymbolDict() == NULL) {
				sig.SetError(ERR_ValueError, "dictionary assignment is not effective");
				return false;
			}
			pExprArg = dynamic_cast<const Expr_Suffix *>(pExprArg)->GetChild();
			Value value = pExprArg->Exec(env, sig);
			if (sig.IsSignalled()) return false;
			if (!value.IsDict()) {
				sig.SetError(ERR_ValueError, "modulo argument must take a dictionary");
				return false;
			}
			foreach_const (ValueDict, item, value.GetDict()) {
				valDict.insert(*item);
			}
		} else if (!pExprArg->ExecInArg(env, sig, valList, nElems, quoteFlag)) {
			return false;
		}
	}
	valListArg.push_back(value);
	return true;
}

bool DeclarationOwner::PrepareArgs(Environment &env, Signal sig,
		const ExprList &exprListArg, ValueList &valListArg, Value &valueWithDict) const
{
	ExprMap exprMap;
	ValueDict &valDict = valueWithDict.GetDict();
	DeclarationList::const_iterator ppDecl = begin();
	bool stayDeclPointerFlag = false;
	ExprOwner exprsToDelete; // store temporary Exprs that are to be deleted at the end
	foreach_const (ExprList, ppExprArg, exprListArg) {
		const Expr *pExprArg = *ppExprArg;
		bool quoteFlag = ppDecl != end() && (*ppDecl)->IsQuote();
		if (!quoteFlag && pExprArg->IsDictAssign()) {
			const Expr_DictAssign *pExprDictAssign =
							dynamic_cast<const Expr_DictAssign *>(pExprArg);
			Value valueKey = pExprDictAssign->GetKey(env, sig);
			if (sig.IsSignalled()) return false;
			if (valueKey.IsSymbol()) {
				exprMap[valueKey.GetSymbol()] = pExprDictAssign->GetRight();
			} else {
				Value value = pExprDictAssign->GetRight()->Exec(env, sig);
				if (sig.IsSignalled()) return false;
				valDict[valueKey] = value;
			}
		} else if (!quoteFlag && IsSuffixed(pExprArg, AScript_Symbol(Char_Modulo))) {
			pExprArg = dynamic_cast<const Expr_Suffix *>(pExprArg)->GetChild();
			Value value = pExprArg->Exec(env, sig);
			if (sig.IsSignalled()) return false;
			if (!value.IsDict()) {
				sig.SetError(ERR_ValueError, "modulo argument must take a dictionary");
				return false;
			}
			foreach_const (ValueDict, item, value.GetDict()) {
				const Value &valueKey = item->first;
				const Value &value = item->second;
				if (valueKey.IsSymbol()) {
					Expr *pExpr;
					if (value.IsExpr()) {
						pExpr = new Expr_Quote(value.GetExpr()->IncRef());
					} else {
						pExpr = new Expr_Value(value);
					}
					exprsToDelete.push_back(pExpr);
					exprMap[valueKey.GetSymbol()] = pExpr;
				} else {
					valDict.insert(*item);
				}
			}
		} else if (ppDecl != end()) {
			const Declaration *pDecl = *ppDecl;
			if (exprMap.find(pDecl->GetSymbol()) != exprMap.end()) {
				sig.SetError(ERR_ValueError, "argument confliction");
				return false;
			}
			size_t nElems = 0;
			if (!pExprArg->ExecInArg(env, sig, valListArg, nElems, quoteFlag)) {
				return false;
			}
			if (pDecl->IsVariableLength()) {
				stayDeclPointerFlag = true;
			} else {
				ppDecl += nElems;
			}
		} else if (IsAllowTooManyArgs()) {
			break;
		} else {
			Declaration::SetError_TooManyArguments(sig);
			return false;
		}
	}
	if (stayDeclPointerFlag) ppDecl = end();
	for ( ; ppDecl != end(); ppDecl++) {
		const Declaration *pDecl = *ppDecl;
		const Expr *pExprArg = pDecl->GetExprDefault();
		ExprMap::iterator iter = exprMap.find(pDecl->GetSymbol());
		if (iter != exprMap.end()) {
			exprMap.erase(iter);
			pExprArg = iter->second;
		}
		Value value;
		if (pExprArg == NULL) {
			if (pDecl->GetOccurPattern() == OCCUR_ZeroOrOnce) {
				//value = Value::Null;
				value = Value::Undefined;
			} else if (pDecl->GetOccurPattern() == OCCUR_ZeroOrMore) {
				break;
			} else {
				Declaration::SetError_NotEnoughArguments(sig);
				return false;
			}
		} else if (pDecl->IsQuote()) {
			value.InitAsExpr(env, pExprArg->IncRef());
			valListArg.push_back(value);
		} else if (pDecl->IsType(VTYPE_Symbol)) {
			const Expr *pExpr = pExprArg;
			if (pExpr->IsQuote()) {
				pExpr = dynamic_cast<const Expr_Quote *>(pExpr)->GetChild();
			}
			if (!pExpr->IsSymbol()) {
				sig.SetError(ERR_TypeError, "symbol is expected");
				return false;
			}
			const Symbol *pSymbol =
						dynamic_cast<const Expr_Symbol *>(pExpr)->GetSymbol();
			value = Value(pSymbol);
		} else {
			value = pExprArg->Exec(env, sig);
			if (sig.IsSignalled()) return false;
		}
		valListArg.push_back(value);
	}
	if (GetSymbolDict() != NULL) {
		foreach (ExprMap, iter, exprMap) {
			const Symbol *pSymbol = iter->first;
			const Expr *pExprArg = iter->second;
			Value value = pExprArg->Exec(env, sig);
			if (sig.IsSignalled()) return false;
			valDict[Value(pSymbol)] = value;
		}
	} else if (!exprMap.empty()) {
		SetError_InvalidArgumentName(sig, exprMap);
		return false;
	}
	return true;
}

bool DeclarationOwner::ValidateAndCast(Environment &env, Signal sig,
						const ValueList &valList, ValueList &valListCasted) const
{
	ValueList::const_iterator pValue = valList.begin();
	DeclarationList::const_iterator ppDecl = begin();
	for ( ; ppDecl != end(); ppDecl++) {
		const Declaration *pDecl = *ppDecl;
		OccurPattern occurPattern = pDecl->GetOccurPattern();
		if (occurPattern == OCCUR_ZeroOrMore || occurPattern == OCCUR_OnceOrMore) {
			Value value;
			ValueList &valListElem = value.InitAsList(env);
			valListCasted.push_back(value);
			for ( ; pValue != valList.end(); pValue++) {
				Value value = *pValue;
				if (!pDecl->ValidateAndCast(env, sig, value)) return false;
				valListElem.push_back(value);
			}
			if (occurPattern == OCCUR_OnceOrMore && valListElem.empty()) {
				Declaration::SetError_NotEnoughArguments(sig);
				return false;
			}
			break;
		} else if (pValue == valList.end()) {
			if (occurPattern == OCCUR_ZeroOrOnce) {
				//valListCasted.push_back(Value::Null);
				valListCasted.push_back(Value::Undefined);
			} else {
				Declaration::SetError_NotEnoughArguments(sig);
				return false;
			}
		} else {
			Value value = *pValue;
			if (!pDecl->ValidateAndCast(env, sig, value)) return false;
			valListCasted.push_back(value);
			pValue++;
		}
	}
	if (pValue != valList.end() && !IsAllowTooManyArgs()) {
		Declaration::SetError_TooManyArguments(sig);
		return false;
	}
	return true;
}

String DeclarationOwner::ToString() const
{
	String str = DeclarationList::ToString();
	if (_pSymbolDict != NULL) {
		if (!empty()) str += ", ";
		str += _pSymbolDict->GetName();
		str += AScript_Symbol(Char_Modulo)->GetName();
	}
	return str;
}

}
