#include "LuaEntityApi.hpp"
#include "components/EditorMarker.hpp"
#include <OgreLogManager.h>
#include <iostream>

namespace editScene
{

// Global entity ID map
LuaEntityIdMap g_luaEntityIdMap;

int luaEntityToId(flecs::entity e)
{
	if (!e.is_valid())
		return -1;
	return g_luaEntityIdMap.addEntity(e);
}

flecs::entity luaIdToEntity(int id)
{
	return g_luaEntityIdMap.getEntity(id);
}

// ---------------------------------------------------------------------------
// Helper: get the Flecs world from the Lua registry
// ---------------------------------------------------------------------------

static flecs::world getWorld(lua_State *L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, "EditSceneFlecsWorld");
	OgreAssert(lua_islightuserdata(L, -1), "Flecs world not registered");
	flecs::world *world =
		static_cast<flecs::world *>(lua_touserdata(L, -1));
	lua_pop(L, 1);
	return *world;
}

// ---------------------------------------------------------------------------
// Lua: ecs.create_entity() -> int (entity ID)
// Creates a new entity with EditorMarkerComponent and returns its Lua ID.
// ---------------------------------------------------------------------------

static int luaCreateEntity(lua_State *L)
{
	flecs::world world = getWorld(L);
	flecs::entity e = world.entity();
	// Add EditorMarkerComponent so it appears in the editor hierarchy
	// (the component is forward-declared; we use a tag approach)
	e.add<EditorMarkerComponent>();
	int id = g_luaEntityIdMap.addEntity(e);
	lua_pushinteger(L, id);
	return 1;
}

// ---------------------------------------------------------------------------
// Lua: ecs.destroy_entity(id) -> nil
// Destroys an entity and removes it from the ID map.
// ---------------------------------------------------------------------------

static int luaDestroyEntity(lua_State *L)
{
	luaL_checktype(L, 1, LUA_TNUMBER);
	int id = lua_tointeger(L, 1);
	flecs::entity e = g_luaEntityIdMap.getEntity(id);
	if (e.is_alive()) {
		g_luaEntityIdMap.removeEntity(e);
		e.destruct();
	}
	return 0;
}

// ---------------------------------------------------------------------------
// Lua: ecs.entity_exists(id) -> bool
// ---------------------------------------------------------------------------

static int luaEntityExists(lua_State *L)
{
	luaL_checktype(L, 1, LUA_TNUMBER);
	int id = lua_tointeger(L, 1);
	lua_pushboolean(L, g_luaEntityIdMap.hasId(id) ? 1 : 0);
	return 1;
}

// ---------------------------------------------------------------------------
// Lua: ecs.get_player_entity() -> int (entity ID) or nil
// Looks up the entity named "player" in the Flecs world.
// ---------------------------------------------------------------------------

static int luaGetPlayerEntity(lua_State *L)
{
	flecs::world world = getWorld(L);
	flecs::entity e = world.lookup("player");
	if (e.is_valid()) {
		int id = g_luaEntityIdMap.addEntity(e);
		lua_pushinteger(L, id);
	} else {
		lua_pushnil(L);
	}
	return 1;
}

// ---------------------------------------------------------------------------
// Lua: ecs.get_entity_by_name(name) -> int (entity ID) or nil
// ---------------------------------------------------------------------------

static int luaGetEntityByName(lua_State *L)
{
	luaL_checktype(L, 1, LUA_TSTRING);
	const char *name = lua_tostring(L, 1);
	flecs::world world = getWorld(L);
	flecs::entity e = world.lookup(name);
	if (e.is_valid()) {
		int id = g_luaEntityIdMap.addEntity(e);
		lua_pushinteger(L, id);
	} else {
		lua_pushnil(L);
	}
	return 1;
}

// ---------------------------------------------------------------------------
// Lua: ecs.set_entity_name(id, name) -> nil
// ---------------------------------------------------------------------------

static int luaSetEntityName(lua_State *L)
{
	luaL_checktype(L, 1, LUA_TNUMBER);
	luaL_checktype(L, 2, LUA_TSTRING);
	int id = lua_tointeger(L, 1);
	const char *name = lua_tostring(L, 2);
	flecs::entity e = g_luaEntityIdMap.getEntity(id);
	e.set_name(name);
	return 0;
}

// ---------------------------------------------------------------------------
// Lua: ecs.get_entity_name(id) -> string or nil
// ---------------------------------------------------------------------------

static int luaGetEntityName(lua_State *L)
{
	luaL_checktype(L, 1, LUA_TNUMBER);
	int id = lua_tointeger(L, 1);
	flecs::entity e = g_luaEntityIdMap.getEntity(id);
	const char *name = e.name();
	if (name && name[0]) {
		lua_pushstring(L, name);
	} else {
		lua_pushnil(L);
	}
	return 1;
}

// ---------------------------------------------------------------------------
// Lua: ecs.parent(id) -> int (parent ID) or nil
// ---------------------------------------------------------------------------

static int luaGetParent(lua_State *L)
{
	luaL_checktype(L, 1, LUA_TNUMBER);
	int id = lua_tointeger(L, 1);
	flecs::entity e = g_luaEntityIdMap.getEntity(id);
	flecs::entity parent = e.parent();
	if (parent.is_valid()) {
		int parentId = g_luaEntityIdMap.addEntity(parent);
		lua_pushinteger(L, parentId);
	} else {
		lua_pushnil(L);
	}
	return 1;
}

// ---------------------------------------------------------------------------
// Lua: ecs.children(id) -> table of child IDs
// ---------------------------------------------------------------------------

static int luaGetChildren(lua_State *L)
{
	luaL_checktype(L, 1, LUA_TNUMBER);
	int id = lua_tointeger(L, 1);
	flecs::entity e = g_luaEntityIdMap.getEntity(id);

	lua_newtable(L); // result table
	int index = 1;
	e.children([&](flecs::entity child) {
		int childId = g_luaEntityIdMap.addEntity(child);
		lua_pushinteger(L, childId);
		lua_rawseti(L, -2, index);
		index++;
	});
	return 1;
}

// ---------------------------------------------------------------------------
// Register all entity API functions
// ---------------------------------------------------------------------------

void registerLuaEntityApi(lua_State *L)
{
	// Create the "ecs" global table
	lua_newtable(L);

	// Entity management
	lua_pushcfunction(L, luaCreateEntity);
	lua_setfield(L, -2, "create_entity");

	lua_pushcfunction(L, luaDestroyEntity);
	lua_setfield(L, -2, "destroy_entity");

	lua_pushcfunction(L, luaEntityExists);
	lua_setfield(L, -2, "entity_exists");

	lua_pushcfunction(L, luaGetPlayerEntity);
	lua_setfield(L, -2, "get_player_entity");

	lua_pushcfunction(L, luaGetEntityByName);
	lua_setfield(L, -2, "get_entity_by_name");

	lua_pushcfunction(L, luaSetEntityName);
	lua_setfield(L, -2, "set_entity_name");

	lua_pushcfunction(L, luaGetEntityName);
	lua_setfield(L, -2, "get_entity_name");

	// Hierarchy
	lua_pushcfunction(L, luaGetParent);
	lua_setfield(L, -2, "parent");

	lua_pushcfunction(L, luaGetChildren);
	lua_setfield(L, -2, "children");

	// Set the global
	lua_setglobal(L, "ecs");
}

} // namespace editScene
