#ifndef __ENVIRONMENT_H__
#define __ENVIRONMENT_H__

#include "Value.h"

namespace AScript {

class Class;
class Class_Function;
class Class_String;
class Class_List;
class Class_Dict;
class Class_File;
class Class_FileStat;
class Class_DateTime;
class Class_Expr;
class Class_Environment;
class Class_Error;
class Class_Struct;

class Module;

//-----------------------------------------------------------------------------
// ValueTypeMap
//-----------------------------------------------------------------------------
typedef std::map<const Symbol *, ValueType, Symbol::KeyCompare_UniqNumber> ValueTypeMap;

//-----------------------------------------------------------------------------
// ClassMap
//-----------------------------------------------------------------------------
typedef std::map<ValueType, Class *> ClassMap;

//-----------------------------------------------------------------------------
// ModuleMap
//-----------------------------------------------------------------------------
typedef std::map<String, Module *> ModuleMap;

//-----------------------------------------------------------------------------
// Environment
//-----------------------------------------------------------------------------
class Environment {
public:
	class Global {
	private:
		ValueTypeMap	_valTypeMap;
		ClassMap		_classMap;
		ModuleMap		_moduleMap;
		StringList		_workingDirList;
	public:
		SymbolPool		*_pSymbolPool;
		bool			_echoFlag;
		Class			*_pClass;
		Class_Function	*_pClass_Function;
		Class_String	*_pClass_String;
		Class_List		*_pClass_List;
		Class_Dict		*_pClass_Dict;
		Class_File		*_pClass_File;
		Class_FileStat	*_pClass_FileStat;
		Class_DateTime	*_pClass_DateTime;
		Class_Expr		*_pClass_Expr;
		Class_Environment *_pClass_Environment;
		Class_Error		*_pClass_Error;
		Class_Struct	*_pClass_Struct;
		Function		*_pFunc_Neg;
		Function		*_pFunc_Invert;
		Function		*_pFunc_Not;
		Function		*_pFunc_Plus;
		Function		*_pFunc_Minus;
		Function		*_pFunc_Multiply;
		Function		*_pFunc_Divide;
		Function		*_pFunc_Modulo;
		Function		*_pFunc_Power;
		Function		*_pFunc_Equal;
		Function		*_pFunc_NotEqual;
		Function		*_pFunc_Greater;
		Function		*_pFunc_Less;
		Function		*_pFunc_GreaterEq;
		Function		*_pFunc_LessEq;
		Function		*_pFunc_Compare;
		Function		*_pFunc_ContainCheck;
		Function		*_pFunc_Or;
		Function		*_pFunc_And;
		Function		*_pFunc_Xor;
		Function		*_pFunc_OrOr;
		Function		*_pFunc_AndAnd;
		Function		*_pFunc_Sequence;
		Function		*_pFunc_acos;
		Function		*_pFunc_asin;
		Function		*_pFunc_atan;
		Function		*_pFunc_atan2;
		Function		*_pFunc_ceil;
		Function		*_pFunc_cos;
		Function		*_pFunc_cosh;
		Function		*_pFunc_exp;
		Function		*_pFunc_abs;
		Function		*_pFunc_floor;
		Function		*_pFunc_log;
		Function		*_pFunc_log10;
		Function		*_pFunc_sin;
		Function		*_pFunc_sinh;
		Function		*_pFunc_sqrt;
		Function		*_pFunc_tan;
		Function		*_pFunc_tanh;
		Module			*_pModule_sys;
	public:
		Global();
		~Global();
		inline static void Delete(Global *pGlobal) {
			delete pGlobal;
		}
		void Prepare(Environment &env);
		ValueType LookupValueType(const Symbol *pSymbol) const;
		const Class *LookupClass(ValueType valType) const;
		Module *LookupModule(const TCHAR *pathName) const;
		void RegisterModule(const TCHAR *pathName, Module *pModule);
	};
	class Frame {
	private:
		int _cntRef;
		EnvType _envType;
		Global *_pGlobal;
		std::auto_ptr<ValueMap> _pValueMap;
	public:
		Frame(const Frame &frame);
		Frame(EnvType envType, Global *pGlobal);
		virtual ~Frame();
		inline Frame *IncRef() { _cntRef++; return this; }
		inline int DecRef() { _cntRef--; return _cntRef; }
		inline int GetRefCnt() const { return _cntRef; }
		inline static void Delete(Frame *pFrame) {
			if (pFrame != NULL && pFrame->DecRef() <= 0) delete pFrame;
		}
		inline Frame *Clone() const { return new Frame(*this); }
		inline EnvType GetEnvType() const { return _envType; }
		inline bool IsEnvType(EnvType envType) const { return _envType == envType; }
		inline Global *GetGlobal() { return _pGlobal; }
		inline const Global *GetGlobal() const { return _pGlobal; }
		inline const ValueMap &GetValueMap() const {
			return (_pValueMap.get() == NULL)? ValueMap::Null : *_pValueMap;
		}
		void AssignValue(const Symbol *pSymbol, const Value &value);
		Value *LookupValue(const Symbol *pSymbol);
		void DbgPrint() const;
	};
	class FrameList : public std::list<Frame *> {
	public:
		inline bool IsExist(Frame *pFrame) const {
			return std::find(begin(), end(), pFrame) != end();
		}
	};
	typedef std::map<const Symbol *, Frame *, Symbol::KeyCompare_UniqNumber> FrameCache;
private:
	FrameList _frameList;
	FrameCache *_pFrameCache;
public:
	Environment(const Environment &env);
	Environment(Environment *pEnvOuter, EnvType envType);
	virtual ~Environment();
	inline FrameList &GetFrameList()			{ return _frameList;						}
	inline const FrameList &GetFrameList() const{ return _frameList;						}
	inline Frame &GetTopFrame()					{ return *_frameList.front();				}
	inline const Frame &GetTopFrame() const		{ return *_frameList.front();				}
	inline Frame &GetBottomFrame()				{ return *_frameList.back();				}
	inline const Frame &GetBottomFrame() const	{ return *_frameList.back();				}
	inline bool IsEnvType(EnvType envType) const { return GetTopFrame().IsEnvType(envType); }
	inline Global *GetGlobal()					{ return GetTopFrame().GetGlobal();			}
	inline const Global *GetGlobal() const		{ return GetTopFrame().GetGlobal();			}
	inline Class *GetClass_Object()				{ return GetGlobal()->_pClass;				}
	inline Class_Function *GetClass_Function()	{ return GetGlobal()->_pClass_Function;		}
	inline Class_String *GetClass_String()		{ return GetGlobal()->_pClass_String;		}
	inline Class_List *GetClass_List()			{ return GetGlobal()->_pClass_List;			}
	inline Class_Dict *GetClass_Dict()			{ return GetGlobal()->_pClass_Dict;			}
	inline Class_File *GetClass_File()			{ return GetGlobal()->_pClass_File;			}
	inline Class_FileStat *GetClass_FileStat()	{ return GetGlobal()->_pClass_FileStat;		}
	inline Class_DateTime *GetClass_DateTime()	{ return GetGlobal()->_pClass_DateTime;		}
	inline Class_Expr *GetClass_Expr()			{ return GetGlobal()->_pClass_Expr;			}
	inline Class_Environment *GetClass_Environment() { return GetGlobal()->_pClass_Environment; }
	inline Class_Error *GetClass_Error()		{ return GetGlobal()->_pClass_Error;		}
	inline Class_Struct *GetClass_Struct()		{ return GetGlobal()->_pClass_Struct;		}
	inline const Function &GetFunc_Neg()		{ return *GetGlobal()->_pFunc_Neg;			}
	inline const Function &GetFunc_Invert()		{ return *GetGlobal()->_pFunc_Invert;		}
	inline const Function &GetFunc_Not()		{ return *GetGlobal()->_pFunc_Not;			}
	inline const Function &GetFunc_Plus()		{ return *GetGlobal()->_pFunc_Plus;			}
	inline const Function &GetFunc_Minus()		{ return *GetGlobal()->_pFunc_Minus;		}
	inline const Function &GetFunc_Multiply()	{ return *GetGlobal()->_pFunc_Multiply;		}
	inline const Function &GetFunc_Divide()		{ return *GetGlobal()->_pFunc_Divide;		}
	inline const Function &GetFunc_Modulo()		{ return *GetGlobal()->_pFunc_Modulo;		}
	inline const Function &GetFunc_Power()		{ return *GetGlobal()->_pFunc_Power;		}
	inline const Function &GetFunc_Equal()		{ return *GetGlobal()->_pFunc_Equal;		}
	inline const Function &GetFunc_NotEqual()	{ return *GetGlobal()->_pFunc_NotEqual;		}
	inline const Function &GetFunc_Greater()	{ return *GetGlobal()->_pFunc_Greater;		}
	inline const Function &GetFunc_Less()		{ return *GetGlobal()->_pFunc_Less;			}
	inline const Function &GetFunc_GreaterEq()	{ return *GetGlobal()->_pFunc_GreaterEq;	}
	inline const Function &GetFunc_LessEq()		{ return *GetGlobal()->_pFunc_LessEq;		}
	inline const Function &GetFunc_Compare()	{ return *GetGlobal()->_pFunc_Compare;		}
	inline const Function &GetFunc_ContainCheck() { return *GetGlobal()->_pFunc_ContainCheck;	}
	inline const Function &GetFunc_Or()			{ return *GetGlobal()->_pFunc_Or;			}
	inline const Function &GetFunc_And()		{ return *GetGlobal()->_pFunc_And;			}
	inline const Function &GetFunc_Xor()		{ return *GetGlobal()->_pFunc_Xor;			}
	inline const Function &GetFunc_OrOr()		{ return *GetGlobal()->_pFunc_OrOr;			}
	inline const Function &GetFunc_AndAnd()		{ return *GetGlobal()->_pFunc_AndAnd;		}
	inline const Function &GetFunc_Sequence()	{ return *GetGlobal()->_pFunc_Sequence;		}
	inline const Function &GetFunc_acos()		{ return *GetGlobal()->_pFunc_acos;			}
	inline const Function &GetFunc_asin()		{ return *GetGlobal()->_pFunc_asin;			}
	inline const Function &GetFunc_atan()		{ return *GetGlobal()->_pFunc_atan;			}
	inline const Function &GetFunc_atan2()		{ return *GetGlobal()->_pFunc_atan2;		}
	inline const Function &GetFunc_ceil()		{ return *GetGlobal()->_pFunc_ceil;			}
	inline const Function &GetFunc_cos()		{ return *GetGlobal()->_pFunc_cos;			}
	inline const Function &GetFunc_cosh()		{ return *GetGlobal()->_pFunc_cosh;			}
	inline const Function &GetFunc_exp()		{ return *GetGlobal()->_pFunc_exp;			}
	inline const Function &GetFunc_abs()		{ return *GetGlobal()->_pFunc_abs;			}
	inline const Function &GetFunc_floor()		{ return *GetGlobal()->_pFunc_floor;		}
	inline const Function &GetFunc_log()		{ return *GetGlobal()->_pFunc_log;			}
	inline const Function &GetFunc_log10()		{ return *GetGlobal()->_pFunc_log10;		}
	inline const Function &GetFunc_sin()		{ return *GetGlobal()->_pFunc_sin;			}
	inline const Function &GetFunc_sinh()		{ return *GetGlobal()->_pFunc_sinh;			}
	inline const Function &GetFunc_sqrt()		{ return *GetGlobal()->_pFunc_sqrt;			}
	inline const Function &GetFunc_tan()		{ return *GetGlobal()->_pFunc_tan;			}
	inline const Function &GetFunc_tanh()		{ return *GetGlobal()->_pFunc_tanh;			}
	inline Module *GetModule_sys()				{ return GetGlobal()->_pModule_sys;			}
	inline void SetEchoFlag(bool echoFlag)		{ GetGlobal()->_echoFlag = echoFlag;		}
	inline bool GetEchoFlag() const				{ return GetGlobal()->_echoFlag;			}
	void AssignValue(const Symbol *pSymbol, const Value &value, bool escalateFlag);
	void AssignFunction(Function *pFunc);
	Value *LookupValue(const Symbol *pSymbol, bool escalateFlag);
	inline const Value *LookupValue(const Symbol *pSymbol, bool escalateFlag) const {
		return const_cast<const Value *>(
				const_cast<Environment *>(this)->LookupValue(pSymbol, escalateFlag));
	}
	Function *LookupFunction(const Symbol *pSymbol, bool escalateFlag);
	ValueType LookupValueType(const Symbol *pSymbol, const Class **ppClass);
	inline const Class *LookupClass(ValueType valType) {
		return GetGlobal()->LookupClass(valType);
	}
	void AssignModule(Module *pModule);
	Module *ImportModule(Signal sig,
					const Symbol *pSymbolModule, const SymbolSet &symbolSet);
	static bool IsBinaryModule(const TCHAR *pathName);
	void SetupBuiltIn(Signal sig, int argc, const TCHAR *argv[]);
	virtual bool IsModule() const;
	virtual bool IsClass() const;
	virtual bool IsObject() const;
	inline void Error(const TCHAR *fileName, int lineNo, const TCHAR *str) {
		::_ftprintf(stderr, _T("fatal error at line.%d in %s: %s\n"), lineNo, fileName, str);
	}
	// virtual function of Console
	void DbgPrint() const;
	void PutPrompt(bool indentFlag);
	void PutChar(TCHAR ch);
	void PutString(const TCHAR *str);
	void PutValue(Signal sig, const Value &value);
private:
	Module *ImportModule_Script(Signal sig, const TCHAR *pathName);
	Module *ImportModule_Binary(Signal sig, const TCHAR *pathName);
};

class EnvironmentRoot : public Environment {
public:
	inline EnvironmentRoot() : Environment(NULL, ENVTYPE_Root) {}
	virtual ~EnvironmentRoot();
};

}

#endif
