/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

%begin %{
#ifdef __GNUC__
	/* GCC 4.7 has a problem with some code generated by SWIG and trigger
	 * a warning. We need to disable it in order to compile properly. */
	#pragma GCC diagnostic ignored "-Warray-bounds"
	#pragma GCC diagnostic ignored "-Wreturn-type"
#endif

#ifdef __clang__
	/* Clang raise some warning on the swig generated code */
	#pragma GCC diagnostic ignored "-Wself-assign"
#endif
%}

%{
	#include <haka/error.h>
	#include <haka/macro.h>
	#include <haka/thread.h>
	#include <haka/lua/luautils.h>
	#include <haka/lua/state.h>

	#include <stdlib.h>

	#undef SWIG_fail
	#undef SWIG_fail_arg
	#undef SWIG_check_num_args

	#define SWIG_fail {SWIG_arg=-1;goto fail;}
	#define SWIG_fail_arg(func_name,argnum,type) \
	  {lua_pushfstring(L,"Error in %s (arg %d), expected '%s' got '%s'",\
	  func_name,argnum,type,SWIG_Lua_typename(L,argnum));\
	  SWIG_arg=-1;goto fail;}
	#define SWIG_check_num_args(func_name,a,b) \
	  if (lua_gettop(L)<a || lua_gettop(L)>b) \
	  {lua_pushfstring(L,"Error in %s expected %d..%d args, got %d",func_name,a,b,lua_gettop(L));\
	  SWIG_arg=-1;goto fail;}
	#define SWIG_execfail {SWIG_arg=-2;goto fail;}

	#define SWIG_lua_always(L, a) 1

	#define STR_TO_WCS_(str)   L##str
	#define STR_TO_WCS(str)    STR_TO_WCS_(str)

	/* We need to override SWIG to avoid overriding a struct metatable. We let user dynamically
	 * add Lua function in this metatable. This is going to be lost if it gets overwritten.
	 */
	SWIGINTERN void  SWIG_Lua_class_register_custom(lua_State* L,swig_lua_class* clss)
	{
		/* Check if the metatable is already registered */
		SWIG_Lua_get_class_registry(L);
		lua_getfield(L, -1, clss->name);
		if (!lua_isnil(L, -1)) {
			lua_pop(L, 2);
			return;
		}
		lua_pop(L, 2);

		SWIG_Lua_class_register(L, clss);
	}

	#define SWIG_Lua_class_register SWIG_Lua_class_register_custom

	/* Fix the thread safety of module loading.
	 *
	 * The goal is to wrap the function created by SWIG named SWIG_init in
	 * a lock. To do this we use a trick to redefine the macro SWIG_init to
	 * force our function to be called instead of the default one.
	 *
	 * SWIG_init and SWIG_init_user is a marco defined by SWIG which uses the
	 * module name. The real function SWIG_init is defined at the end of the
	 * generated file.
	 */
	#define SWIG_custom_init CONCAT(SWIG_init_user, _init)

	static mutex_t luaopen_lock = MUTEX_INIT;

	#ifdef __cplusplus
	extern "C" {
	#endif

	#if ((SWIG_LUA_TARGET == SWIG_LUA_FLAVOR_ELUA) || (SWIG_LUA_TARGET == SWIG_LUA_FLAVOR_ELUAC))
	LUALIB_API int SWIG_custom_init(lua_State* L);
	#else
	SWIGEXPORT int SWIG_custom_init(lua_State* L);
	#endif

	#if ((SWIG_LUA_TARGET == SWIG_LUA_FLAVOR_ELUA) || (SWIG_LUA_TARGET == SWIG_LUA_FLAVOR_ELUAC))
	LUALIB_API int SWIG_init(lua_State* L)
	#else
	SWIGEXPORT int SWIG_init(lua_State* L) /* default Lua action */
	#endif
	{
		#undef SWIG_init
		#define SWIG_init SWIG_custom_init

		mutex_lock(&luaopen_lock);
		SWIG_init(L);
		mutex_unlock(&luaopen_lock);
	}

	#ifdef __cplusplus
	}
	#endif
%}


