#include "Symbol.h"
#include "Environment.h"
#include "Parser.h"
#include "Module_sys.h"
#include "Module_math.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Environment
//-----------------------------------------------------------------------------
Environment::Environment(const Environment &env) : _pFrameCache(NULL)
{
	// _pFrameCache will be initialized when the program reads some variable at first
	foreach_const (FrameList, ppFrame, env._frameList) {
		Frame *pFrame = *ppFrame;
		_frameList.push_back(pFrame->IncRef());
	}
}

Environment::Environment(Environment *pEnvOuter, EnvType envType) : _pFrameCache(NULL)
{
	// _pFrameCache will be initialized when the program reads some variable at first
	if (pEnvOuter == NULL) {
		SymbolPool::Initialize();
		Global *pGlobal = new Global();
		pGlobal->_pSymbolPool = SymbolPool::GetInstance();
		_frameList.push_back(new Frame(ENVTYPE_Root, pGlobal));
	} else if (envType == ENVTYPE_Block) {
		_frameList.push_back(new Frame(envType, pEnvOuter->GetGlobal()));
		foreach (FrameList, ppFrame, pEnvOuter->_frameList) {
			Frame *pFrame = *ppFrame;
			if (!pFrame->IsEnvType(ENVTYPE_Method)) {
				_frameList.push_back(pFrame->IncRef());
			}
		}
	} else if (envType == ENVTYPE_Outer) {
		FrameList &frameList = pEnvOuter->_frameList;
		FrameList::iterator ppFrame = frameList.begin();
		if (frameList.size() > 1) ppFrame++;
		for ( ; ppFrame != frameList.end(); ppFrame++) {
			Frame *pFrame = *ppFrame;
			_frameList.push_back(pFrame->IncRef());
		}
	} else {
		_frameList.push_back(new Frame(envType, pEnvOuter->GetGlobal()));
		foreach (FrameList, ppFrame, pEnvOuter->_frameList) {
			Frame *pFrame = *ppFrame;
			_frameList.push_back(pFrame->IncRef());
		}
	}
}

Environment::~Environment()
{
	delete _pFrameCache;
	foreach (FrameList, ppFrame, _frameList) {
		Frame::Delete(*ppFrame);
	}
}

EnvironmentRoot::~EnvironmentRoot()
{
	Global *pGlobal = GetGlobal();
	Global::Delete(pGlobal);
}

void Environment::AssignValue(const Symbol *pSymbol, const Value &value, bool escalateFlag)
{
	if (escalateFlag) {
		if (_pFrameCache != NULL) {
			FrameCache::iterator iter = _pFrameCache->find(pSymbol);
			if (iter != _pFrameCache->end()) {
				Frame *pFrame = iter->second;
				pFrame->AssignValue(pSymbol, value);
				return;
			}
		}
		foreach (FrameList, ppFrame, _frameList) {
			Frame *pFrame = *ppFrame;
			if (!pFrame->IsEnvType(ENVTYPE_Block)) {
				pFrame->AssignValue(pSymbol, value);
				break;
			}
		}
	} else {
		GetTopFrame().AssignValue(pSymbol, value);
	}
}

Value *Environment::LookupValue(const Symbol *pSymbol, bool escalateFlag)
{
	Value *pValue = NULL;
	Frame *pFrame = NULL;
	EnvType envType = GetTopFrame().GetEnvType();
	if (!escalateFlag) {// || envType == ENVTYPE_Module) {
		pFrame = &GetTopFrame();
		pValue = pFrame->LookupValue(pSymbol);
		if (pValue != NULL) goto success;
	} else if (envType == ENVTYPE_Method) {
		foreach (FrameList, ppFrame, _frameList) {
			pFrame = *ppFrame;
			if (!(pFrame->IsEnvType(ENVTYPE_Instance) ||
								pFrame->IsEnvType(ENVTYPE_Class))) {
				pValue = pFrame->LookupValue(pSymbol);
				if (pValue != NULL) goto success;
			}
		}
	} else if (envType == ENVTYPE_Instance || envType == ENVTYPE_Class) {
		foreach (FrameList, ppFrame, _frameList) {
			pFrame = *ppFrame;
			if (pFrame->IsEnvType(ENVTYPE_Instance) ||
								pFrame->IsEnvType(ENVTYPE_Class)) {
				pValue = pFrame->LookupValue(pSymbol);
				if (pValue != NULL) goto success;
			}
		}
	} else {
		foreach (FrameList, ppFrame, _frameList) {
			pFrame = *ppFrame;
			pValue = pFrame->LookupValue(pSymbol);
			if (pValue != NULL) goto success;
		}
	}
	return NULL;
success:
	if (_pFrameCache == NULL) _pFrameCache = new FrameCache();
	(*_pFrameCache)[pSymbol] = pFrame;
	return pValue;
}

