/* Interface to external module (*Serv) symbols for database modules.
 *
 * IRC Services is copyright (c) 1996-2002 Andrew Church.
 *     E-mail: <achurch@achurch.org>
 * Parts written by Andrew Kempe and others.
 * This program is free but copyrighted software; see the file COPYING for
 * details.
 */

#include "services.h"
#include "modules.h"

#define IN_EXTSYMS_C
#include "extsyms.h"

/*************************************************************************/

static Module *module_nickserv;
static Module *module_chanserv;
static Module *module_memoserv;
static Module *module_operserv;
static Module *module_statserv;

static const char *this_module_name = "(unknown-module)";

/*************************************************************************/

static void fatal_no_symbol(const char *symbol)
{
    fatal("%s: undefined symbol `%s'", this_module_name, symbol);
}

/*************************************************************************/
/*************************************************************************/

#ifdef GCC3_HACK
#define IMPORT_FUNC(modulename, module, func)				\
static void __dblocal_##func##_stub0(void) {				\
    void *ptr = NULL;							\
    if (!module)							\
	module = find_module(modulename);				\
    if (module)								\
	ptr = get_module_symbol(module, #func);				\
    if (!ptr)								\
	fatal_no_symbol(#func);						\
    __dblocal_##func = ptr;						\
}									\
static void *__dblocal_##func##_stub(void) {				\
    __dblocal_##func##_stub0();						\
    __builtin_return(__builtin_apply((void *)__dblocal_##func,		\
		     __builtin_apply_args(), 64));			\
}									\
typeof(func) *__dblocal_##func = (void *) __dblocal_##func##_stub;
#else  /* !GCC3_HACK */
#define IMPORT_FUNC(modulename, module, func)				\
static void *__dblocal_##func##_stub(void) {				\
    void *ptr = NULL;							\
    if (!module)							\
	module = find_module(modulename);				\
    if (module)								\
	ptr = get_module_symbol(module, #func);				\
    if (!ptr)								\
	fatal_no_symbol(#func);						\
    __dblocal_##func = ptr;						\
    __builtin_return(__builtin_apply(ptr, __builtin_apply_args(), 64));	\
}									\
typeof(func) *__dblocal_##func = (void *) __dblocal_##func##_stub;
#endif  /* GCC3_HACK */


#define IMPORT_VAR(modulename, module, var)				\
static typeof(var) *__dblocal_##var##_ptr;				\
typeof(var) __dblocal_get_##var(void) {					\
    if (!__dblocal_##var##_ptr) {					\
	if (!module)							\
	    module = find_module(modulename);				\
	if (module)							\
	    __dblocal_##var##_ptr = get_module_symbol(module, #var);	\
	if (!__dblocal_##var##_ptr)					\
	    fatal_no_symbol(#var);					\
    }									\
    return *__dblocal_##var;						\
}


#define IMPORT_VAR_MAYBE(modulename, module, var, default)		\
static typeof(var) *__dblocal_##var##_ptr;				\
typeof(var) __dblocal_get_##var(void) {					\
    if (!__dblocal_##var##_ptr) {					\
	if (!module)							\
	    module = find_module(modulename);				\
	if (module)							\
	    __dblocal_##var##_ptr = get_module_symbol(module, #var);	\
    }									\
    return __dblocal_##var##_ptr ? *__dblocal_##var##_ptr : default;	\
}

/*************************************************************************/

IMPORT_VAR_MAYBE("chanserv/main", module_chanserv, CSMaxReg, CHANMAX_DEFAULT);
IMPORT_VAR_MAYBE("memoserv/main", module_memoserv, MSMaxMemos,MEMOMAX_DEFAULT);

IMPORT_FUNC("nickserv/main", module_nickserv, _get_ngi);
IMPORT_FUNC("nickserv/main", module_nickserv, _get_ngi_id);
IMPORT_FUNC("nickserv/main", module_nickserv, free_nickinfo);
IMPORT_FUNC("nickserv/main", module_nickserv, free_nickgroupinfo);
IMPORT_FUNC("nickserv/main", module_nickserv, check_expire_nick);
IMPORT_FUNC("chanserv/main", module_chanserv, free_channelinfo);
IMPORT_FUNC("chanserv/main", module_chanserv, check_expire_channel);
IMPORT_FUNC("chanserv/main", module_chanserv, reset_levels);
IMPORT_FUNC("operserv/main", module_operserv, check_expire_maskdata);
IMPORT_FUNC("statserv/main", module_statserv, new_serverstats);
IMPORT_FUNC("statserv/main", module_statserv, free_serverstats);

/*************************************************************************/
/*************************************************************************/

static int do_unload_module(Module *mod)
{
    if (mod == module_nickserv) {
	module_nickserv = NULL;
	__dblocal__get_ngi = (void *) __dblocal__get_ngi_stub;
	__dblocal__get_ngi_id = (void *) __dblocal__get_ngi_id_stub;
	__dblocal_free_nickinfo = (void *) __dblocal_free_nickinfo_stub;
	__dblocal_free_nickgroupinfo = (void *) __dblocal_free_nickgroupinfo_stub;
    } else if (mod == module_chanserv) {
	module_chanserv = NULL;
	__dblocal_CSMaxReg_ptr = NULL;
	__dblocal_free_channelinfo = (void *) __dblocal_free_channelinfo_stub;
	__dblocal_reset_levels = (void *) __dblocal_reset_levels_stub;
    } else if (mod == module_memoserv) {
	module_memoserv = NULL;
	__dblocal_MSMaxMemos_ptr = NULL;
    } else if (mod == module_statserv) {
	module_statserv = NULL;
	__dblocal_new_serverstats = (void *) __dblocal_new_serverstats_stub;
	__dblocal_free_serverstats = (void *) __dblocal_free_serverstats_stub;
    }
    return 0;
}

/*************************************************************************/

/* `name' is the name of the calling module (used for errors) */

int init_extsyms(const char *name)
{
    if (!add_callback(NULL, "unload module", do_unload_module))
	return 0;
    this_module_name = name;
    return 1;
}

/*************************************************************************/

void exit_extsyms()
{
    remove_callback(NULL, "unload module", do_unload_module);
}

/*************************************************************************/