%insert("init") %{
	if (luaL_loadbuffer(L, SWIG_LUACODE, strlen(SWIG_LUACODE), SWIG_name)) {
		luaL_error(L, lua_tostring(L, -1));
		lua_pop(L, 1);
		return;
	}

	lua_pushvalue(L, -2);
	if (lua_pcall(L, 1, 0, 0)) {
		luaL_error(L, lua_tostring(L, -1));
		lua_pop(L, 1);
		return;
	}

	/* Avoid the default swig behavior to be used */
	#define SWIG_Lua_dostring(...)
%}

%exception %{
	const char *error;
	$action
	if ((error = clear_error())) {
		lua_pushstring(L, error);
		SWIG_execfail;
	}
%}

%typecheck(20000) void *ANY {
	$1 = 1;
}

%typemap(in) void *ANY {
	$1 = (void *)lua_topointer(L, $input);
}

%typecheck(20000) char *KEY {
	$1 = 1;
}

%typemap(in) char *KEY {
	size_t len;
	$1 = (char *)lua_converttostring(L, $input, &len);
}

%define STRUCT_UNKNOWN_KEY_ERROR(type)

%extend type {
	void __getitem(char *KEY);
	void __setitem(char *KEY, void *ANY);
}

%{
void type ## ___getitem(struct type *self, char *KEY) {
	error("unknown field '%s'", KEY);
}

void type ## ___setitem(struct type *self, char *KEY, void *ANY) {
	error("unknown field '%s'", KEY);
}
%}

%enddef

%include "typemaps.i"

%typemap(in, numinputs=0) const char **OUTPUT (char *temp)
%{ $1 = &temp; %}

%typemap(argout) const char **OUTPUT
%{ lua_pushstring(L, *$1); SWIG_arg++;%}

%typemap(in, numinputs=0) char **TEMP_OUTPUT (char *temp)
%{ temp = NULL; $1 = &temp; %}

%typemap(argout) char **TEMP_OUTPUT
%{ lua_pushstring(L, *$1); SWIG_arg++; free(*$1); %}

%typemap(in, numinputs=0) (char **TEMP_OUTPUT, size_t *TEMP_SIZE) (char *temp, size_t temp_size)
%{ temp = NULL; temp_size = -1; $1 = &temp; $2 = &temp_size; %}

%typemap(argout) (char **TEMP_OUTPUT, size_t *TEMP_SIZE)
%{
	if (*$2 == (size_t)-1)
		lua_pushstring(L, *$1);
	else
		lua_pushlstring(L, *$1, *$2);

	SWIG_arg++;
	free(*$1);
%}

%typemap(in,checkfn="SWIG_lua_isnilstring",fragment="SWIG_lua_isnilstring") (const char *STRING, size_t SIZE)
%{
	$1 = ($ltype)lua_tostring(L, $input);
	$2 = lua_rawlen(L, $input);
%}

%typemap(in, numinputs=0) bool *YIELD (bool temp=false)
%{ $1 = &temp; %}

%typemap(argout) bool *YIELD
%{
	if (*$1) {
		lua_pop(L, SWIG_arg);
		return -1;
	}
%}

%define APPLY_OUTPUT(type)

	%apply type *OUTPUT { type *OUTPUT1 };
	%apply type *OUTPUT { type *OUTPUT2 };
	%apply type *OUTPUT { type *OUTPUT3 };
	%apply type *OUTPUT { type *OUTPUT4 };
	%apply type *OUTPUT { type *OUTPUT5 };

%enddef

APPLY_OUTPUT(bool)
APPLY_OUTPUT(int)
APPLY_OUTPUT(const char *)
APPLY_OUTPUT(SWIGTYPE *)