//****** reference problem of FunctionCustom and Environment still remains ******
void Environment::AssignFunction(Function *pFunc)
{
	Value value;
	value.InitAsFunction(*this, pFunc);
	GetTopFrame().AssignValue(pFunc->GetSymbol(), value);
}

Function *Environment::LookupFunction(const Symbol *pSymbol, bool escalateFlag)
{
	EnvType envType = GetTopFrame().GetEnvType();
	if (!escalateFlag) {
		Frame *pFrame = &GetTopFrame();
		Value *pValue = pFrame->LookupValue(pSymbol);
		if (pValue != NULL && pValue->IsFunction()) {
			return pValue->GetFunction();
		}
	} else if (envType == ENVTYPE_Instance || envType == ENVTYPE_Class) {
		foreach (FrameList, ppFrame, _frameList) {
			Frame *pFrame = *ppFrame;
			if (pFrame->IsEnvType(ENVTYPE_Instance) ||
									pFrame->IsEnvType(ENVTYPE_Class)) {
				Value *pValue = pFrame->LookupValue(pSymbol);
				if (pValue != NULL && pValue->IsFunction()) {
					return pValue->GetFunction();
				}
			}
		}
	} else {
		foreach (FrameList, ppFrame, _frameList) {
			Frame *pFrame = *ppFrame;
			Value *pValue = pFrame->LookupValue(pSymbol);
			if (pValue != NULL && pValue->IsFunction()) {
				return pValue->GetFunction();
			}
		}
	}
	return NULL;
}

ValueType Environment::LookupValueType(const Symbol *pSymbol, const Class **ppClass)
{
	if (ppClass != NULL) *ppClass = NULL;
	ValueType valType = GetGlobal()->LookupValueType(pSymbol);
	if (valType != VTYPE_Invalid) {
		if (ppClass != NULL) {
			*ppClass = GetGlobal()->LookupClass(valType);
		}
		return valType;
	}
	Function *pFunc = LookupFunction(pSymbol, true);
	const Class *pClass;
	if (pFunc != NULL && (pClass = pFunc->GetClassToConstruct()) != NULL) {
		if (ppClass != NULL) *ppClass = pClass;
		return VTYPE_Object;
	}
	return VTYPE_Invalid;
}

void Environment::AssignModule(Module *pModule)
{
	Value value;
	value.InitAsModule(pModule);
	GetTopFrame().AssignValue(pModule->GetSymbol(), value);
}

Module *Environment::ImportModule(Signal sig,
						const Symbol *pSymbolModule, const SymbolSet &symbolSet)
{
	const Value *pValDirNameList = GetModule_sys()->LookupValue(AScript_Symbol(path), false);
	if (pValDirNameList == NULL) {
		sig.SetError(ERR_ValueError, _T("variable path is not specified"));
		return NULL;
	} else if (!pValDirNameList->IsList()) {
		sig.SetError(ERR_ValueError, _T("wrong value of variable path"));
		return NULL;
	}
	StringList extNameList;
	extNameList.push_back(_T("azd"));
	extNameList.push_back(_T("azc"));
	extNameList.push_back(_T("az"));
	bool foundFlag = false;
	String pathName;
	foreach_const (ValueList, pValue, pValDirNameList->GetList()) {
		if (!pValue->IsString()) {
			sig.SetError(ERR_ValueError, _T("wrong element value of variable path"));
			return NULL;
		}
		foreach_const (StringList, pExtName, extNameList) {
			pathName = pValue->GetString();
			pathName += File::Separator;
			pathName += pSymbolModule->GetName();
			pathName += _T('.');
			pathName += *pExtName;
			if (File::IsExist(pathName.c_str())) {
				foundFlag = true;
				break;
			}
		}
		if (foundFlag) break;
	}
	if (!foundFlag) {
		sig.SetError(ERR_IOError,
				_T("can't find a module named '%s'"), pSymbolModule->GetName());
		return NULL;
	}
	Module *pModule = GetGlobal()->LookupModule(pathName.c_str());
	if (pModule == NULL) {
		if (IsBinaryModule(pathName.c_str())) {
			pModule = ImportModule_Binary(sig, pathName.c_str());
		} else {
			pModule = ImportModule_Script(sig, pathName.c_str());
		}
		if (pModule == NULL) return NULL;
		GetGlobal()->RegisterModule(pathName.c_str(), pModule);
	}
	return pModule;
}

