package ru.dbotthepony.kstarbound.lua

import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import org.apache.logging.log4j.LogManager
import org.classdump.luna.ByteString
import org.classdump.luna.exec.CallPausedException
import org.classdump.luna.runtime.LuaFunction

class LuaMessageHandler(val lua: LuaEnvironment) {
	private val handlers = HashMap<String, LuaFunction<*, *, *, *, *>>()

	init {
		val table = lua.newTable()

		table["setHandler"] = luaFunction { name: ByteString, handler: LuaFunction<*, *, *, *, *>? ->
			if (handler == null) {
				handlers.remove(name.decode())
			} else {
				handlers[name.decode()] = handler
			}
		}

		lua.globals["message"] = table
	}

	fun handle(message: String, isLocal: Boolean, parameters: JsonArray): JsonElement {
		if (lua.errorState)
			return JsonNull.INSTANCE

		val handler = handlers[message] ?: return JsonNull.INSTANCE

		try {
			val unpacked = arrayOfNulls<Any>(parameters.size() + 2)

			unpacked[0] = ByteString.of(message)
			unpacked[1] = isLocal

			for ((i, v) in parameters.withIndex()) {
				unpacked[i + 2] = lua.from(v)
			}

			val result = lua.executor.call(lua, handler, *unpacked)

			if (result.isEmpty()) {
				return JsonNull.INSTANCE
			} else if (result.size == 1) {
				return toJsonFromLua(result[0])
			} else {
				val array = JsonArray()

				for (v in result) {
					array.add(toJsonFromLua(v))
				}

				return array
			}
		} catch (err: CallPausedException) {
			lua.markErrored()
			LOGGER.error("Message handler for $message tried to yield", err)
			return JsonNull.INSTANCE
		} catch (err: Throwable) {
			lua.markErrored()
			LOGGER.error("Message handler for $message errored", err)
			return JsonNull.INSTANCE
		}
	}

	companion object {
		private val LOGGER = LogManager.getLogger()
	}
}