<commit_before>#include "engine/engine.h"
#include "engine/blob.h"
#include "engine/crc32.h"
#include "engine/debug/debug.h"
#include "engine/fs/disk_file_device.h"
#include "engine/fs/file_system.h"
#include "engine/fs/memory_file_device.h"
#include "engine/fs/os_file.h"
#include "engine/fs/resource_file_device.h"
#include "engine/input_system.h"
#include "engine/iplugin.h"
#include "engine/lifo_allocator.h"
#include "engine/log.h"
#include "engine/lua_wrapper.h"
#include "engine/math_utils.h"
#include "engine/mtjd/manager.h"
#include "engine/path.h"
#include "engine/plugin_manager.h"
#include "engine/prefab.h"
#include "engine/profiler.h"
#include "engine/property_descriptor.h"
#include "engine/property_register.h"
#include "engine/resource_manager.h"
#include "engine/timer.h"
#include "engine/universe/component.h"
#include "engine/universe/universe.h"
#include <imgui/imgui.h>


namespace Lumix
{

namespace LuaImGui
{


int InputTextMultiline(lua_State* L)
{
	char buf[4096];
	auto* name = LuaWrapper::checkArg<const char*>(L, 1);
	auto* value = LuaWrapper::checkArg<const char*>(L, 2);
	copyString(buf, value);
	bool changed = ImGui::InputTextMultiline(name, buf, lengthOf(buf), ImVec2(-1, -1));
	lua_pushboolean(L, changed);
	if (changed)
	{
		lua_pushstring(L, buf);
	}
	else
	{
		lua_pushvalue(L, 2);
	}
	return 2;
}


int DragFloat(lua_State* L)
{
	auto* name = LuaWrapper::checkArg<const char*>(L, 1);
	float value = LuaWrapper::checkArg<float>(L, 2);
	bool changed = ImGui::DragFloat(name, &value);
	lua_pushboolean(L, changed);
	lua_pushnumber(L, value);
	return 2;
}


int PushStyleVar(lua_State* L)
{
	int var = LuaWrapper::checkArg<int>(L, 1);
	if (lua_gettop(L) > 2)
	{
		ImVec2 v;
		v.x = LuaWrapper::checkArg<float>(L, 2);
		v.y = LuaWrapper::checkArg<float>(L, 3);
		ImGui::PushStyleVar(var, v);
	}
	else
	{
		float v = LuaWrapper::checkArg<float>(L, 2);
		ImGui::PushStyleVar(var, v);
	}
	return 0;
}


int PushID(lua_State* L)
{
	int id = LuaWrapper::checkArg<int>(L, 1);
	ImGui::PushID(id);
	return 0;
}


int SetStyleColor(lua_State* L)
{
	auto& style = ImGui::GetStyle();
	int index = LuaWrapper::checkArg<int>(L, 1);
	ImVec4 color;
	color.x = LuaWrapper::checkArg<float>(L, 2);
	color.y = LuaWrapper::checkArg<float>(L, 3);
	color.z = LuaWrapper::checkArg<float>(L, 4);
	color.w = LuaWrapper::checkArg<float>(L, 5);
	style.Colors[index] = color;
	return 0;
}


int SliderFloat(lua_State* L)
{
	auto* name = LuaWrapper::checkArg<const char*>(L, 1);
	float value = LuaWrapper::checkArg<float>(L, 2);
	float min = LuaWrapper::checkArg<float>(L, 3);
	float max = LuaWrapper::checkArg<float>(L, 4);
	bool changed = ImGui::SliderFloat(name, &value, min, max, "");
	lua_pushboolean(L, changed);
	lua_pushnumber(L, value);
	return 2;
}


int Text(lua_State* L)
{
	auto* text = LuaWrapper::checkArg<const char*>(L, 1);
	ImGui::Text("%s", text);
	return 0;
}


int LabelText(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	auto* text = LuaWrapper::checkArg<const char*>(L, 2);
	ImGui::LabelText(label, "%s", text);
	return 0;
}


int Button(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	ImVec2 size(0, 0);
	if (lua_gettop(L) > 2)
	{
		size.x = LuaWrapper::checkArg<float>(L, 2);
		size.y = LuaWrapper::checkArg<float>(L, 3);
	}
	bool clicked = ImGui::Button(label, size);
	lua_pushboolean(L, clicked);
	return 1;
}


int CollapsingHeader(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	lua_pushboolean(L, ImGui::CollapsingHeader(label));
	return 1;
}


int Checkbox(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	bool b = LuaWrapper::checkArg<bool>(L, 2);
	bool clicked = ImGui::Checkbox(label, &b);
	lua_pushboolean(L, clicked);
	lua_pushboolean(L, b);
	return 2;
}


int GetWindowPos(lua_State* L)
{
	ImVec2 pos = ImGui::GetWindowPos();
	LuaWrapper::push(L, Vec2(pos.x, pos.y));
	return 1;
}


int SetNextWindowPos(lua_State* L)
{
	ImVec2 pos;
	pos.x = LuaWrapper::checkArg<float>(L, 1);
	pos.y = LuaWrapper::checkArg<float>(L, 2);
	ImGui::SetNextWindowPos(pos);
	return 0;
}


int AlignFirstTextHeightToWidgets(lua_State* L)
{
	ImGui::AlignFirstTextHeightToWidgets();
	return 0;
}


int Selectable(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	bool selected = false;
	if (lua_gettop(L) > 1)
	{
		selected = LuaWrapper::checkArg<bool>(L, 2);
	}
	bool clicked = ImGui::Selectable(label, selected);
	lua_pushboolean(L, clicked);
	return 1;
}


int SetCursorScreenPos(lua_State* L)
{
	ImVec2 pos;
	pos.x = LuaWrapper::checkArg<float>(L, 1);
	pos.y = LuaWrapper::checkArg<float>(L, 2);
	ImGui::SetCursorScreenPos(pos);
	return 0;
}


int Separator(lua_State* L)
{
	ImGui::Separator();
	return 0;
}


void Rect(float w, float h, u32 color)
{
	ImGui::Rect(w, h, color);
}


void Dummy(float w, float h)
{
	ImGui::Dummy({w, h});
}


void Image(void* texture_id, float w, float h)
{
	ImGui::Image(texture_id, ImVec2(w, h));
}


bool IsItemHovered()
{
	return ImGui::IsItemHovered();
}


bool IsMouseDown(int button)
{
	return ImGui::IsMouseDown(button);
}


bool IsMouseClicked(int button)
{
	return ImGui::IsMouseClicked(button);
}


int SetNextWindowPosCenter(lua_State* L)
{
	ImGui::SetNextWindowPosCenter();
	return 0;
}


int SetNextWindowSize(float w, float h)
{
	ImGui::SetNextWindowSize(ImVec2(w, h));
	return 0;
}


int Begin(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	ImGuiWindowFlags flags = 0;
	if (lua_gettop(L) > 1)
	{
		flags = LuaWrapper::checkArg<int>(L, 2);
	}
	bool res = ImGui::Begin(label, nullptr, flags);
	lua_pushboolean(L, res);
	return 1;
}


int BeginChildFrame(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	ImVec2 size(0, 0);
	if (lua_gettop(L) > 1)
	{
		size.x = LuaWrapper::checkArg<float>(L, 2);
		size.y = LuaWrapper::checkArg<float>(L, 3);
	}
	bool res = ImGui::BeginChildFrame(ImGui::GetID(label), size);
	lua_pushboolean(L, res);
	return 1;
}


int BeginDock(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	bool res = ImGui::BeginDock(label);
	lua_pushboolean(L, res);
	return 1;
}


int GetDisplayWidth(lua_State* L)
{
	float w = ImGui::GetIO().DisplaySize.x;
	LuaWrapper::push(L, w);
	return 1;
}


int GetDisplayHeight(lua_State* L)
{
	float w = ImGui::GetIO().DisplaySize.y;
	LuaWrapper::push(L, w);
	return 1;
}


int GetWindowWidth(lua_State* L)
{
	float w = ImGui::GetWindowWidth();
	LuaWrapper::push(L, w);
	return 1;
}


int GetWindowHeight(lua_State* L)
{
	float w = ImGui::GetWindowHeight();
	LuaWrapper::push(L, w);
	return 1;
}


int SameLine(lua_State* L)
{
	float pos_x = 0;
	if (lua_gettop(L) > 0)
	{
		pos_x = LuaWrapper::checkArg<float>(L, 1);
	}
	ImGui::SameLine(pos_x);
	return 0;
}


void registerCFunction(lua_State* L, const char* name, lua_CFunction f)
{
	lua_pushcfunction(L, f);
	lua_setfield(L, -2, name);
}
}

static const u32 SERIALIZED_ENGINE_MAGIC = 0x5f4c454e; // == '_LEN'
static const ResourceType PREFAB_TYPE("prefab");


static FS::OsFile g_error_file;
static bool g_is_error_file_opened = false;


#pragma pack(1)
class SerializedEngineHeader
{
public:
	u32 m_magic;
	u32 m_reserved; // for crc
};
#pragma pack()


static void showLogInVS(const char* system, const char* message)
{
	Debug::debugOutput(system);
	Debug::debugOutput(" : ");
	Debug::debugOutput(message);
	Debug::debugOutput("\n");
}


static void logErrorToFile(const char*, const char* message)
{
	if (!g_is_error_file_opened) return;
	g_error_file.write(message, stringLength(message));
	g_error_file.flush();
}


class EngineImpl LUMIX_FINAL : public Engine
{
public:
	EngineImpl(const char* base_path0, const char* base_path1, FS::FileSystem* fs, IAllocator& allocator)
		: m_allocator(allocator)
		, m_prefab_resource_manager(m_allocator)
		, m_resource_manager(m_allocator)
		, m_lua_resources(m_allocator)
		, m_last_lua_resource_idx(-1)
		, m_mtjd_manager(nullptr)
		, m_fps(0)
		, m_is_game_running(false)
		, m_last_time_delta(0)
		, m_time(0)
		, m_path_manager(m_allocator)
		, m_time_multiplier(1.0f)
		, m_paused(false)
		, m_next_frame(false)
		, m_lifo_allocator(m_allocator, 10 * 1024 * 1024)
	{
		g_log_info.log("Core") << "Creating engine...";
		Profiler::setThreadName("Main");
		installUnhandledExceptionHandler();

		g_is_error_file_opened = g_error_file.open("error.log", FS::Mode::CREATE_AND_WRITE, allocator);

		g_log_error.getCallback().bind<logErrorToFile>();
		g_log_info.getCallback().bind<showLogInVS>();
		g_log_warning.getCallback().bind<showLogInVS>();
		g_log_error.getCallback().bind<showLogInVS>();

		m_platform_data = {};
		m_state = lua_newstate(luaAllocator, &m_allocator);
		luaL_openlibs(m_state);
		registerLuaAPI();

		m_mtjd_manager = MTJD::Manager::create(m_allocator);
		if (!fs)
		{
			m_file_system = FS::FileSystem::create(m_allocator);

			m_mem_file_device = LUMIX_NEW(m_allocator, FS::MemoryFileDevice)(m_allocator);
			m_resource_file_device = LUMIX_NEW(m_allocator, FS::ResourceFileDevice)(m_allocator);
			m_disk_file_device = LUMIX_NEW(m_allocator, FS::DiskFileDevice)("disk", base_path0, m_allocator);

			m_file_system->mount(m_mem_file_device);
			m_file_system->mount(m_resource_file_device);
			m_file_system->mount(m_disk_file_device);
			bool is_patching = base_path1[0] != 0 && !equalStrings(base_path0, base_path1);
			if (is_patching)
			{
				m_patch_file_device = LUMIX_NEW(m_allocator, FS::DiskFileDevice)("patch", base_path1, m_allocator);
				m_file_system->mount(m_patch_file_device);
				m_file_system->setDefaultDevice("memory:patch:disk:resource");
				m_file_system->setSaveGameDevice("memory:disk:resource");
			}
			else
			{
				m_patch_file_device = nullptr;
				m_file_system->setDefaultDevice("memory:disk:resource");
				m_file_system->setSaveGameDevice("memory:disk:resource");
			}
		}
		else
		{
			m_file_system = fs;
			m_mem_file_device = nullptr;
			m_resource_file_device = nullptr;
			m_disk_file_device = nullptr;
			m_patch_file_device = nullptr;
		}

		m_resource_manager.create(*m_file_system);
		m_prefab_resource_manager.create(PREFAB_TYPE, m_resource_manager);

		m_timer = Timer::create(m_allocator);
		m_fps_timer = Timer::create(m_allocator);
		m_fps_frame = 0;
		PropertyRegister::init(m_allocator);

		m_plugin_manager = PluginManager::create(*this);
		m_input_system = InputSystem::create(m_allocator);

		g_log_info.log("Core") << "Engine created.";
	}


