#ifndef __FUNCTION_H__
#define __FUNCTION_H__

#include "Common.h"
#include "Declaration.h"
#include "Environment.h"

namespace AScript {

class ExprList;
class Expr_Block;
class Class_Custom;
class FunctionCustom;
class Object_Dict;

//-----------------------------------------------------------------------------
// Context
//-----------------------------------------------------------------------------
class ContextBase {
private:
	const SymbolSet &_attrs;
	const SymbolSet &_attrsOpt;
	const Expr_Block *_pExprBlock;
	Function *_pFuncBlock;
	const Function **_ppFuncSuccRequester;
	Value _valueSelf;
	ResultMode _rsltMode;
public:
	inline ContextBase(const SymbolSet &attrs, const SymbolSet &attrsOpt,
				const Expr_Block *pExprBlock, const Function **ppFuncSuccRequester,
				const Value &valueSelf, ResultMode rsltMode = RSLTMODE_Normal);
	inline ContextBase(ContextBase &context);
	inline ContextBase(ContextBase &context, ResultMode rsltMode);
	virtual ~ContextBase();
	inline bool IsSet(const Symbol *pSymbol) const {
		return _attrs.IsSet(pSymbol);
	}
	inline bool IsAttrEmpty() const { return _attrs.empty(); }
	inline const SymbolSet &GetAttrs() const { return _attrs; }
	inline const SymbolSet &GetAttrsOpt() const { return _attrsOpt; }
	inline const Expr_Block *GetBlock() const { return _pExprBlock; }
	inline Value &GetSelf() { return _valueSelf; }
	inline Class *GetSelfClass() { return _valueSelf.GetClass(); }
	inline Object *GetSelfObj() { return _valueSelf.GetObject(); }
	inline bool IsBlockSpecified() const { return _pExprBlock != NULL; }
	inline bool IsResNormal() const { return _rsltMode == RSLTMODE_Normal; }
	inline bool IsResList() const { return _rsltMode == RSLTMODE_List; }
	inline bool IsResXList() const { return _rsltMode == RSLTMODE_XList; }
	inline bool IsResSet() const { return _rsltMode == RSLTMODE_Set; }
	inline bool IsResXSet() const { return _rsltMode == RSLTMODE_XSet; }
	inline bool IsResVoid() const { return _rsltMode == RSLTMODE_Void; }
	inline void RequestSucceeding(const Function *pFuncSuccRequester) {
		if (_ppFuncSuccRequester != NULL) *_ppFuncSuccRequester = pFuncSuccRequester;
	}
	inline void SetFuncBlock(Function *pFunc) { _pFuncBlock = pFunc; }
	inline const Function *GetFuncBlock() const { return _pFuncBlock; }
};

class ContextExpr : public ContextBase {
private:
	const ExprList &_exprListArg;
public:
	inline ContextExpr(const SymbolSet &attrs, const SymbolSet &attrsOpt,
				const Expr_Block *pExprBlock, const Function **ppFuncSuccRequester,
				const Value &valueSelf, const ExprList &exprListArg) :
			ContextBase(attrs, attrsOpt, pExprBlock, ppFuncSuccRequester, valueSelf),
			_exprListArg(exprListArg) {}
	inline ContextExpr(const ExprList &exprListArg) :
			ContextBase(SymbolSet::Null, SymbolSet::Null, NULL, NULL, Value::Null),
			_exprListArg(exprListArg) {}
	inline const ExprList &GetArgs() const { return _exprListArg; }
};

class Context : public ContextBase {
private:
	const ValueList &_valListArg;
	const Value &_valueWithDict;
public:
	inline Context(const SymbolSet &attrs, const SymbolSet &attrsOpt,
				const Expr_Block *pExprBlock, const Function **ppFuncSuccRequester,
				const Value &valueSelf, const ValueList &valListArg,
				const Value &valueWithDict = Value::Null) :
			ContextBase(attrs, attrsOpt, pExprBlock, ppFuncSuccRequester, valueSelf),
			_valListArg(valListArg), _valueWithDict(valueWithDict) {}
	inline Context(const ValueList &valListArg, const Value &valueWithDict = Value::Null) :
			ContextBase(SymbolSet::Null, SymbolSet::Null, NULL, NULL, Value::Null),
			_valListArg(valListArg), _valueWithDict(valueWithDict) {}
	inline Context(ContextBase &contextBase, const ValueList &valListArg,
									const Value &valueWithDict) :
			ContextBase(contextBase),
			_valListArg(valListArg), _valueWithDict(valueWithDict) {}
	inline Context(ContextBase &contextBase, ResultMode rsltMode,
				const ValueList &valListArg, const Value &valueWithDict) :
			ContextBase(contextBase, rsltMode),
			_valListArg(valListArg), _valueWithDict(valueWithDict) {}
	inline size_t CountArgs() const { return _valListArg.size(); }
	inline const ValueList &GetArgs() const { return _valListArg; }
	inline Value GetValue(size_t idxArg) {
		return (idxArg < _valListArg.size())? _valListArg[idxArg] : Value::Null;
	}
	inline const Value &GetValue(size_t idxArg) const {
		return (idxArg < _valListArg.size())? _valListArg[idxArg] : Value::Null;
	}
	inline bool IsValid(size_t idxArg) const			{ return GetValue(idxArg).IsValid();	}
	inline bool IsInvalid(size_t idxArg) const			{ return GetValue(idxArg).IsInvalid();	}
	inline bool IsNumber(size_t idxArg) const			{ return GetValue(idxArg).IsNumber();	}
	inline bool IsBoolean(size_t idxArg) const			{ return GetValue(idxArg).IsBoolean();	}
	inline bool IsSymbol(size_t idxArg) const			{ return GetValue(idxArg).IsSymbol();	}
	inline bool IsString(size_t idxArg) const			{ return GetValue(idxArg).IsString();	}
	inline bool IsComplex(size_t idxArg) const			{ return GetValue(idxArg).IsComplex();	}
	inline bool IsModule(size_t idxArg) const			{ return GetValue(idxArg).IsModule();	}
	inline bool IsClass(size_t idxArg) const			{ return GetValue(idxArg).IsClass();	}
	inline bool IsGenericObject(size_t idxArg) const	{ return GetValue(idxArg).IsGenericObject(); }
	inline bool IsFunction(size_t idxArg) const			{ return GetValue(idxArg).IsFunction();	}
	inline bool IsList(size_t idxArg) const				{ return GetValue(idxArg).IsList();		}
	inline bool IsDict(size_t idxArg) const				{ return GetValue(idxArg).IsDict();		}
	inline bool IsFile(size_t idxArg) const				{ return GetValue(idxArg).IsFile();		}
	inline bool IsExpr(size_t idxArg) const				{ return GetValue(idxArg).IsExpr();		}
	inline bool IsError(size_t idxArg) const			{ return GetValue(idxArg).IsError();	}
	inline bool IsExprOrSymbol(size_t idxArg) const		{ return GetValue(idxArg).IsExprOrSymbol();	}
	inline bool IsNumberOrComplex(size_t idxArg) const	{ return GetValue(idxArg).IsNumberOrComplex(); }
	inline Number GetNumber(size_t idxArg) const		{ return GetValue(idxArg).GetNumber();	}
	inline long GetLong(size_t idxArg) const			{ return GetValue(idxArg).GetLong();	}
	inline long GetULong(size_t idxArg) const			{ return GetValue(idxArg).GetULong();	}
	inline bool GetBoolean(size_t idxArg) const			{ return GetValue(idxArg).GetBoolean();	}
	inline const Symbol *GetSymbol(size_t idxArg) const	{ return GetValue(idxArg).GetSymbol();	}
	inline const TCHAR *GetString(size_t idxArg) const	{ return GetValue(idxArg).GetString();	}
	inline const Object_String *GetStringObj(size_t idxArg) const	{ return GetValue(idxArg).GetStringObj();	}
	inline Complex GetComplex(size_t idxArg) const		{ return GetValue(idxArg).GetComplex();	}
	inline const ValueList &GetList(size_t idxArg) const{ return GetValue(idxArg).GetList();	}
	inline File &GetFile(size_t idxArg)					{ return GetValue(idxArg).GetFile();	}
	inline File &GetFile(size_t idxArg) const			{
		return const_cast<Context *>(this)->GetValue(idxArg).GetFile();
	}
	inline const Expr *GetExpr(size_t idxArg) const		{ return GetValue(idxArg).GetExpr();	}
	inline const Function *GetFunction(size_t idxArg) const	{ return GetValue(idxArg).GetFunction(); }
	inline ErrorType GetErrorType(size_t idxArg) const	{ return GetValue(idxArg).GetErrorType(); }
	inline const Value &GetValueWithDict() const		{ return _valueWithDict;				}
	inline const ValueDict &GetDict() const				{
		return _valueWithDict.IsDict()? _valueWithDict.GetDict() : ValueDict::Null;
	}
};

//-----------------------------------------------------------------------------
// Function
//-----------------------------------------------------------------------------
class Function {
public:
	class ResultListComposer {
	private:
		Environment &_env;
		ContextBase &_contextBase;
		Value &_result;
		ValueList *_pValList;
		size_t _cnt;
		bool _excludeNilFlag;
		bool _setFlag;
	public:
		ResultListComposer(Environment &env, ContextBase &contextBase, Value &result);
		void Store(const Value &value);
	};
	typedef std::map<const Symbol *, const Expr *, Symbol::KeyCompare_UniqNumber> ExprMap;
protected:
	int _cntRef;
	const Symbol *_pSymbol;
	const Symbol *_pSymbolDict;
	DeclarationOwner _declOwner;
	String _mathSymbol;
	FunctionType _funcType;
	ResultMode _rsltMode;
	MapFlag _mapFlag;
	SymbolSet _attrsOpt;
	bool _dynamicScopeFlag;
	bool _allowTooManyArgsFlag;
	struct {
		OccurPattern occurPattern;
		bool insideScopeFlag;
		const Symbol *pSymbol;
	} _blockInfo;
public:
	Function(const Function &func);
	Function(const Symbol *pSymbol, FunctionType funcType);
	Function(const TCHAR *name, FunctionType funcType);
	virtual ~Function();
	inline Function *IncRef() { _cntRef++; return this; }
	inline int DecRef() { _cntRef--; return _cntRef; }
	inline int GetRefCnt() const { return _cntRef; }
	inline static void Delete(Function *pFunc) {
		if (pFunc != NULL && pFunc->DecRef() <= 0) delete pFunc;
	}
	inline void SetMathSymbol(const TCHAR *mathSymbol) { _mathSymbol = mathSymbol; }
	inline bool IsAnonymous() const { return _pSymbol->IsIdentical(AScript_Symbol(_anonymous_)); }
	inline void SetSymbol(const Symbol *pSymbol) { _pSymbol = pSymbol; }
	inline const Symbol *GetSymbol() const { return _pSymbol; }
	inline const TCHAR *GetName() const { return _pSymbol->GetName(); }
	inline const TCHAR *GetMathSymbol() const { return _mathSymbol.c_str(); }
	virtual bool IsCustom() const;
	virtual bool IsNeg() const;
	virtual bool IsPlus() const;
	virtual bool IsMinus() const;
	virtual bool IsMultiply() const;
	virtual bool IsDivide() const;
	virtual bool IsModulo() const;
	virtual bool IsPower() const;
	virtual bool IsContainCheck() const;
	virtual Class *GetClassToConstruct() const;
	Value EvalExpr(Environment &env, Signal sig, ContextExpr &contextExpr) const;
	Value Eval(Environment &env, Signal sig, Context &context) const;
	Value EvalMulti(Environment &env, Signal sig, const ValueList &valListArg) const;
	Value EvalMap(Environment &env, Signal sig, Context &context) const;
	virtual Expr *Diff(Environment &env, Signal sig,
					const ExprList &exprArgs, const Symbol *pSymbolVar) const;
	virtual Expr *Optimize(Environment &env, Signal sig, ExprList &exprArgs) const;
	virtual bool IsSucceedable(const Object *pObj) const;
	inline FunctionType GetFuncType() const { return _funcType; }
	inline void SetMode(ResultMode rsltMode, MapFlag mapFlag = MAP_Off) {
		_rsltMode = rsltMode, _mapFlag = mapFlag;
	}
	inline MapFlag GetMapFlag() const { return _mapFlag; }
	bool CustomDeclare(Environment &env, Signal sig,
			const SymbolSet &attrsAcceptable, ContextExpr &contextExpr);
	bool InitAsBlockFunc(Environment &env, Signal sig, const Expr_Block *pExprBlock);
	Declaration *DeclareArg(Environment &env, const TCHAR *name, ValueType valType,
			bool listFlag = false, OccurPattern occurPattern = OCCUR_Once,
			Expr *pExprDefault = NULL);
	inline void DeclareDictArg(const TCHAR *name) { _pSymbolDict = Symbol::Add(name); }
	inline void DeclareAttr(const Symbol *pSymbol) { _attrsOpt.Insert(pSymbol); }
	inline DeclarationList &GetDeclList() { return _declOwner; }
	inline const DeclarationList &GetDeclList() const { return _declOwner; }
	inline bool IsUnary() const {
		return _declOwner.size() == 1 && !_declOwner.front()->IsVariableLength();
	}
	inline bool IsUnaryable() const { return _declOwner.size() == 1; }
	inline bool IsApplicable(const ValueList &valListArg) const {
		return _declOwner.IsApplicable(valListArg);
	}
	inline void AllowTooManyArgs(bool flag) { _allowTooManyArgsFlag = flag; }
	void DeclareBlock(OccurPattern occurPattern,
					const Symbol *pSymbol = NULL, bool insideScopeFlag = false);
	void AssignArgs(Environment &env, Signal sig, const ValueList &valList) const;
	String ToString(Signal sig) const;
	void SetError_DivideByZero(Signal sig) const;
	void SetError_DiffUnavailable(Signal sig) const;
	void SetError_InvalidArgumentName(Signal sig, const ExprMap &exprMap) const;
	void SetError_UnsupportedAttr(Signal sig, const SymbolSet &attrs) const;
	void SetError_NotConstructor(Signal sig) const;
	void SetError_NotEnoughArguments(Signal sig) const;
	void SetError_TooManyArguments(Signal sig) const;
	void SetError_ArgumentType(Signal sig,
					const Declaration *pDecl, const Value &value) const;
	void SetError_ArgumentTypeByIndex(Signal sig,
					size_t idxArg, const Value &value) const;
	void SetError_InvalidValue(Signal sig, const Value &value) const;
	void SetError_InvalidValue(Signal sig, const Value &value1, const Value &value2) const;
	void SetError_InvalidValType(Signal sig, const Value &value) const;
	void SetError_InvalidValType(Signal sig, const Value &value1, const Value &value2) const;
	void SetError_InvalidValTypeM(Signal sig, const Value &value1, const Value &value2) const;
	void SetError_ArgumentMustBeList(Signal sig,
					const Declaration *pDecl, const Value &value) const;
	void SetError_InvalidFunctionExpression(Signal sig) const;
	const Function *GetBlockFunction(Environment &env, Signal sig,
												ContextBase &contextBase) const;
protected:
	static Expr *DiffHelper(Environment &env, Signal sig, Expr *pExprGen,
					const Expr *pExprChild, const Symbol *pSymbolVar);
	bool PrepareArgsForUnary(Environment &env, Signal sig, const ExprList &exprArgs,
					ValueList &valListArg, Value &valueWithDict) const;
	bool PrepareArgs(Environment &env, Signal sig, const ExprList &exprArgs,
					ValueList &valListArg, Value &valueWithDict) const;
	bool ValidateArgs(Environment &env, Signal sig,
					const ValueList &valList, ValueList &valListCasted) const;
	bool ValidateArg(Environment &env, Signal sig,
					const Declaration *pDecl, Value &value, bool listElemFlag) const;
	Value EvalOverride(Signal sig, Context &context, bool &evaluatedFlag) const;
private:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const = 0;
};

//-----------------------------------------------------------------------------
// FunctionCustom
//-----------------------------------------------------------------------------
class FunctionCustom : public Function {
private:
	Environment _envScope;
	Expr *_pExprBody;
	Class_Custom *_pClassToConstruct; // this may be NULL
public:
	FunctionCustom(const FunctionCustom &func, Environment &envScope);
	FunctionCustom(Environment &envScope, const Symbol *pSymbol, Expr *pExpr,
					Class_Custom *pClassToConstruct, FunctionType funcType);
	virtual ~FunctionCustom();
	inline const Expr *GetExprBody() const { return _pExprBody; }
	virtual bool IsCustom() const;
	void SetClassToConstruct(Class_Custom *pClassToConstruct);
	virtual Class *GetClassToConstruct() const;
	virtual Expr *Diff(Environment &env, Signal sig,
				const ExprList &exprArgs, const Symbol *pSymbolVar) const;
private:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
};

//-----------------------------------------------------------------------------
// ConstructorBase
//-----------------------------------------------------------------------------
class ConstructorBase : public Function {
protected:
	Class *_pClassToConstruct;
public:
	inline ConstructorBase(const Symbol *pSymbol, Class *pClass) :
			Function(pSymbol, FUNCTYPE_Function), _pClassToConstruct(pClass) {}
	inline ConstructorBase(const TCHAR *name, Class *pClass) :
			Function(name, FUNCTYPE_Function), _pClassToConstruct(pClass) {}
	virtual Class *GetClassToConstruct() const;
};

}

#endif
