#include <iostream>
#include <filesystem>
#include "EditorApp.hpp"
#include "GameMode.hpp"
#include "systems/EditorUISystem.hpp"
#include "systems/PhysicsSystem.hpp"
#include "systems/BuoyancySystem.hpp"
#include "systems/EditorSunSystem.hpp"
#include "systems/EditorSkyboxSystem.hpp"
#include "systems/EditorWaterPlaneSystem.hpp"
#include "systems/LightSystem.hpp"
#include "systems/CameraSystem.hpp"
#include "systems/LodSystem.hpp"
#include "systems/StaticGeometrySystem.hpp"
#include "systems/ProceduralTextureSystem.hpp"
#include "systems/ProceduralMaterialSystem.hpp"
#include "systems/ProceduralMeshSystem.hpp"
#include "systems/CharacterSlotSystem.hpp"
#include "systems/AnimationTreeSystem.hpp"
#include "systems/BehaviorTreeSystem.hpp"
#include "systems/NavMeshSystem.hpp"
#include "systems/CharacterSystem.hpp"
#include "systems/SmartObjectSystem.hpp"
#include "systems/GoapRunnerSystem.hpp"
#include "systems/PathFollowingSystem.hpp"
#include "systems/GoapPlannerSystem.hpp"
#include "systems/CellGridSystem.hpp"

#include "systems/NormalDebugSystem.hpp"
#include "systems/RoomLayoutSystem.hpp"
#include "systems/StartupMenuSystem.hpp"
#include "systems/DialogueSystem.hpp"
#include "systems/CharacterClassSystem.hpp"
#include "components/CharacterClassDatabase.hpp"
#include "components/CharacterClassComponent.hpp"
#include "systems/PlayerControllerSystem.hpp"
#include "systems/SceneSerializer.hpp"
#include "camera/EditorCamera.hpp"
#include "components/EntityName.hpp"
#include "components/Transform.hpp"
#include "components/Renderable.hpp"
#include "components/EditorMarker.hpp"
#include "components/PhysicsCollider.hpp"
#include "components/RigidBody.hpp"
#include "components/GeneratedPhysicsTag.hpp"
#include "components/BuoyancyInfo.hpp"
#include "components/InWater.hpp"
#include "components/WaterPhysics.hpp"
#include "components/WaterPlane.hpp"
#include "components/Sun.hpp"
#include "components/Skybox.hpp"
#include "components/Light.hpp"
#include "components/Camera.hpp"
#include "components/Lod.hpp"
#include "components/LodSettings.hpp"
#include "components/StaticGeometry.hpp"
#include "components/StaticGeometryMember.hpp"
#include "components/ProceduralTexture.hpp"
#include "components/ProceduralMaterial.hpp"
#include "components/Primitive.hpp"
#include "components/TriangleBuffer.hpp"
#include "components/CharacterSlots.hpp"
#include "components/AnimationTree.hpp"
#include "components/AnimationTreeTemplate.hpp"
#include "components/Character.hpp"
#include "components/StartupMenu.hpp"
#include "components/PlayerController.hpp"
#include "components/CellGrid.hpp"
#include "components/CellGridModule.hpp"
#include "components/ActionDatabase.hpp"
#include "components/ActionDebug.hpp"
#include "components/BehaviorTree.hpp"
#include "components/GoapBlackboard.hpp"
#include "components/PrefabInstance.hpp"
#include "systems/PrefabSystem.hpp"
#include "components/NavMesh.hpp"
#include "components/SmartObject.hpp"
#include "components/Actuator.hpp"
#include "components/GoapPlanner.hpp"
#include "components/GoapRunner.hpp"
#include "components/PathFollowing.hpp"
#include "systems/ActuatorSystem.hpp"
#include "systems/EventHandlerSystem.hpp"
#include "systems/EventBus.hpp"
#include "systems/ItemSystem.hpp"
#include "components/EventHandler.hpp"
#include "components/Item.hpp"
#include "components/Inventory.hpp"

#include <OgreRTShaderSystem.h>
#include <imgui.h>
#include "lua/LuaEntityApi.hpp"
#include "lua/LuaComponentApi.hpp"
#include "lua/LuaEventApi.hpp"
#include "lua/LuaActionApi.hpp"
#include "lua/LuaBehaviorTreeApi.hpp"
#include "lua/LuaGameModeApi.hpp"
#include "lua/LuaCharacterClassApi.hpp"
#include "lua/LuaDialogueApi.hpp"

//=============================================================================
// ImGuiRenderListener Implementation
//=============================================================================

ImGuiRenderListener::ImGuiRenderListener(Ogre::ImGuiOverlay *imguiOverlay,
					 EditorUISystem *uiSystem,
					 Ogre::RenderWindow *renderWindow,
					 EditorApp *editorApp)
	: m_imguiOverlay(imguiOverlay)
	, m_uiSystem(uiSystem)
	, m_renderWindow(renderWindow)
	, m_editorApp(editorApp)
{
	m_lastTime = m_timer.getMilliseconds();
}