Module *Environment::ImportModule_Script(Signal sig, const TCHAR *pathName)
{
	File file;
	file.Open(sig, pathName, _T("rt"));
	if (sig.IsError()) return NULL;
	Module *pModule = new Module(this, AScript_Symbol(_anonymous_));
	bool echoFlagSaved = pModule->GetEchoFlag();
	pModule->SetEchoFlag(false);
	Parser().Exec(*pModule, sig, file, true);
	pModule->SetEchoFlag(echoFlagSaved);
	if (sig.IsSignalled()) {
		delete pModule;
		return NULL;
	}
	return pModule;
}

#if defined(WINVER)
Module *Environment::ImportModule_Binary(Signal sig, const TCHAR *pathName)
{
	HMODULE hModule = ::LoadLibrary(pathName);
	if (hModule == NULL) {
		sig.SetError(ERR_ImportError, _T("can't open module file '%s'"),
							File::ExtractBaseName(pathName).c_str());
		return NULL;
	}
	FARPROC pFunc = ::GetProcAddress(hModule, _T("_AScriptModuleEntry"));
	if (pFunc == NULL) {
		sig.SetError(ERR_ImportError, _T("can't find entry function '%s'"),
												_T("AScriptModuleEntry"));
		::FreeLibrary(hModule);
		return NULL;
	}
	ModuleEntryType moduleEntry = reinterpret_cast<ModuleEntryType>(pFunc);
	Module *pModule = new Module(this, AScript_Symbol(_anonymous_));
	(*moduleEntry)(*pModule, sig);
	if (sig.IsSignalled()) {
		delete pModule;
		return NULL;
	}
	return pModule;
}
#else
Module *Environment::ImportModule_Binary(Signal sig, const TCHAR *pathName)
{
	return NULL;
}
#endif

bool Environment::IsBinaryModule(const TCHAR *pathName)
{
	return ::_tcslen(pathName) >= 4 &&
			::_tcsicmp(pathName + ::_tcslen(pathName) - 4, _T(".azd")) == 0;
}

void Environment::DbgPrint() const
{
	int idx = 0;
	foreach_const (FrameList, ppFrame, _frameList) {
		idx++;
		::_tprintf(_T("frame#%d "), idx);
		(*ppFrame)->DbgPrint();
	}
}

void Environment::PutValue(Signal sig, const Value &value)
{
	File().OpenStdout().PutString(value.ToString(sig, false).c_str());
}

void Environment::PutChar(TCHAR ch)
{
	File().OpenStdout().PutChar(ch);
}

void Environment::PutString(const TCHAR *str)
{
	File().OpenStdout().PutString(str);
}

void Environment::PutPrompt(bool indentFlag)
{
	Value *pValue = GetModule_sys()->LookupValue(
						Symbol::Add(indentFlag? _T("ps2") : _T("ps1")), false);
	const TCHAR *str = (pValue == NULL || !pValue->IsString())?
												_T("") : pValue->GetString();
	PutString(str);
}

