#ifndef __MODULE_H__
#define __MODULE_H__

#include "Object.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Module
//-----------------------------------------------------------------------------
class Module : public ObjectBase {
protected:
	const Symbol *_pSymbol;
private:
	inline Module(const Module &module) :
					ObjectBase(module), _pSymbol(module._pSymbol) {}
public:
	inline Module(Environment *pEnvOuter, const Symbol *pSymbol) :
					ObjectBase(pEnvOuter, ENVTYPE_Module), _pSymbol(pSymbol) {}
	virtual ~Module();
	inline Module *IncRef() { _cntRef++; return this; }
	inline const TCHAR *GetName() const { return _pSymbol->GetName(); }
	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; }
	virtual bool IsModule() const;
	virtual Module *Clone() const;
	virtual String ToString(Signal sig, bool exprFlag);
};

void InitModule(Environment &env);

}

//-----------------------------------------------------------------------------
// Macros and functions to implement modules
//-----------------------------------------------------------------------------
namespace AScript {
class Environment;
class Signal;
}

typedef void (*ModuleEntryType)(AScript::Environment &env, AScript::Signal sig);

#define AScript_BeginModuleHeader(name) \
namespace AScript {														\
namespace Module_##name {												\
void MixIn(Environment &env, Signal sig);								\
Module *Import(Environment &env, Signal sig);

#define AScript_EndModuleHeader(name) \
} }

#define AScript_BeginModule(name) \
namespace AScript { \
namespace Module_##name {

#define AScript_EndModule(name) \
Module *Import(Environment &env, Signal sig)					\
{																\
	Module *pModule = new Module(&env, Symbol::Add(_T(#name)));	\
	MixIn(*pModule, sig);										\
	if (sig.IsSignalled()) {									\
		delete pModule;											\
		return NULL;											\
	}															\
	env.AssignModule(pModule);									\
	return pModule;												\
}																\
}}

#define AScript_BeginSubModule(name) \
namespace SubModule_##name {

#define AScript_EndSubModule(name) \
}

#define AScript_AssignSubModule(name) \
do {																	\
	Module *pModule = new Module(&env, Symbol::Add(_T(#name)));			\
	SubModule_##name::MixIn(*pModule, sig);								\
	if (sig.IsSignalled()) {											\
		delete pModule;													\
		return;															\
	}																	\
	env.AssignModule(pModule);											\
} while (0)

#if defined(ASCRIPT_DLL_MODULE)
#define AScript_DLLModuleEntry(name) \
extern "C" __declspec(dllexport)										\
void AScriptModuleEntry(AScript::Environment &env, AScript::Signal sig)	\
{																		\
	AScript::InitModule(env);											\
	AScript::Module_##name::MixIn(env, sig);							\
}
#else
#define AScript_DLLModuleEntry(name)
#endif

#define AScript_DeclarePrivSymbol(name) \
static const Symbol *_pPrivSymbol_##name = NULL;

#define AScript_RealizePrivSymbol(name) \
_pPrivSymbol_##name = Symbol::Add(#name);

#define AScript_RealizePrivSymbolEx(name, str) \
_pPrivSymbol_##name = Symbol::Add(str);

#define AScript_PrivSymbol(name) (_pPrivSymbol_##name)

#define AScript_DeclareFunction(name) \
class Func_##name : public Function {									\
public:																	\
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const; \
	Func_##name(Environment &env, const TCHAR *name = _T(#name));		\
};																		\
Func_##name::Func_##name(Environment &env, const TCHAR *name) :			\
									Function(name, FUNCTYPE_Function)

#define AScript_DeclareMethod(className, name) \
class Func_##className##__##name : public Function {					\
public:																	\
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const; \
	Func_##className##__##name(Environment &env, const TCHAR *name = _T(#name)); \
};																		\
Func_##className##__##name::Func_##className##__##name(Environment &env, const TCHAR *name) : \
									Function(name, FUNCTYPE_Instance)

#define AScript_ImplementFunction(name) \
Value Func_##name::DoEval(Environment &env, Signal sig, Context &context) const

#define AScript_ImplementMethod(className, name) \
Value Func_##className##__##name::DoEval(Environment &env, Signal sig, Context &context) const

#define AScript_Function(name) Func_##name
#define AScript_Method(className, name) Func_##className##__##name

#define AScript_DeclareFunctionBegin(name) \
class Func_##name : public Function {

#define AScript_DeclareFunctionEnd(name) \
public:																	\
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const; \
	Func_##name(Environment &env, const TCHAR *name = _T(#name));		\
};																		\
Func_##name::Func_##name(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)

#define AScript_DeclareDiff() \
virtual Expr *Diff(Environment &env, Signal sig, \
			const ExprList &exprArgs, const Symbol *pSymbolVar) const

#define AScript_ImplementDiff(name) \
Expr *Func_##name::Diff(Environment &env, Signal sig, \
			const ExprList &exprArgs, const Symbol *pSymbolVar) const

#define AScript_ModuleEntry() \
void MixIn(Environment &env, Signal sig)

#define AScript_AssignFunction(name) \
env.AssignFunction(new Func_##name(env))

#define AScript_AssignFunctionEx(name, nameAlias) \
env.AssignFunction(new Func_##name(env, nameAlias))

#define AScript_AssignMethod(className, name) \
AssignFunction(new Func_##className##__##name(*this))

#define AScript_AssignMethodEx(className, name, nameAlias) \
AssignFunction(new Func_##className##__##name(*this, nameAlias))

#define AScript_AssignValue(symbol, value) \
env.AssignValue(symbol, value, false)

#define AScript_DeclareClass(name) \
class Class_##name : public Class {												\
public:																			\
	static Class_##name *_pInst;												\
private:																		\
	Class_##name##(Class *pClassSuper, const Symbol *pSymbol);					\
public:																			\
	static Class_##name *GetInstance(Environment &env);							\
};																				\
Class_##name *Class_##name##::_pInst = NULL;									\
Class_##name *Class_##name##::GetInstance(Environment &env)						\
{																				\
	if (_pInst == NULL) {														\
		_pInst = new Class_##name##(env.GetClass_Object(), Symbol::Add(_T(#name))); \
	}																			\
	return _pInst;																\
}

#define AScript_ImplementClass(name) \
Class_##name::Class_##name(Class *pClassSuper, const Symbol *pSymbol) : Class(pClassSuper, pSymbol)

#define AScript_Class(name, env) \
Class_##name::GetInstance(env)

#define AScript_BlockSignalHandlerInLoop(sig, result, returnOnSignal) \
if ((sig).IsBreak()) {					\
	result = (sig).GetValue();			\
	(sig).ClearSignal();				\
	break;								\
} else if ((sig).IsReturn()) {			\
	break;								\
} else if ((sig).IsContinue()) {		\
	result = (sig).GetValue();			\
	(sig).ClearSignal();				\
} else if ((sig).IsSignalled()) {		\
	return returnOnSignal;				\
}

#endif