void ImGuiRenderListener::preViewportUpdate(
	const Ogre::RenderTargetViewportEvent &evt)
{
	(void)evt;

	// Calculate delta time
	unsigned long currentTime = m_timer.getMilliseconds();
	m_deltaTime = (currentTime - m_lastTime) / 1000.0f;
	m_lastTime = currentTime;

	// Start ImGui frame before viewport renders
	Ogre::ImGuiOverlay::NewFrame();

	// Render editor UI with delta time
	if (m_uiSystem) {
		m_uiSystem->update(m_deltaTime);
	}

	// Render actuator markers in game mode (after NewFrame so draw
	// commands survive)
	if (m_editorApp) {
		ActuatorSystem *actuatorSys = m_editorApp->getActuatorSystem();
		if (actuatorSys)
			actuatorSys->render();
	}

	// Render startup menu in game mode (inside ImGui frame scope)
	if (m_editorApp &&
	    m_editorApp->getGameMode() == EditorApp::GameMode::Game &&
	    m_editorApp->getGamePlayState() == EditorApp::GamePlayState::Menu) {
		StartupMenuSystem *sms = m_editorApp->getStartupMenuSystem();
		if (sms)
			sms->update(m_deltaTime);
	}

	// Render dialogue box (game mode or editor preview)
	DialogueSystem::getInstance().update(m_deltaTime);

	// Character class system (needs, level-ups, dialogs)
	if (m_editorApp && m_editorApp->getCharacterClassSystem()) {
		m_editorApp->getCharacterClassSystem()->update(m_deltaTime);
		m_editorApp->getCharacterClassSystem()->renderDialogs();
	}
}

void ImGuiRenderListener::postViewportUpdate(
	const Ogre::RenderTargetViewportEvent &evt)
{
	(void)evt;

	// Capture stats after rendering (they're updated at end of frame)
	if (m_renderWindow && m_uiSystem) {
		Ogre::RenderTarget::FrameStats stats =
			m_renderWindow->getStatistics();
		m_uiSystem->setLastBatchCount(
			static_cast<int>(stats.batchCount));
	}

	// End ImGui frame after viewport renders
	ImGui::EndFrame();
}

//=============================================================================
// EditorApp Implementation
//=============================================================================

EditorApp::EditorApp()
	: OgreBites::ApplicationContext("EditSceneEditor")
	, m_sceneMgr(nullptr)
	, m_overlaySystem(nullptr)
	, m_imguiOverlay(nullptr)
	, m_currentModifiers(0)
	, m_gameMode(GameMode::Editor)
	, m_gamePlayState(GamePlayState::Menu)
{
}

EditorApp::~EditorApp()
{
	// Shutdown UI system first (cleans up gizmo while SceneManager is still valid)
	if (m_uiSystem) {
		m_uiSystem->shutdown();
	}

	// Delete all editor entities before OGRE cleanup
	// This ensures all components with Ogre resources are cleaned up while SceneManager exists
	// Collect entities first, then delete after iteration (can't modify during iteration)
	std::vector<flecs::entity> entitiesToDelete;

	// DialogueSystem is a singleton, no manual teardown needed

	m_startupMenuSystem.reset();
	m_playerControllerSystem.reset();
	m_itemSystem.reset();
	m_eventHandlerSystem.reset();
	m_actuatorSystem.reset();
	m_goapPlannerSystem.reset();
	m_pathFollowingSystem.reset();
	m_goapRunnerSystem.reset();
	m_smartObjectSystem.reset();
	m_roomLayoutSystem.reset();
	m_normalDebugSystem.reset();
	m_cellGridSystem.reset();
	m_characterSystem.reset();
	m_navMeshSystem.reset();
	m_behaviorTreeSystem.reset();
	m_animationTreeSystem.reset();
	m_characterSlotSystem.reset();
	m_proceduralMeshSystem.reset();
	m_proceduralMaterialSystem.reset();
	m_proceduralTextureSystem.reset();
	m_staticGeometrySystem.reset();
	m_lodSystem.reset();
	m_cameraSystem.reset();
	m_lightSystem.reset();
	m_physicsSystem.reset();
	m_imguiListener.reset();
	m_uiSystem.reset();
	m_camera.reset();

	// Now OGRE can shut down safely
	// Singletons are managed by OGRE, don't delete them
}

