#include "AScript.h"
#include "Declaration.h"
#include "Expr.h"

namespace AScript {

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

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

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

Declaration *Declaration::Create(Environment &env, Signal sig, const Expr *pExpr)
{
	bool listFlag = false;
	OccurPattern occurPattern = OCCUR_Once;
	ValueType valType = VTYPE_AnyType;
	const Class *pClass = NULL;
	Expr *pExprDefault = NULL;
	if (pExpr->IsAssign()) {
		const Expr_Assign *pExprAssign = dynamic_cast<const Expr_Assign *>(pExpr);
		pExpr = pExprAssign->GetExprLeft();
		pExprDefault = pExprAssign->GetExprRight()->IncRef();
	}
	if (pExpr->IsSuffix()) {
		const Expr_Suffix *pExprSuffix = dynamic_cast<const Expr_Suffix *>(pExpr);
		pExpr = pExprSuffix->GetExprChild();
		occurPattern = pExprSuffix->GetOccurPattern();
		if (occurPattern == OCCUR_Invalid) {
			sig.SetError(ERR_SyntaxError, _T("invalid argument expression"));
			return NULL;
		}
		if (pExprDefault) {
			sig.SetError(ERR_SyntaxError, _T("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)->GetExprCar();
		listFlag = true;
	}
	if (!pExpr->IsSymbol()) {
		sig.SetError(ERR_SyntaxError, _T("invalid argument expression"));
		return NULL;
	}
	const Expr_Symbol *pExprSym = dynamic_cast<const Expr_Symbol *>(pExpr);
	const SymbolSet &attrs = pExprSym->GetAttrs();
	if (!attrs.empty()) {
		if (valType != VTYPE_AnyType) {
			sig.SetError(ERR_SyntaxError, _T("incompatible type declaration"));
			return NULL;
		}
		const Symbol *pSymbol = *attrs.begin();
		valType = env.LookupValueType(pSymbol, &pClass);
		if (valType == VTYPE_Invalid) {
			sig.SetError(ERR_TypeError, _T("invalid type name '%s'"), pSymbol->GetName());
			return NULL;
		}
	}
	return new Declaration(pExprSym->GetSymbol(), valType, pClass, listFlag,
								occurPattern, pExprDefault);
}

String Declaration::ToString(Signal sig) const
{
	String str;
	if (_valType == VTYPE_Quote) {
		str += _T("`");
	}
	str += _pSymbol->GetName();
	if (_listFlag) str += _T("[]");
	str += GetOccurPatternSymbol(_occurPattern)->GetName();
	if (_valType != VTYPE_Invalid &&
					_valType != VTYPE_AnyType && _valType != VTYPE_Quote) {
		str += _T(":");
		if (_pClass == NULL) {
			str += GetValueTypeSymbol(_valType)->GetName();
		} else {
			str += _pClass->GetName();
		}
	}
	if (_pExprDefault != NULL) {
		str += _T(" = ");
		str += _pExprDefault->ToString(sig);
	}
	return str;
}

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

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

bool DeclarationList::IsApplicable(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->IsApplicable(*pValue)) return false;
		if (!pDecl->IsVariableLength()) ppDecl++;
	}
	return true;
}

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);
		}
	}
}

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

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

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

}