	static bool LUA_hasFilesystemWork(Engine* engine)
	{
		return engine->getFileSystem().hasWork();
	}


	static void LUA_processFilesystemWork(Engine* engine)
	{
		engine->getFileSystem().updateAsyncTransactions();
	}


	static void LUA_startGame(Engine* engine, Universe* universe)
	{
		if(engine && universe) engine->startGame(*universe);
	}


	static ComponentHandle LUA_createComponent(Universe* universe, Entity entity, const char* type)
	{
		if (!universe) return INVALID_COMPONENT;
		ComponentType cmp_type = PropertyRegister::getComponentType(type);
		IScene* scene = universe->getScene(cmp_type);
		if (!scene) return INVALID_COMPONENT;
		if (scene->getComponent(entity, cmp_type) != INVALID_COMPONENT)
		{
			g_log_error.log("Lua Script") << "Component " << type << " already exists in entity " << entity.index;
			return INVALID_COMPONENT;
		}

		return scene->createComponent(cmp_type, entity);
	}


	static Entity LUA_createEntity(Universe* univ)
	{
		return univ->createEntity(Vec3(0, 0, 0), Quat(0, 0, 0, 1));
	}


	static void setProperty(const ComponentUID& cmp,
		const PropertyDescriptorBase& desc,
		lua_State* L,
		IAllocator& allocator)
	{
		switch (desc.getType())
		{
		case PropertyDescriptorBase::STRING:
		case PropertyDescriptorBase::FILE:
		case PropertyDescriptorBase::RESOURCE:
			if (lua_isstring(L, -1))
			{
				const char* str = lua_tostring(L, -1);
				InputBlob input_blob(str, stringLength(str));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::DECIMAL:
			if (lua_isnumber(L, -1))
			{
				float f = (float)lua_tonumber(L, -1);
				InputBlob input_blob(&f, sizeof(f));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::ENTITY:
			if (lua_isinteger(L, -1))
			{
				int i = (int)lua_tointeger(L, -1);
				InputBlob input_blob(&i, sizeof(i));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::BOOL:
			if (lua_isboolean(L, -1))
			{
				bool b = lua_toboolean(L, -1) != 0;
				InputBlob input_blob(&b, sizeof(b));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::VEC3:
		case PropertyDescriptorBase::COLOR:
			if (lua_istable(L, -1))
			{
				auto v = LuaWrapper::toType<Vec3>(L, -1);
				InputBlob input_blob(&v, sizeof(v));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::VEC2:
			if (lua_istable(L, -1))
			{
				auto v = LuaWrapper::toType<Vec2>(L, -1);
				InputBlob input_blob(&v, sizeof(v));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::INT2:
			if (lua_istable(L, -1))
			{
				auto v = LuaWrapper::toType<Int2>(L, -1);
				InputBlob input_blob(&v, sizeof(v));
				desc.set(cmp, -1, input_blob);
			}
			break;
		default:
			g_log_error.log("Lua Script") << "Property " << desc.getName() << " has unsupported type";
			break;
		}
	}



	static int LUA_getComponentType(const char* component_type)
	{
		return PropertyRegister::getComponentType(component_type).index;
	}


	static ComponentHandle LUA_getComponent(Universe* universe, Entity entity, int component_type)
	{
		if (!universe->hasComponent(entity, {component_type})) return INVALID_COMPONENT;
		ComponentType type = {component_type};
		IScene* scene = universe->getScene(type);
		if (scene) return scene->getComponent(entity, type);
		
		ASSERT(false);
		return INVALID_COMPONENT;
	}


	static int LUA_createEntityEx(lua_State* L)
	{
		auto* engine = LuaWrapper::checkArg<Engine*>(L, 1);
		auto* ctx = LuaWrapper::checkArg<Universe*>(L, 2);
		LuaWrapper::checkTableArg(L, 3);

		Entity e = ctx->createEntity(Vec3(0, 0, 0), Quat(0, 0, 0, 1));

		lua_pushvalue(L, 3);
		lua_pushnil(L);
		while (lua_next(L, -2) != 0)
		{
			const char* parameter_name = luaL_checkstring(L, -2);
			if (equalStrings(parameter_name, "position"))
			{
				auto pos = LuaWrapper::toType<Vec3>(L, -1);
				ctx->setPosition(e, pos);
			}
			else if (equalStrings(parameter_name, "rotation"))
			{
				auto rot = LuaWrapper::toType<Quat>(L, -1);
				ctx->setRotation(e, rot);
			}
			else
			{
				ComponentType cmp_type = PropertyRegister::getComponentType(parameter_name);
				IScene* scene = ctx->getScene(cmp_type);
				if (scene)
				{
					ComponentUID cmp(e, cmp_type, scene, scene->createComponent(cmp_type, e));
					if (cmp.isValid())
					{
						lua_pushvalue(L, -1);
						lua_pushnil(L);
						while (lua_next(L, -2) != 0)
						{
							const char* property_name = luaL_checkstring(L, -2);
							auto* desc = PropertyRegister::getDescriptor(cmp_type, crc32(property_name));
							if (!desc)
							{
								g_log_error.log("Lua Script") << "Unknown property " << property_name;
							}
							else
							{
								setProperty(cmp, *desc, L, engine->getAllocator());
							}

							lua_pop(L, 1);
						}
						lua_pop(L, 1);
					}
				}
			}
			lua_pop(L, 1);
		}
		lua_pop(L, 1);

		LuaWrapper::push(L, e);
		return 1;
	}


	static int LUA_setEntityRotation(lua_State* L)
	{
		Universe* univ = LuaWrapper::checkArg<Universe*>(L, 1);
		int entity_index = LuaWrapper::checkArg<int>(L, 2);

		if (lua_gettop(L) > 3)
		{
			Vec3 axis = LuaWrapper::checkArg<Vec3>(L, 3);
			float angle = LuaWrapper::checkArg<float>(L, 4);
			univ->setRotation({ entity_index }, Quat(axis, angle));
		}
		else
		{
			Quat rot = LuaWrapper::checkArg<Quat>(L, 3);
			univ->setRotation({ entity_index }, rot);
		}
		return 0;
	}


	static IScene* LUA_getScene(Universe* universe, const char* name)
	{
		u32 hash = crc32(name);
		return universe->getScene(hash);
	}


	static int LUA_loadResource(EngineImpl* engine, const char* path, const char* type)
	{
		ResourceManagerBase* res_manager = engine->getResourceManager().get(ResourceType(type));
		if (!res_manager) return -1;
		Resource* res = res_manager->load(Path(path));
		++engine->m_last_lua_resource_idx;
		engine->m_lua_resources.insert(engine->m_last_lua_resource_idx, res);
		return engine->m_last_lua_resource_idx;
	}


	static void LUA_setEntityLocalRotation(Universe* universe, Entity entity, const Quat& rotation)
	{
		if (!entity.isValid()) return;
		if (!universe->getParent(entity).isValid()) return;

		universe->setLocalRotation(entity, rotation);
	}


	static void LUA_setEntityLocalPosition(Universe* universe, Entity entity, const Vec3& position)
	{
		if (!entity.isValid()) return;
		if (!universe->getParent(entity).isValid()) return;

		universe->setLocalPosition(entity, position);
	}


	static float LUA_getInputActionValue(Engine* engine, u32 action)
	{
		auto v = engine->getInputSystem().getActionValue(action);
		return v;
	}


	static void LUA_addInputAction(Engine* engine, u32 action, int type, int key, int controller_id)
	{
		engine->getInputSystem().addAction(action, InputSystem::InputType(type), key, controller_id);
	}


	static void LUA_setEntityPosition(Universe* univ, Entity entity, Vec3 pos) { univ->setPosition(entity, pos); }
	static void LUA_unloadResource(EngineImpl* engine, int resource_idx) { engine->unloadLuaResource(resource_idx); }
	static Universe* LUA_createUniverse(EngineImpl* engine) { return &engine->createUniverse(false); }
	static void LUA_destroyUniverse(EngineImpl* engine, Universe* universe) { engine->destroyUniverse(*universe); }
	static Universe* LUA_getSceneUniverse(IScene* scene) { return &scene->getUniverse(); }
	static void LUA_logError(const char* text) { g_log_error.log("Lua Script") << text; }
	static void LUA_logInfo(const char* text) { g_log_info.log("Lua Script") << text; }
	static void LUA_pause(Engine* engine, bool pause) { engine->pause(pause); }
	static void LUA_nextFrame(Engine* engine) { engine->nextFrame(); }
	static void LUA_setTimeMultiplier(Engine* engine, float multiplier) { engine->setTimeMultiplier(multiplier); }
	static Entity LUA_getFirstEntity(Universe* universe) { return universe->getFirstEntity(); }
	static Entity LUA_getNextEntity(Universe* universe, Entity entity) { return universe->getNextEntity(entity); }
	static Vec4 LUA_multMatrixVec(const Matrix& m, const Vec4& v) { return m * v; }
	static Quat LUA_multQuat(const Quat& a, const Quat& b) { return a * b; }


	static int LUA_multVecQuat(lua_State* L)
	{
		Vec3 v = LuaWrapper::checkArg<Vec3>(L, 1);
		Quat q;
		if (LuaWrapper::isType<Quat>(L, 2))
		{
			q = LuaWrapper::checkArg<Quat>(L, 2);
		}
		else
		{
			Vec3 axis = LuaWrapper::checkArg<Vec3>(L, 2);
			float angle = LuaWrapper::checkArg<float>(L, 3);

			q = Quat(axis, angle);
		}
		
		Vec3 res = q.rotate(v);

		LuaWrapper::push(L, res);
		return 1;
	}


	static Vec3 LUA_getEntityPosition(Universe* universe, Entity entity)
	{
		if (!entity.isValid())
		{
			g_log_warning.log("Engine") << "Requesting position on invalid entity";
			return Vec3(0, 0, 0);
		}
		return universe->getPosition(entity);
	}


	static Quat LUA_getEntityRotation(Universe* universe, Entity entity)
	{
		if (!entity.isValid())
		{
			g_log_warning.log("Engine") << "Requesting rotation on invalid entity";
			return Quat(0, 0, 0, 1);
		}
		return universe->getRotation(entity);
	}


	static void LUA_destroyEntity(Universe* universe, Entity entity)
	{
		universe->destroyEntity(entity);
	}


	static Vec3 LUA_getEntityDirection(Universe* universe, Entity entity)
	{
		Quat rot = universe->getRotation(entity);
		return rot.rotate(Vec3(0, 0, 1));
	}


	void registerLuaAPI()
	{
		lua_pushlightuserdata(m_state, this);
		lua_setglobal(m_state, "g_engine");

		#define REGISTER_FUNCTION(name) \
			LuaWrapper::createSystemFunction(m_state, "Engine", #name, \
				&LuaWrapper::wrap<decltype(&LUA_##name), LUA_##name>); \

		REGISTER_FUNCTION(addInputAction);
		REGISTER_FUNCTION(createComponent);
		REGISTER_FUNCTION(createEntity);
		REGISTER_FUNCTION(createUniverse);
		REGISTER_FUNCTION(destroyEntity);
		REGISTER_FUNCTION(destroyUniverse);
		REGISTER_FUNCTION(getComponent);
		REGISTER_FUNCTION(getComponentType);
		REGISTER_FUNCTION(getEntityDirection);
		REGISTER_FUNCTION(getEntityPosition);
		REGISTER_FUNCTION(getEntityRotation);
		REGISTER_FUNCTION(getFirstEntity);
		REGISTER_FUNCTION(getInputActionValue);
		REGISTER_FUNCTION(getNextEntity);
		REGISTER_FUNCTION(getScene);
		REGISTER_FUNCTION(getSceneUniverse);
		REGISTER_FUNCTION(hasFilesystemWork);
		REGISTER_FUNCTION(loadResource);
		REGISTER_FUNCTION(logError);
		REGISTER_FUNCTION(logInfo);
		REGISTER_FUNCTION(multMatrixVec);
		REGISTER_FUNCTION(multQuat);
		REGISTER_FUNCTION(nextFrame);
		REGISTER_FUNCTION(pause);
		REGISTER_FUNCTION(processFilesystemWork);
		REGISTER_FUNCTION(setEntityLocalPosition);
		REGISTER_FUNCTION(setEntityLocalRotation);
		REGISTER_FUNCTION(setEntityPosition);
		REGISTER_FUNCTION(setEntityRotation);
		REGISTER_FUNCTION(setTimeMultiplier);
		REGISTER_FUNCTION(startGame);
		REGISTER_FUNCTION(unloadResource);

		#undef REGISTER_FUNCTION

		LuaWrapper::createSystemFunction(m_state, "Engine", "instantiatePrefab", &LUA_instantiatePrefab);
		LuaWrapper::createSystemFunction(m_state, "Engine", "createEntityEx", &LUA_createEntityEx);
		LuaWrapper::createSystemFunction(m_state, "Engine", "multVecQuat", &LUA_multVecQuat);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_DOWN", InputSystem::DOWN);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_PRESSED", InputSystem::PRESSED);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_MOUSE_X", InputSystem::MOUSE_X);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_MOUSE_Y", InputSystem::MOUSE_Y);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_LTHUMB_X", InputSystem::LTHUMB_X);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_LTHUMB_Y", InputSystem::LTHUMB_Y);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_RTHUMB_X", InputSystem::RTHUMB_X);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_RTHUMB_Y", InputSystem::RTHUMB_Y);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_RTRIGGER", InputSystem::RTRIGGER);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_LTRIGGER", InputSystem::LTRIGGER);

		lua_newtable(m_state);
		lua_pushvalue(m_state, -1);
		lua_setglobal(m_state, "ImGui");

		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoMove", ImGuiWindowFlags_NoMove);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoCollapse", ImGuiWindowFlags_NoCollapse);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoInputs", ImGuiWindowFlags_NoInputs);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoResize", ImGuiWindowFlags_NoResize);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoTitleBar", ImGuiWindowFlags_NoTitleBar);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoScrollbar", ImGuiWindowFlags_NoScrollbar);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_AlwaysAutoResize", ImGuiWindowFlags_AlwaysAutoResize);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_FrameBg", ImGuiCol_FrameBg);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_WindowBg", ImGuiCol_WindowBg);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_Button", ImGuiCol_Button);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_ButtonActive", ImGuiCol_ButtonActive);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_ButtonHovered", ImGuiCol_ButtonHovered);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_FramePadding", ImGuiStyleVar_FramePadding);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_IndentSpacing", ImGuiStyleVar_IndentSpacing);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_ItemSpacing", ImGuiStyleVar_ItemSpacing);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_ItemInnerSpacing", ImGuiStyleVar_ItemInnerSpacing);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_WindowPadding", ImGuiStyleVar_WindowPadding);
		LuaImGui::registerCFunction(m_state, "AlignFirstTextHeightToWidgets", &LuaImGui::AlignFirstTextHeightToWidgets);
		LuaImGui::registerCFunction(m_state, "Begin", &LuaImGui::Begin);
		LuaImGui::registerCFunction(m_state, "BeginChildFrame", &LuaImGui::BeginChildFrame);
		LuaImGui::registerCFunction(m_state, "BeginDock", LuaImGui::BeginDock);
		LuaImGui::registerCFunction(m_state, "BeginPopup", &LuaWrapper::wrap<decltype(&ImGui::BeginPopup), &ImGui::BeginPopup>);
		LuaImGui::registerCFunction(m_state, "Button", &LuaImGui::Button);
		LuaImGui::registerCFunction(m_state, "Checkbox", &LuaImGui::Checkbox);
		LuaImGui::registerCFunction(m_state, "CollapsingHeader", &LuaImGui::CollapsingHeader);
		LuaImGui::registerCFunction(m_state, "DragFloat", &LuaImGui::DragFloat);
		LuaImGui::registerCFunction(m_state, "Dummy", &LuaWrapper::wrap<decltype(&LuaImGui::Dummy), &LuaImGui::Dummy>);
		LuaImGui::registerCFunction(m_state, "End", &LuaWrapper::wrap<decltype(&ImGui::End), &ImGui::End>);
		LuaImGui::registerCFunction(m_state, "EndChildFrame", &LuaWrapper::wrap<decltype(&ImGui::EndChildFrame), &ImGui::EndChildFrame>);
		LuaImGui::registerCFunction(m_state, "EndDock", &LuaWrapper::wrap<decltype(&ImGui::EndDock), &ImGui::EndDock>);
		LuaImGui::registerCFunction(m_state, "EndPopup", &LuaWrapper::wrap<decltype(&ImGui::EndPopup), &ImGui::EndPopup>);
		LuaImGui::registerCFunction(m_state, "GetDisplayWidth", &LuaImGui::GetDisplayWidth);
		LuaImGui::registerCFunction(m_state, "GetDisplayHeight", &LuaImGui::GetDisplayHeight);
		LuaImGui::registerCFunction(m_state, "GetWindowWidth", &LuaImGui::GetWindowWidth);
		LuaImGui::registerCFunction(m_state, "GetWindowHeight", &LuaImGui::GetWindowHeight);
		LuaImGui::registerCFunction(m_state, "GetWindowPos", &LuaImGui::GetWindowPos);
		LuaImGui::registerCFunction(m_state, "Image", &LuaWrapper::wrap<decltype(&LuaImGui::Image), &LuaImGui::Image>);
		LuaImGui::registerCFunction(m_state, "Indent", &LuaWrapper::wrap<decltype(&ImGui::Indent), &ImGui::Indent>);
		LuaImGui::registerCFunction(m_state, "InputTextMultiline", &LuaImGui::InputTextMultiline);
		LuaImGui::registerCFunction(m_state, "IsItemHovered", &LuaWrapper::wrap<decltype(&LuaImGui::IsItemHovered), &LuaImGui::IsItemHovered>);
		LuaImGui::registerCFunction(m_state, "IsMouseClicked", &LuaWrapper::wrap<decltype(&LuaImGui::IsMouseClicked), &LuaImGui::IsMouseClicked>);
		LuaImGui::registerCFunction(m_state, "IsMouseDown", &LuaWrapper::wrap<decltype(&LuaImGui::IsMouseDown), &LuaImGui::IsMouseDown>);
		LuaImGui::registerCFunction(m_state, "OpenPopup", &LuaWrapper::wrap<decltype(&ImGui::OpenPopup), &ImGui::OpenPopup>);
		LuaImGui::registerCFunction(m_state, "PopItemWidth", &LuaWrapper::wrap<decltype(&ImGui::PopItemWidth), &ImGui::PopItemWidth>);
		LuaImGui::registerCFunction(m_state, "PopID", &LuaWrapper::wrap<decltype(&ImGui::PopID), &ImGui::PopID>);
		LuaImGui::registerCFunction(m_state, "PopStyleVar", &LuaWrapper::wrap<decltype(&ImGui::PopStyleVar), &ImGui::PopStyleVar>);
		LuaImGui::registerCFunction(m_state, "PushItemWidth", &LuaWrapper::wrap<decltype(&ImGui::PushItemWidth), &ImGui::PushItemWidth>);
		LuaImGui::registerCFunction(m_state, "PushID", &LuaImGui::PushID);
		LuaImGui::registerCFunction(m_state, "PushStyleVar", &LuaImGui::PushStyleVar);
		LuaImGui::registerCFunction(m_state, "Rect", &LuaWrapper::wrap<decltype(&LuaImGui::Rect), &LuaImGui::Rect>);
		LuaImGui::registerCFunction(m_state, "SameLine", &LuaImGui::SameLine);
		LuaImGui::registerCFunction(m_state, "Selectable", &LuaImGui::Selectable);
		LuaImGui::registerCFunction(m_state, "Separator", &LuaImGui::Separator);
		LuaImGui::registerCFunction(m_state, "SetCursorScreenPos", &LuaImGui::SetCursorScreenPos);
		LuaImGui::registerCFunction(m_state, "SetNextWindowPos", &LuaImGui::SetNextWindowPos);
		LuaImGui::registerCFunction(m_state, "SetNextWindowPosCenter", &LuaImGui::SetNextWindowPosCenter);
		LuaImGui::registerCFunction(m_state, "SetNextWindowSize", &LuaWrapper::wrap<decltype(&LuaImGui::SetNextWindowSize), &LuaImGui::SetNextWindowSize>);
		LuaImGui::registerCFunction(m_state, "SetStyleColor", &LuaImGui::SetStyleColor);
		LuaImGui::registerCFunction(m_state, "SliderFloat", &LuaImGui::SliderFloat);
		LuaImGui::registerCFunction(m_state, "Text", &LuaImGui::Text);
		LuaImGui::registerCFunction(m_state, "Unindent", &LuaWrapper::wrap<decltype(&ImGui::Unindent), &ImGui::Unindent>);
		LuaImGui::registerCFunction(m_state, "LabelText", &LuaImGui::LabelText);<commit_msg>Lua ImGui API<commit_after>#include "engine/engine.h"