void Environment::SetupBuiltIn(Signal sig, int argc, const TCHAR *argv[])
{
	Environment &env = *this;
	GetGlobal()->Prepare(env);
	AssignValue(AScript_Symbol(nil),	Value::Null,	false);
	AssignValue(AScript_Symbol(true_),	Value(true),	false);
	AssignValue(AScript_Symbol(false_),	Value(false),	false);
	AssignValue(AScript_Symbol(rem),	Value::Null,	false);
	// built-in constructor
	AssignFunction(new Class::Constructor(env));
	//AssignFunction(new Object_Function::Constructor(env));
	AssignFunction(new Object_Function::Constructor(env, _T("lambda")));
	AssignFunction(new Object_Function::Constructor(env, _T("&")));
	AssignFunction(new Object_List::Constructor(env));
	AssignFunction(new Object_List::Constructor(env, _T("@")));
	AssignFunction(new Object_Dict::Constructor(env, _T("dict")));
	AssignFunction(new Object_Dict::Constructor(env, _T("%")));
	AssignFunction(new Object_File::Constructor(env));
	AssignFunction(new Object_File::Constructor(env, _T("open")));
	// modules
	do {
		Module *pModule = Module_sys::Import(env, sig);
		Module_sys::Setup(pModule, sig, argc, argv);
		GetGlobal()->_pModule_sys = pModule;
	} while (0);
}

bool Environment::IsModule() const { return false; }
bool Environment::IsClass() const { return false; }
bool Environment::IsObject() const { return false; }

//-----------------------------------------------------------------------------
// Environment::Global
//-----------------------------------------------------------------------------
Environment::Global::Global() : _echoFlag(false),
	_pFunc_Neg(NULL),
	_pFunc_Invert(NULL),
	_pFunc_Not(NULL),
	_pFunc_Plus(NULL),
	_pFunc_Minus(NULL),
	_pFunc_Multiply(NULL),
	_pFunc_Divide(NULL),
	_pFunc_Modulo(NULL),
	_pFunc_Power(NULL),
	_pFunc_Equal(NULL),
	_pFunc_NotEqual(NULL),
	_pFunc_Greater(NULL),
	_pFunc_Less(NULL),
	_pFunc_GreaterEq(NULL),
	_pFunc_LessEq(NULL),
	_pFunc_Compare(NULL),
	_pFunc_ContainCheck(NULL),
	_pFunc_Or(NULL),
	_pFunc_And(NULL),
	_pFunc_Xor(NULL),
	_pFunc_OrOr(NULL),
	_pFunc_AndAnd(NULL),
	_pFunc_Sequence(NULL),
	_pFunc_acos(NULL),
	_pFunc_asin(NULL),
	_pFunc_atan(NULL),
	_pFunc_atan2(NULL),
	_pFunc_ceil(NULL),
	_pFunc_cos(NULL),
	_pFunc_cosh(NULL),
	_pFunc_exp(NULL),
	_pFunc_abs(NULL),
	_pFunc_floor(NULL),
	_pFunc_log(NULL),
	_pFunc_log10(NULL),
	_pFunc_sin(NULL),
	_pFunc_sinh(NULL),
	_pFunc_sqrt(NULL),
	_pFunc_tan(NULL),
	_pFunc_tanh(NULL)
{
}

