/**
 * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * Kamailio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * Kamailio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>

#include "../../core/sr_module.h"
#include "../../core/dprint.h"
#include "../../core/ut.h"
#include "../../core/mem/mem.h"
#include "../../core/data_lump.h"
#include "../../core/data_lump_rpl.h"
#include "../../core/strutils.h"

#include "app_mono_api.h"

#include <mono/metadata/mono-config.h>

#define SRVERSION "1.0"

/**
 *
 */
typedef struct _sr_mono_load
{
	char *script;
	MonoDomain *domain;
	MonoAssembly *assembly;
	struct _sr_mono_load *next;
} sr_mono_load_t;

static sr_mono_load_t *_sr_mono_load_list = NULL;

int sr_mono_load_class_core();
int sr_mono_load_class_pv();
int sr_mono_load_class_hdr();

/**
 *
 */
static sr_mono_env_t _sr_M_env;

/**
 * @return the static Mono env
 */
sr_mono_env_t *sr_mono_env_get(void)
{
	return &_sr_M_env;
}

/**
 *
 */
int sr_mono_load_script(char *script)
{
	sr_mono_load_t *mi;

	if(_sr_mono_load_list != NULL) {
		LM_ERR("only one assembly can be loaded\n");
		return -1;
	}
	mi = (sr_mono_load_t *)pkg_malloc(sizeof(sr_mono_load_t));
	if(mi == NULL) {
		PKG_MEM_ERROR;
		return -1;
	}
	memset(mi, 0, sizeof(sr_mono_load_t));
	mi->script = script;
	mi->next = _sr_mono_load_list;
	_sr_mono_load_list = mi;
	return 0;
}

/**
 *
 */
int sr_mono_assembly_loaded(void)
{
	if(_sr_mono_load_list != NULL)
		return 1;
	return 0;
}

/**
 *
 */
int sr_mono_register_module(char *mname)
{
	//	if(mono_sr_exp_register_mod(mname)==0)
	//		return 0;
	return -1;
}

/**
 *
 */
int mono_sr_init_mod(void)
{
	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
	//	if(mono_sr_exp_init_mod()<0)
	//		return -1;
	return 0;
}

/**
 *
 */
int mono_sr_init_load(void)
{
	sr_mono_load_t *mi;
	if(_sr_mono_load_list == NULL) {
		LM_DBG("no assembly to load\n");
		return 0;
	}
	mono_config_parse(NULL);
	mi = _sr_mono_load_list;
	if(mi && mi->domain != NULL) {
		LM_ERR("worker mono environment already initialized\n");
		return 0;
	}
	while(mi != NULL) {
		mi->domain = mono_jit_init(mi->script);
		if(mi->domain == NULL) {
			LM_ERR("failed to init domain for: %s\n", mi->script);
			return -1;
		}
		sr_mono_load_class_core();
		sr_mono_load_class_pv();
		sr_mono_load_class_hdr();

		mi->assembly = mono_domain_assembly_open(mi->domain, mi->script);
		if(mi->assembly == NULL) {
			LM_ERR("failed to open assembly: %s\n", mi->script);
			return -1;
		}
		mi = mi->next;
		/* only one (first) assembly for now */
		break;
	}
	return 0;
}

/**
 *
 */
int mono_sr_init_probe(void)
{
	/* todo: test if assemblies exist */
	return 0;
}

/**
 *
 */
int mono_sr_init_child(void)
{
	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));

	/*
	 * Load the default Mono configuration file, this is needed
	 * if you are planning on using the dllmaps defined on the
	 * system configuration
	 */
	mono_config_parse(NULL);

	return 0;
}

/**
 *
 */
void mono_sr_destroy(void)
{
	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
}

/**
 *
 */
int mono_sr_initialized(void)
{
	if(_sr_M_env.domain == NULL)
		return 1;

	return 1;
}

/**
 *
 */