void EditorApp::setup()
{
	try {
		// Base setup
		OgreBites::ApplicationContext::setup();

		// Get root and create scene manager
		Ogre::Root *root = getRoot();
		m_sceneMgr = root->createSceneManager();
		m_sceneMgr->setAmbientLight(
			Ogre::ColourValue(0.3f, 0.3f, 0.3f));

		// Add scene manager to RTShader generator
		Ogre::RTShader::ShaderGenerator *shadergen =
			Ogre::RTShader::ShaderGenerator::getSingletonPtr();
		if (shadergen) {
			shadergen->addSceneManager(m_sceneMgr);
		}

		// Setup overlay system - get from ApplicationContext
		m_overlaySystem = getOverlaySystem();
		OgreAssert(m_overlaySystem, "OverlaySystem not available");
		m_sceneMgr->addRenderQueueListener(m_overlaySystem);

		// Setup ImGui overlay
		m_imguiOverlay = initialiseImGui();
		if (m_imguiOverlay) {
			m_imguiOverlay->setZOrder(300);
			ImGui::StyleColorsDark();
		}

		// Setup camera
		m_camera = std::make_unique<EditorCamera>(m_sceneMgr,
							  getRenderWindow());

		// Setup ECS and scene
		setupECS();
		setupLights();
		createGrid();
		createAxes();
		createDefaultEntities();

		// Setup UI system
		m_uiSystem = std::make_unique<EditorUISystem>(
			m_world, m_sceneMgr, getRenderWindow());
		if (m_uiSystem)
			m_uiSystem->setEditorUIEnabled(m_gameMode ==
						       GameMode::Editor);
		m_uiSystem->setEditorCamera(m_camera.get());

		// Setup physics system
		m_physicsSystem = std::make_unique<EditorPhysicsSystem>(
			m_world, m_sceneMgr);
		m_physicsSystem->initialize();
		m_uiSystem->setPhysicsSystem(m_physicsSystem.get());

		// Setup buoyancy system (requires physics system)
		// Get the physics wrapper from the physics system
		m_buoyancySystem = std::make_unique<BuoyancySystem>(
			m_world, m_physicsSystem->getPhysicsWrapper());
		m_buoyancySystem->initialize();

		m_sunSystem =
			std::make_unique<EditorSunSystem>(m_world, m_sceneMgr);
		m_skyboxSystem = std::make_unique<EditorSkyboxSystem>(
			m_world, m_sceneMgr);
		m_waterPlaneSystem = std::make_unique<EditorWaterPlaneSystem>(
			m_world, m_sceneMgr);

		// Apply debug setting if it was set before system creation
		if (m_debugBuoyancy) {
			m_buoyancySystem->setDebugEnabled(true);
		}

		// Set buoyancy system in UI system for configuration
		if (m_uiSystem) {
			m_uiSystem->setBuoyancySystem(m_buoyancySystem.get());
		}

		// Setup light system
		m_lightSystem = std::make_unique<EditorLightSystem>(m_world,
								    m_sceneMgr);

		// Setup camera system
		m_cameraSystem = std::make_unique<EditorCameraSystem>(
			m_world, m_sceneMgr, getRenderWindow());

		// Setup LOD system
		m_lodSystem =
			std::make_unique<EditorLodSystem>(m_world, m_sceneMgr);
		m_lodSystem->initialize();

		// Setup StaticGeometry system
		m_staticGeometrySystem = std::make_unique<StaticGeometrySystem>(
			m_world, m_sceneMgr);
		m_staticGeometrySystem->initialize();

		// Setup ProceduralTexture system
		m_proceduralTextureSystem =
			std::make_unique<ProceduralTextureSystem>(m_world,
								  m_sceneMgr);
		m_proceduralTextureSystem->initialize();

		// Setup ProceduralMaterial system
		m_proceduralMaterialSystem =
			std::make_unique<ProceduralMaterialSystem>(m_world,
								   m_sceneMgr);
		m_proceduralMaterialSystem->initialize();

		// Setup ProceduralMesh system
		m_proceduralMeshSystem = std::make_unique<ProceduralMeshSystem>(
			m_world, m_sceneMgr);
		m_proceduralMeshSystem->initialize();

		// Setup CharacterSlot system
		m_characterSlotSystem = std::make_unique<CharacterSlotSystem>(
			m_world, m_sceneMgr);
		m_characterSlotSystem->initialize();

		// Setup AnimationTree system
		m_animationTreeSystem = std::make_unique<AnimationTreeSystem>(
			m_world, m_sceneMgr);
		m_animationTreeSystem->initialize();

		// Setup Character physics system (needed by BehaviorTreeSystem)
		m_characterSystem =
			std::make_unique<CharacterSystem>(m_world, m_sceneMgr);
		m_characterSystem->initialize();

		m_behaviorTreeSystem = std::make_unique<BehaviorTreeSystem>(
			m_world, m_sceneMgr, m_animationTreeSystem.get(),
			m_characterSystem.get());

		// Setup NavMesh system
		m_navMeshSystem =
			std::make_unique<NavMeshSystem>(m_world, m_sceneMgr);

		// Setup SmartObject system (requires NavMesh, BehaviorTree, and AnimationTree)
		m_smartObjectSystem = std::make_unique<SmartObjectSystem>(
			m_world, m_sceneMgr, m_navMeshSystem.get(),
			m_behaviorTreeSystem.get());
		// Wire up AnimationTreeSystem for animation state machine control
		m_smartObjectSystem->setAnimationTreeSystem(
			m_animationTreeSystem.get());
		// Wire up EditorApp for game mode detection
		m_smartObjectSystem->setEditorApp(this);

		// Setup Actuator system
		m_actuatorSystem = std::make_unique<ActuatorSystem>(
			m_world, m_sceneMgr, this, m_behaviorTreeSystem.get());

		// Setup Event Handler system
		m_eventHandlerSystem = std::make_unique<EventHandlerSystem>(
			m_world, m_behaviorTreeSystem.get());

		// Setup Item system
		m_itemSystem = std::make_unique<ItemSystem>(
			m_world, m_sceneMgr, this, m_behaviorTreeSystem.get());

		// Wire ItemSystem into SmartObjectSystem for BT access
		m_smartObjectSystem->setItemSystem(m_itemSystem.get());

		// Setup GOAP Runner system
		m_goapRunnerSystem = std::make_unique<GoapRunnerSystem>(
			m_world, m_sceneMgr, m_smartObjectSystem.get(),
			m_behaviorTreeSystem.get(), m_navMeshSystem.get());
		m_goapRunnerSystem->setEditorApp(this);
		m_goapRunnerSystem->setAnimationTreeSystem(
			m_animationTreeSystem.get());

		// Setup GOAP Planner system
		m_goapPlannerSystem =
			std::make_unique<GoapPlannerSystem>(m_world);
		m_goapPlannerSystem->setEditorApp(this);

		// Setup Path Following system
		m_pathFollowingSystem = std::make_unique<PathFollowingSystem>(
			m_world, m_sceneMgr, m_navMeshSystem.get());
		m_pathFollowingSystem->setAnimationTreeSystem(
			m_animationTreeSystem.get());

		// Setup CellGrid system
		m_cellGridSystem =
			std::make_unique<CellGridSystem>(m_world, m_sceneMgr);
		m_cellGridSystem->initialize();

		// Wire CellGridSystem into NavMeshSystem so it can collect
		// batched frame/furniture geometry from StaticGeometry.
		m_navMeshSystem->setCellGridSystem(m_cellGridSystem.get());

		// Setup NormalDebug system (disabled by default)
		m_normalDebugSystem = std::make_unique<NormalDebugSystem>(
			m_world, m_sceneMgr, m_cellGridSystem.get());

		// Wire NormalDebugSystem into UI for toggle
		if (m_uiSystem) {
			m_uiSystem->setNormalDebugSystem(
				m_normalDebugSystem.get());
		}

		// Setup RoomLayout system
		m_roomLayoutSystem =
			std::make_unique<RoomLayoutSystem>(m_world, m_sceneMgr);
		m_roomLayoutSystem->initialize();

		// Setup game systems
		m_startupMenuSystem = std::make_unique<StartupMenuSystem>(
			m_world, m_sceneMgr, this);
		DialogueSystem::getInstance().init(this);
		DialogueSystem::getInstance().loadSettings("dialogue.json");

		m_characterClassSystem =
			std::make_unique<CharacterClassSystem>(
				m_world, this);
		CharacterClassDatabase::loadFromJson(
			"character_class.json");

		m_playerControllerSystem =
			std::make_unique<PlayerControllerSystem>(
				m_world, m_sceneMgr, this);

		if (m_gameMode == GameMode::Game) {
			// Load startup menu scene configured in editor.
			// This must happen before show() so the
			// StartupMenuComponent entity exists for font preparation.
			SceneSerializer serializer(m_world, m_sceneMgr);
			Ogre::LogManager::getSingleton().logMessage(
				"Game mode: Loading startup_menu.json...");
			if (serializer.loadFromFile("startup_menu.json",
						    m_uiSystem.get())) {
				PrefabSystem prefabSys(m_world, m_sceneMgr);
				prefabSys.resolveInstances();
				Ogre::LogManager::getSingleton().logMessage(
					"Game mode: startup_menu.json loaded");
			} else {
				Ogre::LogManager::getSingleton().logMessage(
					"Game mode: Failed to load startup_menu.json: " +
					serializer.getLastError());
			}

			// Pre-load fonts before showing overlay so the font is
			// added to the atlas before OGRE builds it in createFontTexture().
			// The StartupMenuComponent entity is now available from the
			// startup_menu.json scene loaded above.
			if (m_startupMenuSystem)
				m_startupMenuSystem->prepareFont();
			DialogueSystem::getInstance().prepareFont();
		}

		// Now show the overlay — font atlas will be built with our font
		if (m_imguiOverlay)
			m_imguiOverlay->show();

		// Add default entities to UI cache
		for (auto &e : m_defaultEntities) {
			m_uiSystem->addEntity(e);
		}

		// Create and register ImGui render listener
		m_imguiListener = std::make_unique<ImGuiRenderListener>(
			m_imguiOverlay, m_uiSystem.get(), getRenderWindow(),
			this);
		getRenderWindow()->addListener(m_imguiListener.get());

		// Register input listeners
		addInputListener(this);
		addInputListener(getImGuiInputListener());

		// Initialize Lua scripting
		{
			lua_State *L = m_lua.getState();

			// Store the Flecs world pointer in the Lua registry
			// so Lua API functions can access it.
			flecs::world *worldPtr = &m_world;
			lua_pushlightuserdata(L, worldPtr);
			lua_setfield(L, LUA_REGISTRYINDEX,
				     "EditSceneFlecsWorld");

			// Register all Lua API modules.
			// Order matters: Entity API creates the "ecs" table,
			// Component and Event APIs add to it.
			editScene::registerLuaEntityApi(L);
			editScene::registerLuaComponentApi(L);
			editScene::registerLuaEventApi(L);
			editScene::registerLuaActionApi(L);
			editScene::registerLuaBehaviorTreeApi(L);
			editScene::registerLuaGameModeApi(L);
			editScene::registerLuaCharacterClassApi(L);
			editScene::registerLuaDialogueApi(L);

			// Run late setup: load data.lua and initial scripts.
			m_lua.lateSetup();

			// Auto-load Action Database from "actions.json" in the General
			// resource group.  If the file is missing or contains errors
			// the error is logged and the database stays empty (scene
			// components will still be processed below).
			ActionDatabase::loadFromJson("actions.json");

			// Re-process scene components so that any
			// ActionDatabaseComponent entities in the scene override or
			// append actions from the file.
			ActionDatabase::reloadFromSceneComponents(m_world);
		}

		if (m_gameMode == GameMode::Game) {
			// Queue "game_start" event after Lua scripts are loaded
			// so Lua subscribers registered via ecs.subscribe_event()
			// will receive the event.
			EventBus::getInstance().send("game_start");
		}

		// Game mode can be set externally before setup() is called
		m_setupComplete = true;

	} catch (const std::exception &e) {
		Ogre::LogManager::getSingleton().logMessage(
			"Setup failed: " + Ogre::String(e.what()));
		throw;
	}
}

