#include "build/generated/sources/headers/java/main/ru_dbotthepony_kstarbound_lua_LuaJNI.h"
#include "lua-5.4.4/library/lapi.h"
#include "lua-5.4.4/library/lua.h"
#include "lua-5.4.4/library/lauxlib.h"
#include <stdio.h>

static int lua_jniFunc(lua_State *state) {
	JNIEnv *env = (JNIEnv *) lua_touserdata(state, lua_upvalueindex(1));
	jmethodID callback = (jmethodID) lua_touserdata(state, lua_upvalueindex(2));
	jobject* lua_JCClosure = (jobject*) lua_touserdata(state, lua_upvalueindex(3));

	jint result = (*env)->CallIntMethod(env, *lua_JCClosure, callback, (long long) state);

	if (result <= -1) {
		const char* errMsg = lua_tostring(state, -1);
		
		if (errMsg == NULL)
			return luaL_error(state, "Internal JVM Error");

		return luaL_error(state, "%s", errMsg);
	}

	return result;
}

static int mark_closure_free(lua_State *state) {
	JNIEnv *env = (JNIEnv *) lua_touserdata(state, lua_upvalueindex(1));
	jobject* lua_JCClosure = (jobject*) lua_touserdata(state, 1);

	if (lua_JCClosure == NULL) {
		printf("Lua Glue: mark_closure_free: lua_JCClosure is NULL!\n");
		return 0;
	}

	(*env)->DeleteGlobalRef(env, *lua_JCClosure);
	return 0;
}

static JNIEXPORT void JNICALL Java_ru_dbotthepony_kstarbound_lua_LuaJNI_lua_1pushcclosure(JNIEnv *env, jclass interface, jlong luaState, jobject lua_JCClosure) {
	lua_State* LluaState = (lua_State*) luaState;
	
	jclass clazz = (*env)->GetObjectClass(env, lua_JCClosure);

	if (clazz == NULL)
		return;

	jmethodID callback = (*env)->GetMethodID(env, clazz, "invoke", "(J)I");

	if (callback == NULL)
		return;

	lua_pushlightuserdata(LluaState, env);
	lua_pushlightuserdata(LluaState, callback);
	
	void *umemory = lua_newuserdatauv(LluaState, sizeof(jobject), 0);
	jobject* rawBlock = (jobject*) umemory;
	*rawBlock = (*env)->NewGlobalRef(env, lua_JCClosure);

	int userdataIndex = lua_gettop(LluaState);

	// local table = {}
	lua_createtable(LluaState, 0, 1);
	int tableIndex = lua_gettop(LluaState);

	// function()
	lua_pushlightuserdata(LluaState, env);
	lua_pushcclosure(LluaState, mark_closure_free, 1);
	
	// table.__gc = fn
	lua_setfield(LluaState, tableIndex, "__gc");
	
	// setmetatable(userdata, table)
	lua_setmetatable(LluaState, userdataIndex);

	lua_pushcclosure(LluaState, lua_jniFunc, 3);
}