int app_mono_exec(struct sip_msg *msg, char *script, char *param)
{
	int ret;
	char *argv[2];
	int argc;

	argc = 1;
	argv[0] = script;
	if(param != NULL) {
		argc++;
		argv[1] = param;
	}
	LM_DBG("executing Mono assembly: [[%s]]\n", argv[0]);
	_sr_M_env.msg = msg;

	mono_config_parse(NULL);
	/*
	 * mono_jit_init() creates a domain: each assembly is
	 * loaded and run in a MonoDomain.
	 */
	_sr_M_env.domain = mono_jit_init(argv[0]);
	/*
	 * We add our special internal functions, so that C# code
	 * can call us back.
	 */
	sr_mono_load_class_core();
	sr_mono_load_class_pv();
	sr_mono_load_class_hdr();

	_sr_M_env.assembly = mono_domain_assembly_open(_sr_M_env.domain, argv[0]);
	if(_sr_M_env.assembly == NULL) {
		ret = -1;
		goto done;
	}
	/*
	 * mono_jit_exec() will run the Main() method in the assembly.
	 * The return value needs to be looked up from
	 * System.Environment.ExitCode.
	 */
	mono_jit_exec(_sr_M_env.domain, _sr_M_env.assembly, argc, argv);
	ret = mono_environment_exitcode_get();
	LM_DBG("returned code from mono environment: %d\n", ret);

done:
	mono_jit_cleanup(_sr_M_env.domain);

	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
	return (ret == 0) ? 1 : -1;
}

/**
 *
 */
int app_mono_run(struct sip_msg *msg, char *arg)
{
	int ret;
	char *argv[2];
	int argc;
	sr_mono_load_t *mi;

	if(_sr_mono_load_list == NULL)
		return -1;
	mi = _sr_mono_load_list;

	LM_DBG("running Mono assembly: [[%s]]\n", mi->script);
	_sr_M_env.msg = msg;

	_sr_M_env.domain = mi->domain;
	_sr_M_env.assembly = mi->assembly;
	if(_sr_M_env.assembly == NULL) {
		LM_DBG("empty assembly\n");
		memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
		return -1;
	}
	mono_domain_set(_sr_M_env.domain, 0);
	argc = 1;
	argv[0] = mi->script;
	if(arg != NULL) {
		argc++;
		argv[1] = arg;
	}
	mono_jit_exec(_sr_M_env.domain, _sr_M_env.assembly, argc, argv);
	ret = mono_environment_exitcode_get();
	LM_DBG("returned code from mono environment: %d\n", ret);

	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
	return (ret == 0) ? 1 : -1;
}


/**
 *
 */
static MonoString *sr_mono_api_version()
{
	return mono_string_new(mono_domain_get(), SRVERSION);
}

static void sr_mono_log(int level, MonoString *text)
{
	char *logmsg;
	logmsg = mono_string_to_utf8(text);
	LOG(level, "%s", logmsg);
	mono_free(logmsg);
}

static void sr_mono_err(MonoString *text)
{
	char *logmsg;
	logmsg = mono_string_to_utf8(text);
	LOG(L_ERR, "%s", logmsg);
	mono_free(logmsg);
}

static void sr_mono_dbg(MonoString *text)
{
	char *logmsg;
	logmsg = mono_string_to_utf8(text);
	LOG(L_DBG, "%s", logmsg);
	mono_free(logmsg);
}

/**
 *
 */