Environment::Global::~Global()
{
	foreach_const (ClassMap, iter, _classMap) {
		ObjectBase::Delete(iter->second);
	}
	foreach_const (ModuleMap, iter, _moduleMap) {
		ObjectBase::Delete(iter->second);
	}
	Function::Delete(_pFunc_Neg);
	Function::Delete(_pFunc_Invert);
	Function::Delete(_pFunc_Not);
	Function::Delete(_pFunc_Plus);
	Function::Delete(_pFunc_Minus);
	Function::Delete(_pFunc_Multiply);
	Function::Delete(_pFunc_Divide);
	Function::Delete(_pFunc_Modulo);
	Function::Delete(_pFunc_Power);
	Function::Delete(_pFunc_Equal);
	Function::Delete(_pFunc_NotEqual);
	Function::Delete(_pFunc_Greater);
	Function::Delete(_pFunc_Less);
	Function::Delete(_pFunc_GreaterEq);
	Function::Delete(_pFunc_LessEq);
	Function::Delete(_pFunc_Compare);
	Function::Delete(_pFunc_ContainCheck);
	Function::Delete(_pFunc_Or);
	Function::Delete(_pFunc_And);
	Function::Delete(_pFunc_Xor);
	Function::Delete(_pFunc_OrOr);
	Function::Delete(_pFunc_AndAnd);
	Function::Delete(_pFunc_Sequence);
	Function::Delete(_pFunc_acos);
	Function::Delete(_pFunc_asin);
	Function::Delete(_pFunc_atan);
	Function::Delete(_pFunc_atan2);
	Function::Delete(_pFunc_ceil);
	Function::Delete(_pFunc_cos);
	Function::Delete(_pFunc_cosh);
	Function::Delete(_pFunc_exp);
	Function::Delete(_pFunc_abs);
	Function::Delete(_pFunc_floor);
	Function::Delete(_pFunc_log);
	Function::Delete(_pFunc_log10);
	Function::Delete(_pFunc_sin);
	Function::Delete(_pFunc_sinh);
	Function::Delete(_pFunc_sqrt);
	Function::Delete(_pFunc_tan);
	Function::Delete(_pFunc_tanh);
}

