module dworld.plugin_loader;
import core.runtime;
import std.string;

import dworld.plugin;



// OS による動的ロード差異吸収
version (Windows)
{	// Windows
	import std.c.windows.windows;
	private void* loadLibrary(string name)				{ return Runtime.loadLibrary(name);					}
	private int   freeLibrary(void* handle)				{ return Runtime.unloadLibrary(handle);				}
	private void* getFunc(void* handle, string name)	{ return GetProcAddress(handle, toStringz(name));	}
	private string DEFAULT_LOAD_PLUGIN_METHOD = "D11plugin_main10loadPluginFZC6dworld6plugin6Plugin";
}
else
{	// POSIX
	extern (C)
	{
		immutable int RTLD_LAZY   = 0x0001;
		immutable int RTLD_NOW    = 0x0002;
		immutable int RTLD_GLOBAL = 0x0100;
		immutable int RTLD_LOCAL  = 0x0000;
		int   dlclose(void*);
		char* dlerror();
		void* dlopen(in char*, int);
		void* dlsym(void*, in char*);
	}
	private void* loadLibrary(string name)				{ return dlopen(toStringz(name), RTLD_NOW);			}
	private int   freeLibrary(void* handle)				{ return dlclose(handle);							}
	private void* getFunc(void* handle, string name)	{ return dlsym(handle, toStringz(name));			}
	private string DEFAULT_LOAD_PLUGIN_METHOD = "_D11plugin_main10loadPluginFZC6dworld6plugin6Plugin";
}


/**
 * ライブラリロードエラー.
 */
class LoadLibraryException : Exception
{
	this(string msg, string file = __FILE__, size_t size = __LINE__, Throwable next = null)
	{
		super(msg, file, line, next);
	}
}


/**
 * Plugin をロードします.
 *
 * Params:
 *	name     = ロードするプラグイン名(DLL名 or .soファイル名)
 *  funcName = ロード用関数名 (指定なしの場合, デフォルトの関数名を使用します.)
 */
Plugin loadPlugin(string name, string funcName = DEFAULT_LOAD_PLUGIN_METHOD)
{
	void* handle = loadLibrary(name);
	if (handle is null) {
		throw new LoadLibraryException("can't load library : " ~ name);
	}

	void* func = getFunc(handle, funcName);
	if (func is null)
	{
		freeLibrary(handle);
		throw new LoadLibraryException("can't load function : " ~ funcName);
	}

	Plugin function() loadPlugin = cast(Plugin function()) func;
	Plugin plugin = (*loadPlugin)();
	plugin.setHandle(handle);
	return plugin;
}


/**
 * Plugin をアンロードします.
 */
void unloadPlugin(Plugin plugin)
{
	freeLibrary(plugin.getHandle());
}