static int sr_mono_modf(MonoString *nfunc)
{
	int ret;
	int mod_type;
	struct run_act_ctx ra_ctx;
	struct action *act = NULL;
	ksr_cmd_export_t *expf;
	sr_mono_env_t *env_M;
	char *func = NULL;

	env_M = sr_mono_env_get();
	if(env_M->msg == NULL)
		goto error;

	func = mono_string_to_utf8(nfunc);

	expf = find_export_record(func, 0, 0);
	if(expf == NULL) {
		LM_ERR("function '%s' is not available\n", func);
		goto error;
	}
	/* check fixups */
	if(expf->fixup != NULL && expf->free_fixup == NULL) {
		LM_ERR("function '%s' has fixup - cannot be used\n", func);
		goto error;
	}
	mod_type = MODULE0_T;

	act = mk_action(mod_type, 1 /* number of (type, value) pairs */, MODEXP_ST,
			expf,			 /* function */
			NUMBER_ST, 0,	 /* parameter number */
			STRING_ST, NULL, /* param. 1 */
			STRING_ST, NULL, /* param. 2 */
			STRING_ST, NULL, /* param. 3 */
			STRING_ST, NULL, /* param. 4 */
			STRING_ST, NULL, /* param. 5 */
			STRING_ST, NULL	 /* param. 6 */
	);

	if(act == NULL) {
		LM_ERR("action structure could not be created for '%s'\n", func);
		goto error;
	}

	/* handle fixups */
	if(expf->fixup) {
		/* no parameters */
		if(expf->fixup(0, 0) < 0) {
			LM_ERR("Error in fixup (0) for '%s'\n", func);
			goto error;
		}
	}
	init_run_actions_ctx(&ra_ctx);
	ret = do_action(&ra_ctx, act, env_M->msg);
	pkg_free(act);
	mono_free(func);
	return ret;

error:
	if(func != NULL)
		mono_free(func);
	if(act != NULL)
		pkg_free(act);
	return -127;
}


static const sr_M_export_t _sr_M_export_core[] = {
		{"SR.Core::APIVersion", sr_mono_api_version},
		{"SR.Core::Log", sr_mono_log}, {"SR.Core::Err", sr_mono_err},
		{"SR.Core::Dbg", sr_mono_dbg}, {"SR.Core::ModF", sr_mono_modf},
		{NULL, NULL}};

int sr_mono_load_class_core()
{
	int i;
	for(i = 0; _sr_M_export_core[i].name != NULL; i++)
		mono_add_internal_call(
				_sr_M_export_core[i].name, _sr_M_export_core[i].method);
	return 0;
}

/**
 *
 */
static MonoString *sr_mono_pv_gets(MonoString *pv)
{
	str pvn = {0};
	pv_spec_t *pvs;
	pv_value_t val;
	sr_mono_env_t *env_M;
	int pl;

	env_M = sr_mono_env_get();

	pvn.s = mono_string_to_utf8(pv);
	if(pvn.s == NULL || env_M->msg == NULL)
		goto error;

	pvn.len = strlen(pvn.s);
	LM_DBG("pv get: %s\n", pvn.s);
	pl = pv_locate_name(&pvn);
	if(pl != pvn.len) {
		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
		goto error;
	}
	pvs = pv_cache_get(&pvn);
	if(pvs == NULL) {
		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
		goto error;
	}
	memset(&val, 0, sizeof(pv_value_t));
	if(pv_get_spec_value(env_M->msg, pvs, &val) != 0) {
		LM_ERR("unable to get pv value for [%s]\n", pvn.s);
		goto error;
	}
	if((val.flags & PV_VAL_NULL) || !(val.flags & PV_VAL_STR)) {
		mono_free(pvn.s);
		return NULL;
	}
	mono_free(pvn.s);
	return mono_string_new_len(mono_domain_get(), val.rs.s, val.rs.len);

error:
	if(pvn.s != NULL)
		mono_free(pvn.s);
	return NULL;
}

/**
 *
 */
