#pragma BLITZ_APPROVE

#define DLISTR__(s) #s
#define DLISTR_(s)  DLISTR__(s)
#define DLISTR(s)   DLISTR_(s)

#define FN(retval, fn, args)           FN_CN(retval, DLLCALL, fn, args, DLISTR(fn))
#define FN_C(retval, call, fn, args)   FN_CN(retval, call, fn, args, DLISTR(fn))
#define FN_N(retval, fn, args, name)   FN_CN(retval, DLLCALL, fn, args, name)

#define FN0(retval, fn, args)          FN0_CN(retval, DLLCALL, fn, args, DLISTR(fn))
#define FN0_C(retval, call, fn, args)  FN0_CN(retval, call, fn, args, DLISTR(fn))
#define FN0_N(retval, fn, args, name)  FN0_CN(retval, DLLCALL, fn, args, name)

#ifndef DLLTYPE
#define DLLTYPE   COMBINE(T_, DLIMODULE)
#endif

#ifndef DLLCALL
#ifdef PLATFORM_WIN32
#define DLLCALL //__cdecl
#else
#define DLLCALL
#endif
#endif

#ifndef DLLSTRUCT
#define DLLSTRUCT DLIMODULE
#endif

// ---------

#if !defined(DLI_HEADER) && !defined(DLI_SOURCE)
#define DLI_HEADER
#define DLI_SOURCE
#endif

#ifdef DLI_HEADER
#undef DLI_HEADER

#define FN_CN(retval, call, fn, args, name)\
	typedef retval (call fn##_type) args; fn##_type *fn;
#define FN0_CN FN_CN

struct DLLTYPE{
	DLLTYPE();

	UPP::String       GetLibName() const        { return libname; }
	void              SetLibName(const char *n) { if(handle) Free(); libname = n; checked = false; }

	bool              Load();
	bool              Load(const char *n)       { SetLibName(n); return Load(); }
	void              Force();
	void              Free();

	operator          bool ()      { return handle; }

#include DLIHEADER
private:
	UPP::String       libname;
	DLLHANDLE         handle;
	bool              checked;
};

#undef FN_CN
#undef FN0_CN

// ---------

DLLTYPE& DLIMODULE();
DLLTYPE& COMBINE(DLIMODULE, _)();

#endif

#ifdef DLI_SOURCE
#undef DLI_SOURCE

DLLTYPE& COMBINE(DLIMODULE, _)()
{
	static DLLTYPE out;
	return out;
}

DLLTYPE& DLIMODULE()
{
	DLLTYPE& out = COMBINE(DLIMODULE, _)();
	out.Load();
	return out;
}

DLLTYPE::DLLTYPE()
{
	checked = false;
	handle = 0;
	libname =
#ifdef DLLFILENAME
		DLLFILENAME;
#else
		DLISTR(DLLSTRUCT);
#endif
}

bool DLLTYPE::Load()
{
	if(!checked)
	{
		checked = true;

#define FN_CN(retval, call, fn, args, name)  name,
#define FN0_CN(retval, call, fn, args, name) "?" name,

		const char *name[] =
		{
		#include DLIHEADER
			0
		};

#undef FN_CN
#undef FN0_CN

// --- FnPtr map


#define FN_CN(retval, call, fn, args, name)  &fn,
#define FN0_CN FN_CN

		void *proc[] =
		{
		#include DLIHEADER
			0
		};

#undef FN_CN
#undef FN0_CN

		// --- Proc pointers


//		if(handle = LoadDll(file, name, proc))
//			atexit(&Free);
		handle = LoadDll__(libname, name, proc);
	}
	return handle;
}

void DLLTYPE::Force()
{
	if(!DLLTYPE::Load()) {
//		RLOG("Failed to load required dynamic library '" << LibName() << "'");
		exit(1);
	}
}

void DLLTYPE::Free() {
	if(handle) {
		FreeDll__(handle);
		handle = 0;
	}
	checked = false;
}
#endif

#undef FN
#undef FN_C
#undef FN_N

#undef FN0
#undef FN0_C
#undef FN0_N

#undef DLISTR__
#undef DLISTR_
#undef DLISTR

#ifdef DLLFILENAME
#undef DLLFILENAME
#endif

#ifdef DLLAUTOLOAD
#undef DLLAUTOLOAD
#endif

#undef DLLTYPE
#undef DLIMODULE
#undef DLIHEADER
#undef DLLCALL

#ifdef DLLFILENAME
#undef DLLFILENAME
#endif

#ifdef DLLAUTOLOAD
#undef DLLAUTOLOAD
#endif