void EditorApp::setGameMode(GameMode mode)
{
	if (m_setupComplete) {
		Ogre::LogManager::getSingleton().logMessage(
			"setGameMode ignored: cannot change mode after setup");
		return;
	}
	m_gameMode = mode;
	editScene::setEditSceneGameMode(mode == GameMode::Game ?
						editScene::GameMode::Game :
						editScene::GameMode::Editor);
	if (m_gameMode == GameMode::Game) {
		m_gamePlayState = GamePlayState::Menu;
		editScene::setEditSceneGamePlayState(
			editScene::GamePlayState::Menu);
		if (m_uiSystem)
			m_uiSystem->setEditorUIEnabled(false);
	} else {
		m_gamePlayState = GamePlayState::Menu;
		editScene::setEditSceneGamePlayState(
			editScene::GamePlayState::Menu);
		if (m_uiSystem)
			m_uiSystem->setEditorUIEnabled(true);
	}
}

void EditorApp::setDebugBuoyancy(bool enabled)
{
	m_debugBuoyancy = enabled;

	// Apply debug setting to buoyancy system if it exists
	if (m_buoyancySystem) {
		m_buoyancySystem->setDebugEnabled(enabled);
	}

	// Log the debug mode change only if OGRE is initialized
	// (setDebugBuoyancy may be called before OGRE setup)
	try {
		if (Ogre::LogManager::getSingletonPtr()) {
			Ogre::LogManager::getSingleton().logMessage(
				"Buoyancy debug mode: " +
				Ogre::StringConverter::toString(enabled));
		}
	} catch (...) {
		// Ignore if OGRE not initialized yet
	}
}