static int sr_mono_pv_geti(MonoString *pv)
{
	str pvn = {0};
	pv_spec_t *pvs;
	pv_value_t val;
	int pl;
	sr_mono_env_t *env_M;

	env_M = sr_mono_env_get();
	pvn.s = mono_string_to_utf8(pv);

	if(pvn.s == NULL || env_M->msg == NULL)
		goto error;

	pvn.len = strlen(pvn.s);
	LM_DBG("pv get: %s\n", pvn.s);
	pl = pv_locate_name(&pvn);
	if(pl != pvn.len) {
		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
		goto error;
	}
	pvs = pv_cache_get(&pvn);
	if(pvs == NULL) {
		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
		goto error;
	}
	memset(&val, 0, sizeof(pv_value_t));
	if(pv_get_spec_value(env_M->msg, pvs, &val) != 0) {
		LM_ERR("unable to get pv value for [%s]\n", pvn.s);
		goto error;
	}
	if((val.flags & PV_VAL_NULL) || !(val.flags & PV_TYPE_INT)) {
		mono_free(pvn.s);
		return 0;
	}
	mono_free(pvn.s);
	return val.ri;

error:
	if(pvn.s != NULL)
		mono_free(pvn.s);
	return 0;
}


/**
 *
 */
static int sr_mono_pv_seti(MonoString *pv, int iv)
{
	str pvn = {0};
	pv_spec_t *pvs;
	pv_value_t val;
	int pl;
	sr_mono_env_t *env_M;

	env_M = sr_mono_env_get();
	pvn.s = mono_string_to_utf8(pv);

	if(pvn.s == NULL || env_M->msg == NULL)
		goto error;

	memset(&val, 0, sizeof(pv_value_t));
	val.ri = iv;
	val.flags |= PV_TYPE_INT | PV_VAL_INT;

	pvn.len = strlen(pvn.s);
	LM_DBG("pv set: %s\n", pvn.s);
	pl = pv_locate_name(&pvn);
	if(pl != pvn.len) {
		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
		goto error;
	}
	pvs = pv_cache_get(&pvn);
	if(pvs == NULL) {
		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
		goto error;
	}
	if(pv_set_spec_value(env_M->msg, pvs, 0, &val) < 0) {
		LM_ERR("unable to set pv [%s]\n", pvn.s);
		goto error;
	}

	mono_free(pvn.s);
	return 0;
error:
	if(pvn.s != NULL)
		mono_free(pvn.s);
	return -1;
}

/**
 *
 */
static int sr_mono_pv_sets(MonoString *pv, MonoString *sv)
{
	str pvn = {0};
	pv_spec_t *pvs;
	pv_value_t val;
	int pl;
	sr_mono_env_t *env_M;

	env_M = sr_mono_env_get();
	pvn.s = mono_string_to_utf8(pv);

	if(pvn.s == NULL || env_M->msg == NULL)
		goto error;

	memset(&val, 0, sizeof(pv_value_t));
	val.rs.s = mono_string_to_utf8(sv);
	if(val.rs.s == NULL)
		goto error;

	val.rs.len = strlen(val.rs.s);
	val.flags |= PV_VAL_STR;

	pvn.len = strlen(pvn.s);
	LM_DBG("pv set: %s\n", pvn.s);
	pl = pv_locate_name(&pvn);
	if(pl != pvn.len) {
		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
		goto error;
	}
	pvs = pv_cache_get(&pvn);
	if(pvs == NULL) {
		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
		goto error;
	}
	if(pv_set_spec_value(env_M->msg, pvs, 0, &val) < 0) {
		LM_ERR("unable to set pv [%s]\n", pvn.s);
		return -1;
	}

	mono_free(pvn.s);
	return 0;
error:
	if(pvn.s != NULL)
		mono_free(pvn.s);
	return -1;
}

/**
 *
 */
static int sr_mono_pv_unset(MonoString *pv)
{
	str pvn = {0};
	pv_spec_t *pvs;
	pv_value_t val;
	int pl;
	sr_mono_env_t *env_M;

	env_M = sr_mono_env_get();
	pvn.s = mono_string_to_utf8(pv);

	if(pvn.s == NULL || env_M->msg == NULL)
		goto error;

	pvn.len = strlen(pvn.s);
	LM_DBG("pv unset: %s\n", pvn.s);
	pl = pv_locate_name(&pvn);
	if(pl != pvn.len) {
		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
		goto error;
	}
	pvs = pv_cache_get(&pvn);
	if(pvs == NULL) {
		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
		goto error;
	}
	memset(&val, 0, sizeof(pv_value_t));
	val.flags |= PV_VAL_NULL;
	if(pv_set_spec_value(env_M->msg, pvs, 0, &val) < 0) {
		LM_ERR("unable to unset pv [%s]\n", pvn.s);
		goto error;
	}

	mono_free(pvn.s);
	return 0;
error:
	if(pvn.s != NULL)
		mono_free(pvn.s);
	return -1;
}

