/* 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/.
 *
 * Copyright (c) 2021 Silverlan */

#ifndef __LUA_UTIL_CLASS_HPP__
#define __LUA_UTIL_CLASS_HPP__

#include "pragma/networkdefinitions.h"
#include <luasystem.h>
#include <sharedutils/util_string_literal.hpp>

namespace pragma {
	namespace lua {
		namespace detail {
			DLLNETWORK std::string tostring(const luabind::object &o);
			DLLNETWORK void register_lua_debug_tostring(lua_State *l, const std::type_info &typeInfo);
		};

		template<typename... Types>
		struct ClassWrapper {
			ClassWrapper(lua_State *l, const char *name, const std::type_info &typeInfo) : l {l}, luaClass {name}, typeInfo {typeInfo} {}
			~ClassWrapper() { detail::register_lua_debug_tostring(l, typeInfo); }
			lua_State *l;
			luabind::class_<Types...> luaClass;
			const std::type_info &typeInfo;
			luabind::class_<Types...> &operator*() { return luaClass; }
			luabind::class_<Types...> *operator->() { return &luaClass; }
		};

		template<typename... Types>
		ClassWrapper<Types...> register_class(lua_State *l, const char *name)
		{
			using FirstType = std::tuple_element_t<0, std::tuple<Types...>>;
			ClassWrapper<Types...> def {l, name, typeid(FirstType)};
			def->def(luabind::tostring(luabind::self));
			return def;
		}
		template<typename TBase, typename... Types>
		ClassWrapper<TBase, Types...> register_class(lua_State *l, const char *name, void (*tostring)(const TBase &))
		{
			ClassWrapper<TBase, Types...> def {l, name, typeid(TBase)};
			def->def("__tostring", tostring);
			return def;
		}
		template<util::StringLiteral TStr, typename... Types>
		ClassWrapper<Types...> register_class(lua_State *l, const char *name)
		{
			using FirstType = std::tuple_element_t<0, std::tuple<Types...>>;
			ClassWrapper<Types...> def = {l, name, typeid(FirstType)};
			def->def(
			  "__tostring", +[]() -> const char * { return TStr; });
			return def;
		}
		template<util::StringLiteral TStr, typename... Types>
		ClassWrapper<Types...> register_class(lua_State *l)
		{
			using FirstType = std::tuple_element_t<0, std::tuple<Types...>>;
			ClassWrapper<Types...> def {l, TStr.value, typeid(FirstType)};
			def->def(
			  "__tostring", +[]() -> const char * { return TStr.value; });
			return def;
		}
	};
};

#endif