void EditorApp::setGamePlayState(GamePlayState state)
{
	m_gamePlayState = state;
	editScene::setEditSceneGamePlayState(
		state == GamePlayState::Playing ?
			editScene::GamePlayState::Playing :
		state == GamePlayState::Paused ?
			editScene::GamePlayState::Paused :
			editScene::GamePlayState::Menu);

	// Grab/ungrab mouse based on gameplay state
	if (m_gameMode == GameMode::Game) {
		if (state == GamePlayState::Playing) {
			setWindowGrab(true);
		} else if (state == GamePlayState::Menu) {
			setWindowGrab(false);
		}
	}
}

void EditorApp::clearScene()
{
	// Destroy all entities with EditorMarkerComponent
	std::vector<flecs::entity> entitiesToDelete;
	m_world.query<EditorMarkerComponent>().each(
		[&](flecs::entity e, EditorMarkerComponent) {
			entitiesToDelete.push_back(e);
		});
	for (auto &e : entitiesToDelete) {
		if (e.is_alive()) {
			e.destruct();
		}
	}
	if (m_uiSystem) {
		m_uiSystem->clearEntityCache();
	}
}

void EditorApp::startNewGame(const Ogre::String &scenePath)
{
	clearScene();
	SceneSerializer serializer(m_world, m_sceneMgr);
	if (serializer.loadFromFile(scenePath, m_uiSystem.get())) {
		PrefabSystem prefabSys(m_world, m_sceneMgr);
		prefabSys.resolveInstances();
		setGamePlayState(GamePlayState::Playing);
		Ogre::LogManager::getSingleton().logMessage(
			"Game started: loaded scene " + scenePath);
		// Send "scene_ready" event after scene is loaded and
		// entities/components are populated and ready to run.
		EventBus::getInstance().send("scene_ready");
	} else {
		Ogre::LogManager::getSingleton().logMessage(
			"Failed to load scene: " + serializer.getLastError());
	}
}

void EditorApp::setupECS()
{
	// Register components
	m_world.component<EntityNameComponent>();
	m_world.component<TransformComponent>();
	m_world.component<RenderableComponent>();
	m_world.component<EditorMarkerComponent>();

	// Register physics components
	m_world.component<PhysicsColliderComponent>();
	m_world.component<GeneratedPhysicsTag>();
	m_world.component<RigidBodyComponent>();
	m_world.component<BuoyancyInfo>();
	m_world.component<WaterPhysics>();
	m_world.component<WaterPlane>();

	// Register light and camera components
	m_world.component<LightComponent>();
	m_world.component<CameraComponent>();

	// Register LOD components
	m_world.component<LodComponent>();
	m_world.component<LodSettingsComponent>();

	// Register StaticGeometry components
	m_world.component<StaticGeometryComponent>();
	m_world.component<StaticGeometryMemberComponent>();

	// Register ProceduralTexture component
	m_world.component<ProceduralTextureComponent>();

	// Register ProceduralMaterial component
	m_world.component<ProceduralMaterialComponent>();

	// Register Primitive and TriangleBuffer components
	m_world.component<PrimitiveComponent>();
	m_world.component<TriangleBufferComponent>();

	// Register CharacterSlots component
	m_world.component<CharacterSlotsComponent>();

	// Register AnimationTree component
	m_world.component<AnimationTreeComponent>();

	// Register AnimationTreeTemplate component
	m_world.component<AnimationTreeTemplate>();

	// Register Character component
	m_world.component<CharacterComponent>();

	// Register game components
	m_world.component<StartupMenuComponent>();
	m_world.component<PlayerControllerComponent>();
	m_world.component<InWater>();
	m_world.component<CharacterClassComponent>();
	m_world.component<CharacterClassOverrideComponent>();

	// Register environment components
	m_world.component<SunComponent>();
	m_world.component<SkyboxComponent>();

	// Register AI/GOAP components
	// ActionDatabase is now a singleton, registered in ActionDatabaseModule
	m_world.component<ActionDebug>();
	m_world.component<BehaviorTreeComponent>();
	m_world.component<GoapBlackboard>();

	// Register Smart Object component
	m_world.component<SmartObjectComponent>();

	// Register Actuator component
	m_world.component<ActuatorComponent>();

	// Register Event Handler component
	m_world.component<EventHandlerComponent>();

	// Register GOAP Planner component
	m_world.component<GoapPlannerComponent>();

	// Register GOAP Runner component
	m_world.component<GoapRunnerComponent>();

	// Register Path Following component
	m_world.component<PathFollowingComponent>();

	// Register Navigation components
	m_world.component<NavMeshComponent>();
	m_world.component<NavMeshGeometrySource>();

	// Register CellGrid/Town components
	CellGridModule::registerComponents(m_world);

	// Register PrefabInstance component
	m_world.component<PrefabInstanceComponent>();

	// Register Item and Inventory components
	m_world.component<ItemComponent>();
	m_world.component<InventoryComponent>();
}