/**
 *
 */
static int sr_mono_pv_is_null(MonoString *pv)
{
	str pvn = {0};
	pv_spec_t *pvs;
	pv_value_t val;
	int pl;
	sr_mono_env_t *env_M;

	env_M = sr_mono_env_get();
	pvn.s = mono_string_to_utf8(pv);

	if(pvn.s == NULL || env_M->msg == NULL)
		goto error;

	pvn.len = strlen(pvn.s);
	LM_DBG("pv is null test: %s\n", pvn.s);
	pl = pv_locate_name(&pvn);
	if(pl != pvn.len) {
		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
		goto error;
	}
	pvs = pv_cache_get(&pvn);
	if(pvs == NULL) {
		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
		goto error;
	}
	memset(&val, 0, sizeof(pv_value_t));
	if(pv_get_spec_value(env_M->msg, pvs, &val) != 0) {
		LM_INFO("unable to get pv value for [%s]\n", pvn.s);
		goto error;
	}
	mono_free(pvn.s);
	if(val.flags & PV_VAL_NULL) {
		return 1;
	}
	return 0;
error:
	if(pvn.s != NULL)
		mono_free(pvn.s);
	return -1;
}

/**
 *
 */
static const sr_M_export_t _sr_M_export_pv[] = {
		{"SR.PV::GetS", sr_mono_pv_gets}, {"SR.PV::GetI", sr_mono_pv_geti},
		{"SR.PV::SetI", sr_mono_pv_seti}, {"SR.PV::SetS", sr_mono_pv_sets},
		{"SR.PV::Unset", sr_mono_pv_unset},
		{"SR.PV::IsNull", sr_mono_pv_is_null}, {NULL, NULL}};

int sr_mono_load_class_pv()
{
	int i;
	for(i = 0; _sr_M_export_pv[i].name != NULL; i++)
		mono_add_internal_call(
				_sr_M_export_pv[i].name, _sr_M_export_pv[i].method);
	return 0;
}


/**
 *
 */
static int sr_mono_hdr_append(MonoString *hv)
{
	struct lump *anchor;
	struct hdr_field *hf;
	char *hdr;
	str txt = {0};
	sr_mono_env_t *env_M;

	env_M = sr_mono_env_get();
	txt.s = mono_string_to_utf8(hv);

	if(txt.s == NULL || env_M->msg == NULL)
		goto error;

	txt.len = strlen(txt.s);

	LM_DBG("append hf: %s\n", txt.s);
	if(parse_headers(env_M->msg, HDR_EOH_F, 0) == -1) {
		LM_ERR("error while parsing message\n");
		goto error;
	}

	hf = env_M->msg->last_header;
	hdr = (char *)pkg_malloc(txt.len + 1);
	if(hdr == NULL) {
		PKG_MEM_ERROR;
		goto error;
	}
	memcpy(hdr, txt.s, txt.len);
	hdr[txt.len] = '\0';
	anchor = anchor_lump(
			env_M->msg, hf->name.s + hf->len - env_M->msg->buf, 0, 0);
	if((anchor == NULL)
			|| (insert_new_lump_before(anchor, hdr, txt.len, 0) == 0)) {
		LM_ERR("can't insert lump\n");
		pkg_free(hdr);
		goto error;
	}
	mono_free(txt.s);
	return 0;

error:
	if(txt.s != NULL)
		mono_free(txt.s);
	return -1;
}

/**
 *
 */