void Environment::Global::Prepare(Environment &env)
{
	Signal sig;
	_workingDirList.push_back(File::GetWorkingDir());
	_valTypeMap[AScript_Symbol(number)]		= VTYPE_Number;
	_valTypeMap[AScript_Symbol(boolean)]	= VTYPE_Boolean;
	_valTypeMap[AScript_Symbol(symbol)]		= VTYPE_Symbol;
	_valTypeMap[AScript_Symbol(string)]		= VTYPE_String;
	_valTypeMap[AScript_Symbol(complex)]	= VTYPE_Complex;
	_valTypeMap[AScript_Symbol(Function)]	= VTYPE_Function;
	_valTypeMap[AScript_Symbol(List)]		= VTYPE_List;
	_valTypeMap[AScript_Symbol(Dict)]		= VTYPE_Dict;
	_valTypeMap[AScript_Symbol(File)]		= VTYPE_File;
	_valTypeMap[AScript_Symbol(Expr)]		= VTYPE_Expr;
	_valTypeMap[AScript_Symbol(Environment)]= VTYPE_Environment;
	_valTypeMap[AScript_Symbol(Struct)]		= VTYPE_Struct;
	// Class must be initialized at 1st
	_pClass								= new Class(&env, AScript_Symbol(Object));
	// Class_Function must be initialized at 2nd
	_pClass_Function					= new Class_Function(_pClass, AScript_Symbol(Function));
	_pClass_Function->Prepare();		// methods of Function can only be initialized here
	_pClass->Prepare();					// methods of Object can only be initialized here
	_pClass_String						= new Class_String(_pClass, AScript_Symbol(string));
	_pClass_List						= new Class_List(_pClass, AScript_Symbol(List));
	_pClass_Dict						= new Class_Dict(_pClass, AScript_Symbol(Dict));
	_pClass_File						= new Class_File(_pClass, AScript_Symbol(File));
	_pClass_FileStat					= new Class_FileStat(_pClass, AScript_Symbol(FileStat));
	_pClass_DateTime					= new Class_DateTime(_pClass, AScript_Symbol(DateTime));
	_pClass_Expr						= new Class_Expr(_pClass, AScript_Symbol(Expr));
	_pClass_Environment					= new Class_Environment(_pClass, AScript_Symbol(Environment));
	_pClass_Error						= new Class_Error(_pClass, AScript_Symbol(Error));
	_pClass_Struct						= new Class_Struct(_pClass, AScript_Symbol(Struct));
	_classMap[VTYPE_Object]				= _pClass;
	_classMap[VTYPE_Function]			= _pClass_Function;
	_classMap[VTYPE_String]				= _pClass_String;
	_classMap[VTYPE_List]				= _pClass_List;
	_classMap[VTYPE_Dict]				= _pClass_Dict;
	_classMap[VTYPE_File]				= _pClass_File;
	_classMap[VTYPE_FileStat]			= _pClass_FileStat;
	_classMap[VTYPE_DateTime]			= _pClass_DateTime;
	_classMap[VTYPE_Expr]				= _pClass_Expr;
	_classMap[VTYPE_Environment]		= _pClass_Environment;
	_classMap[VTYPE_Error]				= _pClass_Error;
	_classMap[VTYPE_Struct]				= _pClass_Struct;
	env.AssignFunction((_pFunc_Neg		= new Func_Neg(env))->IncRef());
	env.AssignFunction((_pFunc_Invert	= new Func_Invert(env))->IncRef());
	env.AssignFunction((_pFunc_Not		= new Func_Not(env))->IncRef());
	env.AssignFunction((_pFunc_Plus		= new Func_Plus(env))->IncRef());
	env.AssignFunction((_pFunc_Minus	= new Func_Minus(env))->IncRef());
	env.AssignFunction((_pFunc_Multiply	= new Func_Multiply(env))->IncRef());
	env.AssignFunction((_pFunc_Divide	= new Func_Divide(env))->IncRef());
	env.AssignFunction((_pFunc_Modulo	= new Func_Modulo(env))->IncRef());
	env.AssignFunction((_pFunc_Power	= new Func_Power(env))->IncRef());
	env.AssignFunction((_pFunc_Equal	= new Func_Equal(env))->IncRef());
	env.AssignFunction((_pFunc_NotEqual	= new Func_NotEqual(env))->IncRef());
	env.AssignFunction((_pFunc_Greater	= new Func_Greater(env))->IncRef());
	env.AssignFunction((_pFunc_Less		= new Func_Less(env))->IncRef());
	env.AssignFunction((_pFunc_GreaterEq= new Func_GreaterEq(env))->IncRef());
	env.AssignFunction((_pFunc_LessEq	= new Func_LessEq(env))->IncRef());
	env.AssignFunction((_pFunc_Compare	= new Func_Compare(env))->IncRef());
	env.AssignFunction((_pFunc_ContainCheck= new Func_ContainCheck(env))->IncRef());
	env.AssignFunction((_pFunc_Or		= new Func_Or(env))->IncRef());
	env.AssignFunction((_pFunc_And		= new Func_And(env))->IncRef());
	env.AssignFunction((_pFunc_Xor		= new Func_Xor(env))->IncRef());
	env.AssignFunction((_pFunc_OrOr		= new Func_OrOr(env))->IncRef());
	env.AssignFunction((_pFunc_AndAnd	= new Func_AndAnd(env))->IncRef());
	env.AssignFunction((_pFunc_Sequence	= new Func_Sequence(env))->IncRef());
	do {
		static const ErrorType errTypes[] = {
			ERR_SyntaxError,
			ERR_ArithmeticError,
			ERR_TypeError,
			ERR_ZeroDivisionError,
			ERR_ValueError,
			ERR_SystemError,
			ERR_IOError,
			ERR_IndexError,
			ERR_KeyError,
			ERR_ImportError,
			ERR_AttributeError,
		};
		for (int i = 0; i < NUMBEROF(errTypes); i++) {
			ErrorType errType = errTypes[i];
			Object *pObj = new Object_Error(env.GetClass_Error(), errType);
			env.AssignValue(Symbol::Add(GetErrorTypeName(errType)),
											Value(pObj, VTYPE_Error), false);
		}
	} while (0);
	do {
		//AScript::Module_math::MixIn(env, sig);
		//Environment *pEnv = &env;
		Environment *pEnv = AScript::Module_math::Import(env, sig);
		_pFunc_acos	= pEnv->LookupFunction(Symbol::Add(_T("acos")),	false)->IncRef();
		_pFunc_asin	= pEnv->LookupFunction(Symbol::Add(_T("asin")),	false)->IncRef();
		_pFunc_atan	= pEnv->LookupFunction(Symbol::Add(_T("atan")),	false)->IncRef();
		_pFunc_atan2= pEnv->LookupFunction(Symbol::Add(_T("atan2")),false)->IncRef();
		_pFunc_ceil	= pEnv->LookupFunction(Symbol::Add(_T("ceil")),	false)->IncRef();
		_pFunc_cos	= pEnv->LookupFunction(Symbol::Add(_T("cos")),	false)->IncRef();
		_pFunc_cosh	= pEnv->LookupFunction(Symbol::Add(_T("cosh")),	false)->IncRef();
		_pFunc_exp	= pEnv->LookupFunction(Symbol::Add(_T("exp")),	false)->IncRef();
		_pFunc_abs	= pEnv->LookupFunction(Symbol::Add(_T("abs")),	false)->IncRef();
		_pFunc_floor= pEnv->LookupFunction(Symbol::Add(_T("floor")),false)->IncRef();
		_pFunc_log	= pEnv->LookupFunction(Symbol::Add(_T("log")),	false)->IncRef();
		_pFunc_log10= pEnv->LookupFunction(Symbol::Add(_T("log10")),false)->IncRef();
		_pFunc_sin	= pEnv->LookupFunction(Symbol::Add(_T("sin")),	false)->IncRef();
		_pFunc_sinh	= pEnv->LookupFunction(Symbol::Add(_T("sinh")),	false)->IncRef();
		_pFunc_sqrt	= pEnv->LookupFunction(Symbol::Add(_T("sqrt")),	false)->IncRef();
		_pFunc_tan	= pEnv->LookupFunction(Symbol::Add(_T("tan")),	false)->IncRef();
		_pFunc_tanh	= pEnv->LookupFunction(Symbol::Add(_T("tanh")),	false)->IncRef();
	} while (0);
}