void EditorApp::createDefaultEntities()
{
	// Start with empty scene - user creates entities as needed
}

void EditorApp::setupLights()
{
	// Directional light
	Ogre::Light *dirLight = m_sceneMgr->createLight("DirectionalLight");
	dirLight->setType(Ogre::Light::LT_DIRECTIONAL);
	dirLight->setDiffuseColour(Ogre::ColourValue(1.0f, 1.0f, 1.0f));
	dirLight->setSpecularColour(Ogre::ColourValue(0.5f, 0.5f, 0.5f));

	Ogre::SceneNode *dirNode =
		m_sceneMgr->getRootSceneNode()->createChildSceneNode();
	dirNode->attachObject(dirLight);
	dirNode->setDirection(Ogre::Vector3(1, -1, 0), Ogre::Node::TS_WORLD);

	// Fill light
	Ogre::Light *fillLight = m_sceneMgr->createLight("FillLight");
	fillLight->setType(Ogre::Light::LT_DIRECTIONAL);
	fillLight->setDiffuseColour(Ogre::ColourValue(0.4f, 0.4f, 0.4f));

	Ogre::SceneNode *fillNode =
		m_sceneMgr->getRootSceneNode()->createChildSceneNode();
	fillNode->attachObject(fillLight);
	fillNode->setDirection(Ogre::Vector3(-0.5f, -1, 0.5f),
			       Ogre::Node::TS_WORLD);
}

void EditorApp::createGrid()
{
	try {
		Ogre::ManualObject *grid =
			m_sceneMgr->createManualObject("Grid");
		// Use Ogre/AxisGizmo which properly supports vertex colors
		grid->begin("Ogre/AxisGizmo",
			    Ogre::RenderOperation::OT_LINE_LIST);

		// Draw grid lines
		float size = 10.0f;
		int divisions = 20;
		float step = size * 2.0f / divisions;

		for (int i = 0; i <= divisions; ++i) {
			float pos = -size + i * step;

			// Lines along X
			grid->position(pos, 0, -size);
			grid->position(pos, 0, size);

			// Lines along Z
			grid->position(-size, 0, pos);
			grid->position(size, 0, pos);
		}

		grid->end();

		m_gridNode =
			m_sceneMgr->getRootSceneNode()->createChildSceneNode(
				"GridNode");
		m_gridNode->attachObject(grid);

		// Set initial visibility based on game mode
		m_gridNode->setVisible(m_gameMode == GameMode::Editor);
	} catch (const std::exception &e) {
		Ogre::LogManager::getSingleton().logMessage(
			"Grid creation failed: " + Ogre::String(e.what()));
	}
}

void EditorApp::createAxes()
{
	try {
		// X axis (red)
		Ogre::ManualObject *axisX =
			m_sceneMgr->createManualObject("AxisX");
		axisX->begin("Ogre/AxisGizmo",
			     Ogre::RenderOperation::OT_LINE_LIST);
		axisX->colour(1.0f, 0.0f, 0.0f);
		axisX->position(0, 0, 0);
		axisX->position(2, 0, 0);
		axisX->end();

		// Y axis (green)
		Ogre::ManualObject *axisY =
			m_sceneMgr->createManualObject("AxisY");
		axisY->begin("Ogre/AxisGizmo",
			     Ogre::RenderOperation::OT_LINE_LIST);
		axisY->colour(0.0f, 1.0f, 0.0f);
		axisY->position(0, 0, 0);
		axisY->position(0, 2, 0);
		axisY->end();

		// Z axis (blue)
		Ogre::ManualObject *axisZ =
			m_sceneMgr->createManualObject("AxisZ");
		axisZ->begin("Ogre/AxisGizmo",
			     Ogre::RenderOperation::OT_LINE_LIST);
		axisZ->colour(0.0f, 0.0f, 1.0f);
		axisZ->position(0, 0, 0);
		axisZ->position(0, 0, 2);
		axisZ->end();

		// Create axis node
		m_axisNode =
			m_sceneMgr->getRootSceneNode()->createChildSceneNode(
				"AxisNode");
		m_axisNode->attachObject(axisX);
		m_axisNode->attachObject(axisY);
		m_axisNode->attachObject(axisZ);

		// Set initial visibility based on game mode
		m_axisNode->setVisible(m_gameMode == GameMode::Editor);
	} catch (const std::exception &e) {
		Ogre::LogManager::getSingleton().logMessage(
			"Axis creation failed: " + Ogre::String(e.what()));
	}
}

bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt)
{
	if (m_gameMode == GameMode::Editor) {
		// Update camera
		if (m_camera) {
			m_camera->update(evt.timeSinceLastFrame);
		}
	} else if (m_gameMode == GameMode::Game) {
		if (m_gamePlayState == GamePlayState::Playing) {
			if (m_playerControllerSystem) {
				m_playerControllerSystem->update(
					evt.timeSinceLastFrame);
			}
		}
	}

	/* --- Visual mesh setup (must run before animation) --- */
	if (m_characterSlotSystem) {
		m_characterSlotSystem->update();
	}

	/* --- Animation / procedural generation --- */
	if (m_animationTreeSystem) {
		m_animationTreeSystem->update(evt.timeSinceLastFrame);
		if (m_behaviorTreeSystem)
			m_behaviorTreeSystem->update(evt.timeSinceLastFrame);
	}
	if (m_pathFollowingSystem) {
		m_pathFollowingSystem->update(evt.timeSinceLastFrame);
	}
	if (m_proceduralMeshSystem) {
		m_proceduralMeshSystem->update();
	}

	/* --- Static world generation (meshes + physics) --- */
	if (m_roomLayoutSystem) {
		m_roomLayoutSystem->update();
	}
	if (m_cellGridSystem) {
		m_cellGridSystem->update();
	}

	/* --- Normal debug visualization (after geometry is built) --- */
	if (m_normalDebugSystem) {
		m_normalDebugSystem->update();
	}

	/* --- NavMesh builds after static geometry is ready --- */
	if (m_navMeshSystem) {
		m_navMeshSystem->update(evt.timeSinceLastFrame);
	}

	/* --- Smart Object system (AI navigation to smart objects) --- */
	if (m_smartObjectSystem) {
		m_smartObjectSystem->update(evt.timeSinceLastFrame);
	}

	/* --- GOAP Planner system (plan generation) --- */
	if (m_goapPlannerSystem) {
		m_goapPlannerSystem->update(evt.timeSinceLastFrame);
	}

	/* --- GOAP Runner system (plan execution) --- */
	if (m_goapRunnerSystem) {
		m_goapRunnerSystem->update(evt.timeSinceLastFrame);
	}

	/* --- Actuator system (player interaction prompts) --- */
	if (m_actuatorSystem) {
		m_actuatorSystem->update(evt.timeSinceLastFrame);
	}

	/* --- Event Handler system (event-driven BTs) --- */
	if (m_eventHandlerSystem) {
		m_eventHandlerSystem->update(evt.timeSinceLastFrame);
	}

	/* --- Dynamic physics (characters after static world) --- */

	if (m_characterSystem) {
		m_characterSystem->update(evt.timeSinceLastFrame);
	}

	/* --- Buoyancy system (before physics so impulse is integrated) --- */
	if (m_buoyancySystem) {
		// Update camera position for water detection area
		if (m_camera) {
			Ogre::Vector3 cameraPos = m_camera->getPosition();
			m_buoyancySystem->setCameraPosition(cameraPos);
		}
		m_buoyancySystem->update(evt.timeSinceLastFrame);
	}

	/* --- Main physics step --- */
	if (m_physicsSystem) {
		m_physicsSystem->update(evt.timeSinceLastFrame);
	}

	/* --- Rendering support systems --- */
	if (m_sunSystem) {
		m_sunSystem->update(evt.timeSinceLastFrame);
	}
	if (m_skyboxSystem) {
		Ogre::Camera *cam = nullptr;
		if (m_sceneMgr->hasCamera("PlayerCamera"))
			cam = m_sceneMgr->getCamera("PlayerCamera");
		if (!cam && m_camera)
			cam = m_camera->getCamera();
		m_skyboxSystem->update(cam);
	}
	if (m_waterPlaneSystem) {
		Ogre::Camera *cam = nullptr;
		if (m_sceneMgr->hasCamera("PlayerCamera"))
			cam = m_sceneMgr->getCamera("PlayerCamera");
		if (!cam && m_camera)
			cam = m_camera->getCamera();
		m_waterPlaneSystem->update(evt.timeSinceLastFrame, cam);
	}
	if (m_lightSystem) {
		m_lightSystem->update();
	}
	if (m_cameraSystem) {
		m_cameraSystem->update();
	}
	if (m_lodSystem) {
		m_lodSystem->update();
	}
	if (m_staticGeometrySystem) {
		m_staticGeometrySystem->update();
	}
	if (m_proceduralTextureSystem) {
		m_proceduralTextureSystem->update();
	}
	if (m_proceduralMaterialSystem) {
		m_proceduralMaterialSystem->update();
	}

	// Reset per-frame input state
	m_gameInput.resetPerFrame();

	// Don't call base class - it crashes when iterating input listeners
	return true;
}

bool EditorApp::mouseMoved(const OgreBites::MouseMotionEvent &evt)
{
	if (m_gameMode == GameMode::Game) {
		m_gameInput.mouseMoved = true;
		m_gameInput.mouseDeltaX += evt.xrel;
		m_gameInput.mouseDeltaY += evt.yrel;
		return true;
	}

	// Skip if ImGui wants to capture mouse (for gizmo, we still want to process even if over UI)
	// But we need to update hover state
	if (m_camera && m_uiSystem) {
		Ogre::Ray mouseRay = m_camera->getMouseRay(evt.x, evt.y);
		Ogre::Vector2 delta(evt.xrel, evt.yrel);

		// Try gizmo first - gizmo handles its own visibility checks
		if (m_uiSystem->onMouseMoved(mouseRay, delta)) {
			return true;
		}
	}

	// Only pass to camera if ImGui doesn't want the mouse
	ImGuiIO &io = ImGui::GetIO();
	if (!io.WantCaptureMouse && m_camera) {
		m_camera->handleMouseMove(evt);
	}
	return true;
}