static int sr_mono_hdr_remove(MonoString *hv)
{
	struct lump *anchor;
	struct hdr_field *hf;
	str hname;
	str txt = {0};
	sr_mono_env_t *env_M;

	env_M = sr_mono_env_get();
	txt.s = mono_string_to_utf8(hv);

	if(txt.s == NULL || env_M->msg == NULL)
		goto error;

	txt.len = strlen(txt.s);

	LM_DBG("remove hf: %s\n", txt.s);
	if(parse_headers(env_M->msg, HDR_EOH_F, 0) == -1) {
		LM_ERR("error while parsing message\n");
		goto error;
	}

	hname.s = txt.s;
	hname.len = txt.len;
	for(hf = env_M->msg->headers; hf; hf = hf->next) {
		if(cmp_hdrname_str(&hf->name, &hname) == 0) {
			anchor = del_lump(
					env_M->msg, hf->name.s - env_M->msg->buf, hf->len, 0);
			if(anchor == 0) {
				LM_ERR("cannot remove hdr %s\n", txt.s);
				goto error;
			}
		}
	}
	mono_free(txt.s);
	return 0;

error:
	if(txt.s != NULL)
		mono_free(txt.s);
	return -1;
}

/**
 *
 */
static int sr_mono_hdr_insert(MonoString *hv)
{
	struct lump *anchor;
	struct hdr_field *hf;
	char *hdr;
	str txt = {0};
	sr_mono_env_t *env_M;

	env_M = sr_mono_env_get();
	txt.s = mono_string_to_utf8(hv);

	if(txt.s == NULL || env_M->msg == NULL)
		goto error;

	txt.len = strlen(txt.s);

	LM_DBG("insert hf: %s\n", txt.s);
	hf = env_M->msg->headers;
	hdr = (char *)pkg_malloc(txt.len + 1);
	if(hdr == NULL) {
		PKG_MEM_ERROR;
		goto error;
	}
	memcpy(hdr, txt.s, txt.len);
	hdr[txt.len] = '\0';
	anchor = anchor_lump(
			env_M->msg, hf->name.s + hf->len - env_M->msg->buf, 0, 0);
	if((anchor == NULL)
			|| (insert_new_lump_before(anchor, hdr, txt.len, 0) == 0)) {
		LM_ERR("can't insert lump\n");
		pkg_free(hdr);
		goto error;
	}
	mono_free(txt.s);
	return 0;

error:
	if(txt.s != NULL)
		mono_free(txt.s);
	return -1;
}

/**
 *
 */
static int sr_mono_hdr_append_to_reply(MonoString *hv)
{
	str txt = {0};
	sr_mono_env_t *env_M;

	env_M = sr_mono_env_get();
	txt.s = mono_string_to_utf8(hv);

	if(txt.s == NULL || env_M->msg == NULL)
		goto error;

	txt.len = strlen(txt.s);

	LM_DBG("append to reply: %s\n", txt.s);

	if(add_lump_rpl(env_M->msg, txt.s, txt.len, LUMP_RPL_HDR) == 0) {
		LM_ERR("unable to add reply lump\n");
		goto error;
	}

	mono_free(txt.s);
	return 0;

error:
	if(txt.s != NULL)
		mono_free(txt.s);
	return -1;
}


/**
 *
 */
static const sr_M_export_t _sr_M_export_hdr[] = {
		{"SR.HDR::Append", sr_mono_hdr_append},
		{"SR.HDR::Remove", sr_mono_hdr_remove},
		{"SR.HDR::Insert", sr_mono_hdr_insert},
		{"SR.HDR::AppendToReply", sr_mono_hdr_append_to_reply}, {NULL, NULL}};

int sr_mono_load_class_hdr()
{
	int i;
	for(i = 0; _sr_M_export_hdr[i].name != NULL; i++)
		mono_add_internal_call(
				_sr_M_export_hdr[i].name, _sr_M_export_hdr[i].method);
	return 0;
}