#include "engine/blob.h"
#include "engine/crc32.h"
#include "engine/debug/debug.h"
#include "engine/fs/disk_file_device.h"
#include "engine/fs/file_system.h"
#include "engine/fs/memory_file_device.h"
#include "engine/fs/os_file.h"
#include "engine/fs/resource_file_device.h"
#include "engine/input_system.h"
#include "engine/iplugin.h"
#include "engine/lifo_allocator.h"
#include "engine/log.h"
#include "engine/lua_wrapper.h"
#include "engine/math_utils.h"
#include "engine/mtjd/manager.h"
#include "engine/path.h"
#include "engine/plugin_manager.h"
#include "engine/prefab.h"
#include "engine/profiler.h"
#include "engine/property_descriptor.h"
#include "engine/property_register.h"
#include "engine/resource_manager.h"
#include "engine/timer.h"
#include "engine/universe/component.h"
#include "engine/universe/universe.h"
#include <imgui/imgui.h>


namespace Lumix
{

namespace LuaImGui
{


int InputTextMultiline(lua_State* L)
{
	char buf[4096];
	auto* name = LuaWrapper::checkArg<const char*>(L, 1);
	auto* value = LuaWrapper::checkArg<const char*>(L, 2);
	copyString(buf, value);
	bool changed = ImGui::InputTextMultiline(name, buf, lengthOf(buf), ImVec2(-1, -1));
	lua_pushboolean(L, changed);
	if (changed)
	{
		lua_pushstring(L, buf);
	}
	else
	{
		lua_pushvalue(L, 2);
	}
	return 2;
}


int DragFloat(lua_State* L)
{
	auto* name = LuaWrapper::checkArg<const char*>(L, 1);
	float value = LuaWrapper::checkArg<float>(L, 2);
	bool changed = ImGui::DragFloat(name, &value);
	lua_pushboolean(L, changed);
	lua_pushnumber(L, value);
	return 2;
}


int PushStyleVar(lua_State* L)
{
	int var = LuaWrapper::checkArg<int>(L, 1);
	if (lua_gettop(L) > 2)
	{
		ImVec2 v;
		v.x = LuaWrapper::checkArg<float>(L, 2);
		v.y = LuaWrapper::checkArg<float>(L, 3);
		ImGui::PushStyleVar(var, v);
	}
	else
	{
		float v = LuaWrapper::checkArg<float>(L, 2);
		ImGui::PushStyleVar(var, v);
	}
	return 0;
}


int PushID(lua_State* L)
{
	int id = LuaWrapper::checkArg<int>(L, 1);
	ImGui::PushID(id);
	return 0;
}


int SetStyleColor(lua_State* L)
{
	auto& style = ImGui::GetStyle();
	int index = LuaWrapper::checkArg<int>(L, 1);
	ImVec4 color;
	color.x = LuaWrapper::checkArg<float>(L, 2);
	color.y = LuaWrapper::checkArg<float>(L, 3);
	color.z = LuaWrapper::checkArg<float>(L, 4);
	color.w = LuaWrapper::checkArg<float>(L, 5);
	style.Colors[index] = color;
	return 0;
}


int ShowTestWindow(lua_State* L)
{
	ImGui::ShowTestWindow();
	return 0;
}


int SliderFloat(lua_State* L)
{
	auto* name = LuaWrapper::checkArg<const char*>(L, 1);
	float value = LuaWrapper::checkArg<float>(L, 2);
	float min = LuaWrapper::checkArg<float>(L, 3);
	float max = LuaWrapper::checkArg<float>(L, 4);
	bool changed = ImGui::SliderFloat(name, &value, min, max, "");
	lua_pushboolean(L, changed);
	lua_pushnumber(L, value);
	return 2;
}


int Text(lua_State* L)
{
	auto* text = LuaWrapper::checkArg<const char*>(L, 1);
	ImGui::Text("%s", text);
	return 0;
}


int LabelText(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	auto* text = LuaWrapper::checkArg<const char*>(L, 2);
	ImGui::LabelText(label, "%s", text);
	return 0;
}


int Button(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	ImVec2 size(0, 0);
	if (lua_gettop(L) > 2)
	{
		size.x = LuaWrapper::checkArg<float>(L, 2);
		size.y = LuaWrapper::checkArg<float>(L, 3);
	}
	bool clicked = ImGui::Button(label, size);
	lua_pushboolean(L, clicked);
	return 1;
}


int CollapsingHeader(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	lua_pushboolean(L, ImGui::CollapsingHeader(label));
	return 1;
}


int CalcTextSize(lua_State* L)
{
	auto* text = LuaWrapper::checkArg<const char*>(L, 1);
	ImVec2 size = ImGui::CalcTextSize(text);

	LuaWrapper::push(L, size.x);
	LuaWrapper::push(L, size.y);
	return 2;
}


int Checkbox(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	bool b = LuaWrapper::checkArg<bool>(L, 2);
	bool clicked = ImGui::Checkbox(label, &b);
	lua_pushboolean(L, clicked);
	lua_pushboolean(L, b);
	return 2;
}


int GetWindowPos(lua_State* L)
{
	ImVec2 pos = ImGui::GetWindowPos();
	LuaWrapper::push(L, Vec2(pos.x, pos.y));
	return 1;
}


int SetNextWindowPos(lua_State* L)
{
	ImVec2 pos;
	pos.x = LuaWrapper::checkArg<float>(L, 1);
	pos.y = LuaWrapper::checkArg<float>(L, 2);
	ImGui::SetNextWindowPos(pos);
	return 0;
}


int AlignFirstTextHeightToWidgets(lua_State* L)
{
	ImGui::AlignFirstTextHeightToWidgets();
	return 0;
}


int Selectable(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	bool selected = false;
	if (lua_gettop(L) > 1)
	{
		selected = LuaWrapper::checkArg<bool>(L, 2);
	}
	bool clicked = ImGui::Selectable(label, selected);
	lua_pushboolean(L, clicked);
	return 1;
}


int SetCursorScreenPos(lua_State* L)
{
	ImVec2 pos;
	pos.x = LuaWrapper::checkArg<float>(L, 1);
	pos.y = LuaWrapper::checkArg<float>(L, 2);
	ImGui::SetCursorScreenPos(pos);
	return 0;
}


int Separator(lua_State* L)
{
	ImGui::Separator();
	return 0;
}


void Rect(float w, float h, u32 color)
{
	ImGui::Rect(w, h, color);
}


void Dummy(float w, float h)
{
	ImGui::Dummy({w, h});
}


void Image(void* texture_id, float w, float h)
{
	ImGui::Image(texture_id, ImVec2(w, h));
}


bool IsItemHovered()
{
	return ImGui::IsItemHovered();
}


bool IsMouseDown(int button)
{
	return ImGui::IsMouseDown(button);
}


bool IsMouseClicked(int button)
{
	return ImGui::IsMouseClicked(button);
}


int SetNextWindowPosCenter(lua_State* L)
{
	ImGui::SetNextWindowPosCenter();
	return 0;
}


int SetNextWindowSize(float w, float h)
{
	ImGui::SetNextWindowSize(ImVec2(w, h));
	return 0;
}


int Begin(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	ImGuiWindowFlags flags = 0;
	if (lua_gettop(L) > 1)
	{
		flags = LuaWrapper::checkArg<int>(L, 2);
	}
	bool res = ImGui::Begin(label, nullptr, flags);
	lua_pushboolean(L, res);
	return 1;
}


int BeginChildFrame(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	ImVec2 size(0, 0);
	if (lua_gettop(L) > 1)
	{
		size.x = LuaWrapper::checkArg<float>(L, 2);
		size.y = LuaWrapper::checkArg<float>(L, 3);
	}
	bool res = ImGui::BeginChildFrame(ImGui::GetID(label), size);
	lua_pushboolean(L, res);
	return 1;
}


int BeginDock(lua_State* L)
{
	auto* label = LuaWrapper::checkArg<const char*>(L, 1);
	bool res = ImGui::BeginDock(label);
	lua_pushboolean(L, res);
	return 1;
}


int GetDisplayWidth(lua_State* L)
{
	float w = ImGui::GetIO().DisplaySize.x;
	LuaWrapper::push(L, w);
	return 1;
}


int GetDisplayHeight(lua_State* L)
{
	float w = ImGui::GetIO().DisplaySize.y;
	LuaWrapper::push(L, w);
	return 1;
}


int GetWindowWidth(lua_State* L)
{
	float w = ImGui::GetWindowWidth();
	LuaWrapper::push(L, w);
	return 1;
}


int GetWindowHeight(lua_State* L)
{
	float w = ImGui::GetWindowHeight();
	LuaWrapper::push(L, w);
	return 1;
}


int SameLine(lua_State* L)
{
	float pos_x = 0;
	if (lua_gettop(L) > 0)
	{
		pos_x = LuaWrapper::checkArg<float>(L, 1);
	}
	ImGui::SameLine(pos_x);
	return 0;
}


void registerCFunction(lua_State* L, const char* name, lua_CFunction f)
{
	lua_pushcfunction(L, f);
	lua_setfield(L, -2, name);
}
}

static const u32 SERIALIZED_ENGINE_MAGIC = 0x5f4c454e; // == '_LEN'
static const ResourceType PREFAB_TYPE("prefab");


static FS::OsFile g_error_file;
static bool g_is_error_file_opened = false;


#pragma pack(1)
class SerializedEngineHeader
{
public:
	u32 m_magic;
	u32 m_reserved; // for crc
};
#pragma pack()


static void showLogInVS(const char* system, const char* message)
{
	Debug::debugOutput(system);
	Debug::debugOutput(" : ");
	Debug::debugOutput(message);
	Debug::debugOutput("\n");
}


static void logErrorToFile(const char*, const char* message)
{
	if (!g_is_error_file_opened) return;
	g_error_file.write(message, stringLength(message));
	g_error_file.flush();
}


class EngineImpl LUMIX_FINAL : public Engine
{
public:
	EngineImpl(const char* base_path0, const char* base_path1, FS::FileSystem* fs, IAllocator& allocator)
		: m_allocator(allocator)
		, m_prefab_resource_manager(m_allocator)
		, m_resource_manager(m_allocator)
		, m_lua_resources(m_allocator)
		, m_last_lua_resource_idx(-1)
		, m_mtjd_manager(nullptr)
		, m_fps(0)
		, m_is_game_running(false)
		, m_last_time_delta(0)
		, m_time(0)
		, m_path_manager(m_allocator)
		, m_time_multiplier(1.0f)
		, m_paused(false)
		, m_next_frame(false)
		, m_lifo_allocator(m_allocator, 10 * 1024 * 1024)
	{
		g_log_info.log("Core") << "Creating engine...";
		Profiler::setThreadName("Main");
		installUnhandledExceptionHandler();

		g_is_error_file_opened = g_error_file.open("error.log", FS::Mode::CREATE_AND_WRITE, allocator);

		g_log_error.getCallback().bind<logErrorToFile>();
		g_log_info.getCallback().bind<showLogInVS>();
		g_log_warning.getCallback().bind<showLogInVS>();
		g_log_error.getCallback().bind<showLogInVS>();

		m_platform_data = {};
		m_state = lua_newstate(luaAllocator, &m_allocator);
		luaL_openlibs(m_state);
		registerLuaAPI();

		m_mtjd_manager = MTJD::Manager::create(m_allocator);
		if (!fs)
		{
			m_file_system = FS::FileSystem::create(m_allocator);

			m_mem_file_device = LUMIX_NEW(m_allocator, FS::MemoryFileDevice)(m_allocator);
			m_resource_file_device = LUMIX_NEW(m_allocator, FS::ResourceFileDevice)(m_allocator);
			m_disk_file_device = LUMIX_NEW(m_allocator, FS::DiskFileDevice)("disk", base_path0, m_allocator);

			m_file_system->mount(m_mem_file_device);
			m_file_system->mount(m_resource_file_device);
			m_file_system->mount(m_disk_file_device);
			bool is_patching = base_path1[0] != 0 && !equalStrings(base_path0, base_path1);
			if (is_patching)
			{
				m_patch_file_device = LUMIX_NEW(m_allocator, FS::DiskFileDevice)("patch", base_path1, m_allocator);
				m_file_system->mount(m_patch_file_device);
				m_file_system->setDefaultDevice("memory:patch:disk:resource");
				m_file_system->setSaveGameDevice("memory:disk:resource");
			}
			else
			{
				m_patch_file_device = nullptr;
				m_file_system->setDefaultDevice("memory:disk:resource");
				m_file_system->setSaveGameDevice("memory:disk:resource");
			}
		}
		else
		{
			m_file_system = fs;
			m_mem_file_device = nullptr;
			m_resource_file_device = nullptr;
			m_disk_file_device = nullptr;
			m_patch_file_device = nullptr;
		}

		m_resource_manager.create(*m_file_system);
		m_prefab_resource_manager.create(PREFAB_TYPE, m_resource_manager);

		m_timer = Timer::create(m_allocator);
		m_fps_timer = Timer::create(m_allocator);
		m_fps_frame = 0;
		PropertyRegister::init(m_allocator);

		m_plugin_manager = PluginManager::create(*this);
		m_input_system = InputSystem::create(m_allocator);

		g_log_info.log("Core") << "Engine created.";
	}