bool EditorApp::mousePressed(const OgreBites::MouseButtonEvent &evt)
{
	if (m_gameMode == GameMode::Game) {
		return true;
	}

	// Get mouse ray for gizmo interaction FIRST (before ImGui check)
	// This allows clicking on 3D gizmo even when mouse is over empty UI areas
	if (m_camera && m_uiSystem) {
		Ogre::Ray mouseRay = m_camera->getMouseRay(evt.x, evt.y);
		std::cout << "click: " << mouseRay.getOrigin() << " "
			  << mouseRay.getDirection() << std::endl;
		// Try gizmo first - if it handles the event, don't pass to camera or UI
		if (m_uiSystem->onMousePressed(mouseRay)) {
			return true;
		}
	}

	// Then check if ImGui wants the mouse
	ImGuiIO &io = ImGui::GetIO();
	if (io.WantCaptureMouse) {
		return false; // Let ImGui handle it
	}

	if (m_camera) {
		m_camera->handleMousePress(evt);
	}
	return true;
}

bool EditorApp::mouseReleased(const OgreBites::MouseButtonEvent &evt)
{
	if (m_gameMode == GameMode::Game) {
		return true;
	}

	// Handle gizmo mouse release (always process to end dragging)
	if (m_uiSystem) {
		if (m_uiSystem->onMouseReleased()) {
			return true;
		}
	}

	// Only pass to camera if ImGui doesn't want the mouse
	ImGuiIO &io = ImGui::GetIO();
	if (!io.WantCaptureMouse && m_camera) {
		m_camera->handleMouseRelease(evt);
	}
	return true;
}

bool EditorApp::keyPressed(const OgreBites::KeyboardEvent &evt)
{
	m_currentModifiers = evt.keysym.mod;

	if (m_gameMode == GameMode::Game) {
		bool pressed = true;
		switch (evt.keysym.sym) {
		case 'w':
		case 'W':
			m_gameInput.w = pressed;
			break;
		case 's':
		case 'S':
			m_gameInput.s = pressed;
			break;
		case 'a':
		case 'A':
			m_gameInput.a = pressed;
			break;
		case 'd':
		case 'D':
			m_gameInput.d = pressed;
			break;
		case OgreBites::SDLK_LSHIFT:
			m_gameInput.shift = pressed;
			break;
		case 'e':
		case 'E':
			m_gameInput.e = pressed;
			m_gameInput.ePressed = true;
			break;
		case 'f':
		case 'F':
			m_gameInput.f = pressed;
			m_gameInput.fPressed = true;
			break;
		}
		return true;
	}

	// Forward to camera for FPS movement
	if (m_camera) {
		m_camera->handleKeyboard(evt);
	}

	// Delete key to delete selected entity
	if (evt.keysym.sym == 127) { // Delete key
		// Handled in UI
	}

	// F key to focus camera on selected entity
	if (evt.keysym.sym == 'f' || evt.keysym.sym == 'F') {
		if (m_camera && m_uiSystem &&
		    m_uiSystem->getSelectedEntity().is_alive()) {
			auto entity = m_uiSystem->getSelectedEntity();
			if (entity.has<TransformComponent>()) {
				auto &transform =
					entity.get<TransformComponent>();
				m_camera->focusOn(transform.position);
			}
		}
	}

	return true;
}

bool EditorApp::keyReleased(const OgreBites::KeyboardEvent &evt)
{
	m_currentModifiers = evt.keysym.mod;

	if (m_gameMode == GameMode::Game) {
		bool pressed = false;
		switch (evt.keysym.sym) {
		case 'w':
		case 'W':
			m_gameInput.w = pressed;
			break;
		case 's':
		case 'S':
			m_gameInput.s = pressed;
			break;
		case 'a':
		case 'A':
			m_gameInput.a = pressed;
			break;
		case 'd':
		case 'D':
			m_gameInput.d = pressed;
			break;
		case OgreBites::SDLK_LSHIFT:
			m_gameInput.shift = pressed;
			break;
		case 'e':
		case 'E':
			m_gameInput.e = pressed;
			break;
		case 'f':
		case 'F':
			m_gameInput.f = pressed;
			break;
		}
		return true;
	}

	// Forward to camera for FPS movement
	if (m_camera) {
		m_camera->handleKeyboard(evt);
	}

	return true;
}

flecs::entity EditorApp::getSelectedEntity() const
{
	if (m_uiSystem) {
		return m_uiSystem->getSelectedEntity();
	}
	return flecs::entity::null();
}

DialogueSystem *EditorApp::getDialogueSystem() const
{
	return &DialogueSystem::getInstance();
}

void EditorApp::locateResources()
{
	Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
		"Characters", true);
	Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
		"LuaScripts", false);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"./lua-scripts", "FileSystem", "LuaScripts", true, true);

	/* Try multiple relative paths for characters to handle different
	 * working directories (build root vs binary subdirectory) */
	struct CharPathPair {
		const char *male;
		const char *female;
	};
	CharPathPair charPaths[] = {
		{"./characters/male", "./characters/female"},
		{"../../../characters/male", "../../../characters/female"},
		{"../../characters/male", "../../characters/female"},
		{"../characters/male", "../characters/female"},
	};

	bool added = false;
	for (const auto &pair : charPaths) {
		if (std::filesystem::exists(pair.male)) {
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
				pair.male, "FileSystem", "Characters", false, true);
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
				pair.female, "FileSystem", "Characters", false, true);
			Ogre::LogManager::getSingleton().logMessage(
				"Characters resource location added: " +
				Ogre::String(pair.male));
			added = true;
			break;
		}
	}
	if (!added) {
		Ogre::LogManager::getSingleton().logMessage(
			"WARNING: Could not find characters directory from " +
			std::filesystem::current_path().string());
	}

	OgreBites::ApplicationContext::locateResources();
}