ValueType Environment::Global::LookupValueType(const Symbol *pSymbol) const
{
	ValueTypeMap::const_iterator iter = _valTypeMap.find(pSymbol);
	return (iter == _valTypeMap.end())? VTYPE_Invalid : iter->second;
}

const Class *Environment::Global::LookupClass(ValueType valType) const
{
	ClassMap::const_iterator iter = _classMap.find(valType);
	return (iter == _classMap.end())? NULL : iter->second;
}

Module *Environment::Global::LookupModule(const TCHAR *pathName) const
{
	ModuleMap::const_iterator iter = _moduleMap.find(pathName);
	return (iter == _moduleMap.end())? NULL : iter->second;
}

void Environment::Global::RegisterModule(const TCHAR *pathName, Module *pModule)
{
	_moduleMap[pathName] = pModule;
}

//-----------------------------------------------------------------------------
// Environment::Frame
//-----------------------------------------------------------------------------
Environment::Frame::Frame(const Frame &frame) :
			_cntRef(1), _envType(frame._envType), _pGlobal(frame._pGlobal)
{
	if (frame._pValueMap.get() != NULL) {
		_pValueMap.reset(new ValueMap(*frame._pValueMap));
	}
}

Environment::Frame::Frame(EnvType envType, Global *pGlobal) :
			_cntRef(1), _envType(envType), _pGlobal(pGlobal)
{
	DBG(::_tprintf(_T("%p Environment::Frame()\n"), this));
}

Environment::Frame::~Frame()
{
	DBG(::_tprintf(_T("%p ~Environment::Frame()\n"), this));
}

void Environment::Frame::AssignValue(const Symbol *pSymbol, const Value &value)
{
	if (_pValueMap.get() == NULL) _pValueMap.reset(new ValueMap());
	DBG(::_tprintf(_T("%p Environment::Frame::AssignValue('%s', %p)\n"),
										this, pSymbol->GetName(), &value));
	(*_pValueMap)[pSymbol] = value;
}

Value *Environment::Frame::LookupValue(const Symbol *pSymbol)
{
	if (_pValueMap.get() == NULL) return NULL;
	ValueMap::iterator iter = _pValueMap->find(pSymbol);
	return (iter == _pValueMap->end())? NULL : &iter->second;
}

void Environment::Frame::DbgPrint() const
{
	::_tprintf(_T("%p %-10s _pValueMap=%p\n"),
			this, GetEnvTypeName(GetEnvType()), _pValueMap.get());
	if (_pValueMap.get() != NULL && !_pValueMap->empty()) {
		foreach_const (ValueMap, iter, *_pValueMap) {
			::_tprintf(_T(" %s"), iter->first->GetName());
		}
		::_tprintf(_T("\n"));
	}
}

}
