#ifndef __MODULE_H__
#define __MODULE_H__

#include "Object.h"

//-----------------------------------------------------------------------------
// Macros and functions to implement modules
//-----------------------------------------------------------------------------
#define AScript_Module(name) ModuleNS_##name

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

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

#define AScript_IncludeModuleEnd() \
} }

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

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

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

#define AScript_EndSubModule(name) \
}

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

#if defined(ASCRIPT_MODULE_SEPARATED)
#define AScript_RegisterModule(name) \
extern "C" DLLEXPORT \
void AScriptModuleEntry(AScript::Environment &env, AScript::Signal sig) \
{ \
	AScript::ModuleNS_##name::MixIn(env, sig); \
} \
extern "C" DLLEXPORT \
void AScriptModuleTerminate(AScript::Module *pModule) \
{ \
	AScript::ModuleNS_##name::Terminate(pModule); \
}
#else // ASCRIPT_MODULE_INTEGRATED
#define AScript_RegisterModule(name) \
namespace AScript { \
namespace ModuleNS_##name { \
ModuleIntegrator s_integrator(#name, MixIn, Terminate); \
} }
#endif

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

#define AScript_ModuleTerminate() \
DLLEXPORT void _Terminate(Module *pModule)

namespace AScript {

class Environment;
class Signal;

//-----------------------------------------------------------------------------
// Module
//-----------------------------------------------------------------------------
class DLLDECLARE Module : public ObjectBase {
protected:
	const Symbol *_pSymbol;
	Expr *_pExprScript;		// this is set to NULL in binary modules
	ModuleTerminateType _moduleTerminate;
private:
	Module(const Module &module);
public:
	Module(Environment *pEnvOuter, const Symbol *pSymbol,
					Expr *pExprScript, ModuleTerminateType moduleTerminate);
	virtual ~Module();
	inline Module *IncRef() { _cntRef++; return this; }
	inline const char *GetName() const { return _pSymbol->GetName(); }
	inline bool IsAnonymous() const {
		return _pSymbol->IsIdentical(AScript_Symbol(_anonymous_));
	}
	inline void SetSymbol(const Symbol *pSymbol) {
		_pSymbol = pSymbol; 
		AssignValue(AScript_Symbol(__name__), Value(*this, GetName()), false);
	}
	inline const Symbol *GetSymbol() const { return _pSymbol; }
	virtual bool IsModule() const;
	virtual Module *Clone() const;
	virtual String ToString(Signal sig, bool exprFlag);
};

//-----------------------------------------------------------------------------
// utility functions
//-----------------------------------------------------------------------------
namespace AScript_Module(math) {
DLLEXPORT Expr *CreateFuncExpr(const char *name, Expr *pExprArg);
}

}

#endif