	static bool LUA_hasFilesystemWork(Engine* engine)
	{
		return engine->getFileSystem().hasWork();
	}


	static void LUA_processFilesystemWork(Engine* engine)
	{
		engine->getFileSystem().updateAsyncTransactions();
	}


	static void LUA_startGame(Engine* engine, Universe* universe)
	{
		if(engine && universe) engine->startGame(*universe);
	}


	static ComponentHandle LUA_createComponent(Universe* universe, Entity entity, const char* type)
	{
		if (!universe) return INVALID_COMPONENT;
		ComponentType cmp_type = PropertyRegister::getComponentType(type);
		IScene* scene = universe->getScene(cmp_type);
		if (!scene) return INVALID_COMPONENT;
		if (scene->getComponent(entity, cmp_type) != INVALID_COMPONENT)
		{
			g_log_error.log("Lua Script") << "Component " << type << " already exists in entity " << entity.index;
			return INVALID_COMPONENT;
		}

		return scene->createComponent(cmp_type, entity);
	}


	static Entity LUA_createEntity(Universe* univ)
	{
		return univ->createEntity(Vec3(0, 0, 0), Quat(0, 0, 0, 1));
	}


	static void setProperty(const ComponentUID& cmp,
		const PropertyDescriptorBase& desc,
		lua_State* L,
		IAllocator& allocator)
	{
		switch (desc.getType())
		{
		case PropertyDescriptorBase::STRING:
		case PropertyDescriptorBase::FILE:
		case PropertyDescriptorBase::RESOURCE:
			if (lua_isstring(L, -1))
			{
				const char* str = lua_tostring(L, -1);
				InputBlob input_blob(str, stringLength(str));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::DECIMAL:
			if (lua_isnumber(L, -1))
			{
				float f = (float)lua_tonumber(L, -1);
				InputBlob input_blob(&f, sizeof(f));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::ENTITY:
			if (lua_isinteger(L, -1))
			{
				int i = (int)lua_tointeger(L, -1);
				InputBlob input_blob(&i, sizeof(i));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::BOOL:
			if (lua_isboolean(L, -1))
			{
				bool b = lua_toboolean(L, -1) != 0;
				InputBlob input_blob(&b, sizeof(b));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::VEC3:
		case PropertyDescriptorBase::COLOR:
			if (lua_istable(L, -1))
			{
				auto v = LuaWrapper::toType<Vec3>(L, -1);
				InputBlob input_blob(&v, sizeof(v));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::VEC2:
			if (lua_istable(L, -1))
			{
				auto v = LuaWrapper::toType<Vec2>(L, -1);
				InputBlob input_blob(&v, sizeof(v));
				desc.set(cmp, -1, input_blob);
			}
			break;
		case PropertyDescriptorBase::INT2:
			if (lua_istable(L, -1))
			{
				auto v = LuaWrapper::toType<Int2>(L, -1);
				InputBlob input_blob(&v, sizeof(v));
				desc.set(cmp, -1, input_blob);
			}
			break;
		default:
			g_log_error.log("Lua Script") << "Property " << desc.getName() << " has unsupported type";
			break;
		}
	}



	static int LUA_getComponentType(const char* component_type)
	{
		return PropertyRegister::getComponentType(component_type).index;
	}


	static ComponentHandle LUA_getComponent(Universe* universe, Entity entity, int component_type)
	{
		if (!universe->hasComponent(entity, {component_type})) return INVALID_COMPONENT;
		ComponentType type = {component_type};
		IScene* scene = universe->getScene(type);
		if (scene) return scene->getComponent(entity, type);
		
		ASSERT(false);
		return INVALID_COMPONENT;
	}


	static int LUA_createEntityEx(lua_State* L)
	{
		auto* engine = LuaWrapper::checkArg<Engine*>(L, 1);
		auto* ctx = LuaWrapper::checkArg<Universe*>(L, 2);
		LuaWrapper::checkTableArg(L, 3);

		Entity e = ctx->createEntity(Vec3(0, 0, 0), Quat(0, 0, 0, 1));

		lua_pushvalue(L, 3);
		lua_pushnil(L);
		while (lua_next(L, -2) != 0)
		{
			const char* parameter_name = luaL_checkstring(L, -2);
			if (equalStrings(parameter_name, "position"))
			{
				auto pos = LuaWrapper::toType<Vec3>(L, -1);
				ctx->setPosition(e, pos);
			}
			else if (equalStrings(parameter_name, "rotation"))
			{
				auto rot = LuaWrapper::toType<Quat>(L, -1);
				ctx->setRotation(e, rot);
			}
			else
			{
				ComponentType cmp_type = PropertyRegister::getComponentType(parameter_name);
				IScene* scene = ctx->getScene(cmp_type);
				if (scene)
				{
					ComponentUID cmp(e, cmp_type, scene, scene->createComponent(cmp_type, e));
					if (cmp.isValid())
					{
						lua_pushvalue(L, -1);
						lua_pushnil(L);
						while (lua_next(L, -2) != 0)
						{
							const char* property_name = luaL_checkstring(L, -2);
							auto* desc = PropertyRegister::getDescriptor(cmp_type, crc32(property_name));
							if (!desc)
							{
								g_log_error.log("Lua Script") << "Unknown property " << property_name;
							}
							else
							{
								setProperty(cmp, *desc, L, engine->getAllocator());
							}

							lua_pop(L, 1);
						}
						lua_pop(L, 1);
					}
				}
			}
			lua_pop(L, 1);
		}
		lua_pop(L, 1);

		LuaWrapper::push(L, e);
		return 1;
	}


	static int LUA_setEntityRotation(lua_State* L)
	{
		Universe* univ = LuaWrapper::checkArg<Universe*>(L, 1);
		int entity_index = LuaWrapper::checkArg<int>(L, 2);

		if (lua_gettop(L) > 3)
		{
			Vec3 axis = LuaWrapper::checkArg<Vec3>(L, 3);
			float angle = LuaWrapper::checkArg<float>(L, 4);
			univ->setRotation({ entity_index }, Quat(axis, angle));
		}
		else
		{
			Quat rot = LuaWrapper::checkArg<Quat>(L, 3);
			univ->setRotation({ entity_index }, rot);
		}
		return 0;
	}


	static IScene* LUA_getScene(Universe* universe, const char* name)
	{
		u32 hash = crc32(name);
		return universe->getScene(hash);
	}


	static int LUA_loadResource(EngineImpl* engine, const char* path, const char* type)
	{
		ResourceManagerBase* res_manager = engine->getResourceManager().get(ResourceType(type));
		if (!res_manager) return -1;
		Resource* res = res_manager->load(Path(path));
		++engine->m_last_lua_resource_idx;
		engine->m_lua_resources.insert(engine->m_last_lua_resource_idx, res);
		return engine->m_last_lua_resource_idx;
	}


	static void LUA_setEntityLocalRotation(Universe* universe, Entity entity, const Quat& rotation)
	{
		if (!entity.isValid()) return;
		if (!universe->getParent(entity).isValid()) return;

		universe->setLocalRotation(entity, rotation);
	}


	static void LUA_setEntityLocalPosition(Universe* universe, Entity entity, const Vec3& position)
	{
		if (!entity.isValid()) return;
		if (!universe->getParent(entity).isValid()) return;

		universe->setLocalPosition(entity, position);
	}


	static float LUA_getInputActionValue(Engine* engine, u32 action)
	{
		auto v = engine->getInputSystem().getActionValue(action);
		return v;
	}


	static void LUA_addInputAction(Engine* engine, u32 action, int type, int key, int controller_id)
	{
		engine->getInputSystem().addAction(action, InputSystem::InputType(type), key, controller_id);
	}


	static void LUA_setEntityPosition(Universe* univ, Entity entity, Vec3 pos) { univ->setPosition(entity, pos); }
	static void LUA_unloadResource(EngineImpl* engine, int resource_idx) { engine->unloadLuaResource(resource_idx); }
	static Universe* LUA_createUniverse(EngineImpl* engine) { return &engine->createUniverse(false); }
	static void LUA_destroyUniverse(EngineImpl* engine, Universe* universe) { engine->destroyUniverse(*universe); }
	static Universe* LUA_getSceneUniverse(IScene* scene) { return &scene->getUniverse(); }
	static void LUA_logError(const char* text) { g_log_error.log("Lua Script") << text; }
	static void LUA_logInfo(const char* text) { g_log_info.log("Lua Script") << text; }
	static void LUA_pause(Engine* engine, bool pause) { engine->pause(pause); }
	static void LUA_nextFrame(Engine* engine) { engine->nextFrame(); }
	static void LUA_setTimeMultiplier(Engine* engine, float multiplier) { engine->setTimeMultiplier(multiplier); }
	static Entity LUA_getFirstEntity(Universe* universe) { return universe->getFirstEntity(); }
	static Entity LUA_getNextEntity(Universe* universe, Entity entity) { return universe->getNextEntity(entity); }
	static Vec4 LUA_multMatrixVec(const Matrix& m, const Vec4& v) { return m * v; }
	static Quat LUA_multQuat(const Quat& a, const Quat& b) { return a * b; }


	static int LUA_multVecQuat(lua_State* L)
	{
		Vec3 v = LuaWrapper::checkArg<Vec3>(L, 1);
		Quat q;
		if (LuaWrapper::isType<Quat>(L, 2))
		{
			q = LuaWrapper::checkArg<Quat>(L, 2);
		}
		else
		{
			Vec3 axis = LuaWrapper::checkArg<Vec3>(L, 2);
			float angle = LuaWrapper::checkArg<float>(L, 3);

			q = Quat(axis, angle);
		}
		
		Vec3 res = q.rotate(v);

		LuaWrapper::push(L, res);
		return 1;
	}


	static Vec3 LUA_getEntityPosition(Universe* universe, Entity entity)
	{
		if (!entity.isValid())
		{
			g_log_warning.log("Engine") << "Requesting position on invalid entity";
			return Vec3(0, 0, 0);
		}
		return universe->getPosition(entity);
	}


	static Quat LUA_getEntityRotation(Universe* universe, Entity entity)
	{
		if (!entity.isValid())
		{
			g_log_warning.log("Engine") << "Requesting rotation on invalid entity";
			return Quat(0, 0, 0, 1);
		}
		return universe->getRotation(entity);
	}


	static void LUA_destroyEntity(Universe* universe, Entity entity)
	{
		universe->destroyEntity(entity);
	}


	static Vec3 LUA_getEntityDirection(Universe* universe, Entity entity)
	{
		Quat rot = universe->getRotation(entity);
		return rot.rotate(Vec3(0, 0, 1));
	}


	void registerLuaAPI()
	{
		lua_pushlightuserdata(m_state, this);
		lua_setglobal(m_state, "g_engine");

		#define REGISTER_FUNCTION(name) \
			LuaWrapper::createSystemFunction(m_state, "Engine", #name, \
				&LuaWrapper::wrap<decltype(&LUA_##name), LUA_##name>); \

		REGISTER_FUNCTION(addInputAction);
		REGISTER_FUNCTION(createComponent);
		REGISTER_FUNCTION(createEntity);
		REGISTER_FUNCTION(createUniverse);
		REGISTER_FUNCTION(destroyEntity);
		REGISTER_FUNCTION(destroyUniverse);
		REGISTER_FUNCTION(getComponent);
		REGISTER_FUNCTION(getComponentType);
		REGISTER_FUNCTION(getEntityDirection);
		REGISTER_FUNCTION(getEntityPosition);
		REGISTER_FUNCTION(getEntityRotation);
		REGISTER_FUNCTION(getFirstEntity);
		REGISTER_FUNCTION(getInputActionValue);
		REGISTER_FUNCTION(getNextEntity);
		REGISTER_FUNCTION(getScene);
		REGISTER_FUNCTION(getSceneUniverse);
		REGISTER_FUNCTION(hasFilesystemWork);
		REGISTER_FUNCTION(loadResource);
		REGISTER_FUNCTION(logError);
		REGISTER_FUNCTION(logInfo);
		REGISTER_FUNCTION(multMatrixVec);
		REGISTER_FUNCTION(multQuat);
		REGISTER_FUNCTION(nextFrame);
		REGISTER_FUNCTION(pause);
		REGISTER_FUNCTION(processFilesystemWork);
		REGISTER_FUNCTION(setEntityLocalPosition);
		REGISTER_FUNCTION(setEntityLocalRotation);
		REGISTER_FUNCTION(setEntityPosition);
		REGISTER_FUNCTION(setEntityRotation);
		REGISTER_FUNCTION(setTimeMultiplier);
		REGISTER_FUNCTION(startGame);
		REGISTER_FUNCTION(unloadResource);

		#undef REGISTER_FUNCTION

		LuaWrapper::createSystemFunction(m_state, "Engine", "instantiatePrefab", &LUA_instantiatePrefab);
		LuaWrapper::createSystemFunction(m_state, "Engine", "createEntityEx", &LUA_createEntityEx);
		LuaWrapper::createSystemFunction(m_state, "Engine", "multVecQuat", &LUA_multVecQuat);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_DOWN", InputSystem::DOWN);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_PRESSED", InputSystem::PRESSED);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_MOUSE_X", InputSystem::MOUSE_X);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_MOUSE_Y", InputSystem::MOUSE_Y);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_LTHUMB_X", InputSystem::LTHUMB_X);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_LTHUMB_Y", InputSystem::LTHUMB_Y);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_RTHUMB_X", InputSystem::RTHUMB_X);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_RTHUMB_Y", InputSystem::RTHUMB_Y);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_RTRIGGER", InputSystem::RTRIGGER);
		LuaWrapper::createSystemVariable(m_state, "Engine", "INPUT_TYPE_LTRIGGER", InputSystem::LTRIGGER);

		lua_newtable(m_state);
		lua_pushvalue(m_state, -1);
		lua_setglobal(m_state, "ImGui");

		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoMove", ImGuiWindowFlags_NoMove);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoCollapse", ImGuiWindowFlags_NoCollapse);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoInputs", ImGuiWindowFlags_NoInputs);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoResize", ImGuiWindowFlags_NoResize);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoTitleBar", ImGuiWindowFlags_NoTitleBar);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_NoScrollbar", ImGuiWindowFlags_NoScrollbar);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "WindowFlags_AlwaysAutoResize", ImGuiWindowFlags_AlwaysAutoResize);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_FrameBg", ImGuiCol_FrameBg);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_WindowBg", ImGuiCol_WindowBg);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_Button", ImGuiCol_Button);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_ButtonActive", ImGuiCol_ButtonActive);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "Col_ButtonHovered", ImGuiCol_ButtonHovered);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_FramePadding", ImGuiStyleVar_FramePadding);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_IndentSpacing", ImGuiStyleVar_IndentSpacing);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_ItemSpacing", ImGuiStyleVar_ItemSpacing);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_ItemInnerSpacing", ImGuiStyleVar_ItemInnerSpacing);
		LuaWrapper::createSystemVariable(m_state, "ImGui", "StyleVar_WindowPadding", ImGuiStyleVar_WindowPadding);
		LuaImGui::registerCFunction(m_state, "AlignFirstTextHeightToWidgets", &LuaImGui::AlignFirstTextHeightToWidgets);
		LuaImGui::registerCFunction(m_state, "Begin", &LuaImGui::Begin);
		LuaImGui::registerCFunction(m_state, "BeginChildFrame", &LuaImGui::BeginChildFrame);
		LuaImGui::registerCFunction(m_state, "BeginDock", LuaImGui::BeginDock);
		LuaImGui::registerCFunction(m_state, "BeginPopup", &LuaWrapper::wrap<decltype(&ImGui::BeginPopup), &ImGui::BeginPopup>);
		LuaImGui::registerCFunction(m_state, "Button", &LuaImGui::Button);
		LuaImGui::registerCFunction(m_state, "CalcTextSize", &LuaImGui::CalcTextSize);
		LuaImGui::registerCFunction(m_state, "Checkbox", &LuaImGui::Checkbox);
		LuaImGui::registerCFunction(m_state, "CollapsingHeader", &LuaImGui::CollapsingHeader);
		LuaImGui::registerCFunction(m_state, "Columns", &LuaWrapper::wrap<decltype(&ImGui::Columns), &ImGui::Columns>);
		LuaImGui::registerCFunction(m_state, "DragFloat", &LuaImGui::DragFloat);
		LuaImGui::registerCFunction(m_state, "Dummy", &LuaWrapper::wrap<decltype(&LuaImGui::Dummy), &LuaImGui::Dummy>);
		LuaImGui::registerCFunction(m_state, "End", &LuaWrapper::wrap<decltype(&ImGui::End), &ImGui::End>);
		LuaImGui::registerCFunction(m_state, "EndChildFrame", &LuaWrapper::wrap<decltype(&ImGui::EndChildFrame), &ImGui::EndChildFrame>);
		LuaImGui::registerCFunction(m_state, "EndDock", &LuaWrapper::wrap<decltype(&ImGui::EndDock), &ImGui::EndDock>);
		LuaImGui::registerCFunction(m_state, "EndPopup", &LuaWrapper::wrap<decltype(&ImGui::EndPopup), &ImGui::EndPopup>);
		LuaImGui::registerCFunction(m_state, "GetColumnWidth", &LuaWrapper::wrap<decltype(&ImGui::GetColumnWidth), &ImGui::GetColumnWidth>);
		LuaImGui::registerCFunction(m_state, "GetDisplayWidth", &LuaImGui::GetDisplayWidth);
		LuaImGui::registerCFunction(m_state, "GetDisplayHeight", &LuaImGui::GetDisplayHeight);
		LuaImGui::registerCFunction(m_state, "GetWindowWidth", &LuaImGui::GetWindowWidth);
		LuaImGui::registerCFunction(m_state, "GetWindowHeight", &LuaImGui::GetWindowHeight);
		LuaImGui::registerCFunction(m_state, "GetWindowPos", &LuaImGui::GetWindowPos);
		LuaImGui::registerCFunction(m_state, "Image", &LuaWrapper::wrap<decltype(&LuaImGui::Image), &LuaImGui::Image>);
		LuaImGui::registerCFunction(m_state, "Indent", &LuaWrapper::wrap<decltype(&ImGui::Indent), &ImGui::Indent>);
		LuaImGui::registerCFunction(m_state, "InputTextMultiline", &LuaImGui::InputTextMultiline);
		LuaImGui::registerCFunction(m_state, "IsItemHovered", &LuaWrapper::wrap<decltype(&LuaImGui::IsItemHovered), &LuaImGui::IsItemHovered>);
		LuaImGui::registerCFunction(m_state, "IsMouseClicked", &LuaWrapper::wrap<decltype(&LuaImGui::IsMouseClicked), &LuaImGui::IsMouseClicked>);
		LuaImGui::registerCFunction(m_state, "IsMouseDown", &LuaWrapper::wrap<decltype(&LuaImGui::IsMouseDown), &LuaImGui::IsMouseDown>);
		LuaImGui::registerCFunction(m_state, "NewLine", &LuaWrapper::wrap<decltype(&ImGui::NewLine), &ImGui::NewLine>);
		LuaImGui::registerCFunction(m_state, "NextColumn", &LuaWrapper::wrap<decltype(&ImGui::NextColumn), &ImGui::NextColumn>);
		LuaImGui::registerCFunction(m_state, "OpenPopup", &LuaWrapper::wrap<decltype(&ImGui::OpenPopup), &ImGui::OpenPopup>);
		LuaImGui::registerCFunction(m_state, "PopItemWidth", &LuaWrapper::wrap<decltype(&ImGui::PopItemWidth), &ImGui::PopItemWidth>);
		LuaImGui::registerCFunction(m_state, "PopID", &LuaWrapper::wrap<decltype(&ImGui::PopID), &ImGui::PopID>);
		LuaImGui::registerCFunction(m_state, "PopStyleVar", &LuaWrapper::wrap<decltype(&ImGui::PopStyleVar), &ImGui::PopStyleVar>);
		LuaImGui::registerCFunction(m_state, "PushItemWidth", &LuaWrapper::wrap<decltype(&ImGui::PushItemWidth), &ImGui::PushItemWidth>);
		LuaImGui::registerCFunction(m_state, "PushID", &LuaImGui::PushID);
		LuaImGui::registerCFunction(m_state, "PushStyleVar", &LuaImGui::PushStyleVar);
		LuaImGui::registerCFunction(m_state, "Rect", &LuaWrapper::wrap<decltype(&LuaImGui::Rect), &LuaImGui::Rect>);
		LuaImGui::registerCFunction(m_state, "SameLine", &LuaImGui::SameLine);
		LuaImGui::registerCFunction(m_state, "Selectable", &LuaImGui::Selectable);
		LuaImGui::registerCFunction(m_state, "Separator", &LuaImGui::Separator);
		LuaImGui::registerCFunction(m_state, "SetCursorScreenPos", &LuaImGui::SetCursorScreenPos);
		LuaImGui::registerCFunction(m_state, "SetNextWindowPos", &LuaImGui::SetNextWindowPos);
		LuaImGui::registerCFunction(m_state, "SetNextWindowPosCenter", &LuaImGui::SetNextWindowPosCenter);
		LuaImGui::registerCFunction(m_state, "SetNextWindowSize", &LuaWrapper::wrap<decltype(&LuaImGui::SetNextWindowSize), &LuaImGui::SetNextWindowSize>);
		LuaImGui::registerCFunction(m_state, "SetStyleColor", &LuaImGui::SetStyleColor);
		LuaImGui::registerCFunction(m_state, "ShowTestWindow", &LuaImGui::ShowTestWindow);
		LuaImGui::registerCFunction(m_state, "SliderFloat", &LuaImGui::SliderFloat);
		LuaImGui::registerCFunction(m_state, "Text", &LuaImGui::Text);
		LuaImGui::registerCFunction(m_state, "Unindent", &LuaWrapper::wrap<decltype(&ImGui::Unindent), &ImGui::Unindent>);
		LuaImGui::registerCFunction(m_state, "LabelText", &LuaImGui::LabelText);
{'fraction_non_alphanumeric': 0.14682973490024598, 'fraction_numerical': 0.008745558895873189, 'mean_word_length': 6.48848810437452, 'pattern_counts': {'":': 0, '<': 232, '<?xml version=': 0, '>': 292, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7352865', 'n_tokens_mistral': 23364, 'n_tokens_neox': 21573, 'n_words': 3600}