#include "EditorApp.hpp"
#include "systems/EditorUISystem.hpp"
#include "camera/EditorCamera.hpp"
#include "components/Transform.hpp"
#include "components/Renderable.hpp"
#include "components/EditorComponents.hpp"
#include "components/EulerUtils.hpp"

EditorApp::EditorApp()
	: OgreBites::ApplicationContext("OgreEditor")
	, mSceneMgr(nullptr)
	, mOverlaySystem(nullptr)
	, mImGuiOverlay(nullptr)
	, mIsDraggingCursor(false)
	, mCurrentModifiers(0)
	, mGridVisible(true)
	, mAxesVisible(true)
	, m3DCursorPosition(Ogre::Vector3::ZERO)
{
}

EditorApp::~EditorApp()
{
	if (mImGuiOverlay) {
		delete mImGuiOverlay;
		mImGuiOverlay = nullptr;
	}
	if (mOverlaySystem) {
		delete mOverlaySystem;
		mOverlaySystem = nullptr;
	}
}

void EditorApp::setup()
{
	// Call base class setup first
	OgreBites::ApplicationContext::setup();

	// Get root and setup scene
	Ogre::Root *root = getRoot();
	if (!root) {
		OGRE_EXCEPT(Ogre::Exception::ERR_RT_ASSERTION_FAILED,
			    "Failed to get Ogre::Root", "EditorApp::setup");
		return;
	}

	mSceneMgr = root->createSceneManager();
	if (!mSceneMgr) {
		OGRE_EXCEPT(Ogre::Exception::ERR_RT_ASSERTION_FAILED,
			    "Failed to create SceneManager",
			    "EditorApp::setup");
		return;
	}

	// Setup overlay system
	mOverlaySystem = new Ogre::OverlaySystem();
	mSceneMgr->addRenderQueueListener(mOverlaySystem);

	// Setup render window
	Ogre::RenderWindow *window = getRenderWindow();
	if (!window) {
		OGRE_EXCEPT(Ogre::Exception::ERR_RT_ASSERTION_FAILED,
			    "Failed to get RenderWindow", "EditorApp::setup");
		return;
	}

	// Setup ImGui overlay
	setupImGui();

	setupFlecs();
	setupScene();

	// Setup editor camera
	mEditorCamera = std::make_unique<EditorCamera>(mSceneMgr, window);

	// Setup UI system
	mUISystem = std::make_unique<EditorUISystem>(mWorld, mSceneMgr,
						     m3DCursorPosition);

	// Set the cursor moved callback
	mUISystem->setOnCursorMoved(
		[this](const Ogre::Vector3 &pos) { m3DCursorPosition = pos; });

	// Register input listeners
	addInputListener(this);

	// Add ImGui input listener - this is a method of ApplicationContext
	OgreBites::InputListener *imguiListener = getImGuiInputListener();
	if (imguiListener) {
		addInputListener(imguiListener);
	}
}

void EditorApp::setupFlecs()
{
	// Configure flecs
	mWorld.set_entity_range(1, 10000);

	// Register components using the helper function
	registerComponents(mWorld);

	// Create default entities
	flecs::entity rootEntity = mWorld.entity();
	rootEntity.set<EntityNameComponent>({ "Root" });

	// Create a test entity with mesh
	flecs::entity testEntity = mWorld.entity();
	testEntity.set<EntityNameComponent>({ "Test Cube" });

	// Create transform with scene node
	TransformComponent transform;
	transform.node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	transform.position = Ogre::Vector3(0, 0, 0);
	transform.applyToNode();
	testEntity.set<TransformComponent>(transform);

	// Try to load a mesh
	try {
		RenderableComponent renderable("cube.mesh");
		renderable.entity = mSceneMgr->createEntity("cube.mesh");
		testEntity.set<RenderableComponent>(renderable);

		// Attach to node
		if (testEntity.has<TransformComponent>()) {
			auto &testTransform =
				testEntity.get_mut<TransformComponent>();
			if (testTransform.node && renderable.entity) {
				testTransform.node->attachObject(
					renderable.entity);
			}
		}
	} catch (const std::exception &e) {
		Ogre::LogManager::getSingleton().logMessage(
			"Failed to load cube.mesh: " + std::string(e.what()));
	}

	// Optional: Add a system for auto-updating transforms
	mWorld.system<TransformComponent>().each(
		[](flecs::entity e, TransformComponent &transform) {
			// Auto-sync transforms if needed
			// transform.updateFromNode();
		});
}

void EditorApp::setupScene()
{
	// Setup ambient light
	mSceneMgr->setAmbientLight(Ogre::ColourValue(0.3f, 0.3f, 0.3f));

	// Create directional light with scene node
	Ogre::Light *directionalLight = mSceneMgr->createLight("MainLight");
	directionalLight->setType(Ogre::Light::LT_DIRECTIONAL);
	directionalLight->setDiffuseColour(Ogre::ColourValue(1.0f, 1.0f, 1.0f));
	directionalLight->setSpecularColour(
		Ogre::ColourValue(0.5f, 0.5f, 0.5f));

	// Create scene node for directional light and set direction
	Ogre::SceneNode *lightNode =
		mSceneMgr->getRootSceneNode()->createChildSceneNode();
	lightNode->attachObject(directionalLight);
	lightNode->setDirection(Ogre::Vector3(1, -1, 0), Ogre::Node::TS_WORLD);

	// Create a fill light from below
	Ogre::Light *fillLight = mSceneMgr->createLight("FillLight");
	fillLight->setType(Ogre::Light::LT_DIRECTIONAL);
	fillLight->setDiffuseColour(Ogre::ColourValue(0.4f, 0.4f, 0.4f));
	fillLight->setSpecularColour(Ogre::ColourValue(0.2f, 0.2f, 0.2f));

	Ogre::SceneNode *fillLightNode =
		mSceneMgr->getRootSceneNode()->createChildSceneNode();
	fillLightNode->attachObject(fillLight);
	fillLightNode->setDirection(Ogre::Vector3(-0.5f, -1.0f, 0.5f),
				    Ogre::Node::TS_WORLD);

	// Optional: Add a grid helper with named node for toggling
	try {
		Ogre::Plane plane(Ogre::Vector3::UNIT_Y, -1);
		Ogre::MeshManager::getSingleton().createPlane(
			"grid_plane",
			Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
			plane, 20, 20, 20, 20, true, 1, 5, 5,
			Ogre::Vector3::UNIT_Z);

		Ogre::Entity *gridEntity =
			mSceneMgr->createEntity("grid_plane");

		// Try to set a grid material if available
		if (Ogre::MaterialManager::getSingleton().resourceExists(
			    "GridMaterial")) {
			gridEntity->setMaterialName("GridMaterial");
		}

		// Create node with a name for easy access
		Ogre::SceneNode *gridNode =
			mSceneMgr->getRootSceneNode()->createChildSceneNode(
				"GridNode");
		gridNode->attachObject(gridEntity);
		gridNode->setPosition(0, -1, 0);

		Ogre::LogManager::getSingleton().logMessage(
			"Grid created successfully");
	} catch (const std::exception &e) {
		Ogre::LogManager::getSingleton().logMessage(
			"Grid plane creation failed: " + std::string(e.what()));
	}

	// Add a simple axis helper
	createAxisHelper();
}

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

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

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

		// Create named node for easy access
		Ogre::SceneNode *axisNode =
			mSceneMgr->getRootSceneNode()->createChildSceneNode(
				"AxisNode");
		axisNode->attachObject(axisX);
		axisNode->attachObject(axisY);
		axisNode->attachObject(axisZ);

	} catch (const std::exception &e) {
		Ogre::LogManager::getSingleton().logMessage(
			"Axis helper creation failed: " +
			std::string(e.what()));
	}
}

void EditorApp::setupImGui()
{
	// Create ImGui overlay using Ogre's built-in integration
	mImGuiOverlay = new Ogre::ImGuiOverlay();

	// Show the overlay - this enables it in the overlay system
	mImGuiOverlay->show();

	// Setup ImGui style
	ImGui::StyleColorsDark();

	// Configure ImGui IO (standard features only)
	ImGuiIO &io = ImGui::GetIO();
	io.ConfigWindowsMoveFromTitleBarOnly = true;

	Ogre::LogManager::getSingleton().logMessage(
		"ImGui overlay initialized");
}

bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt)
{
	// Update camera
	if (mEditorCamera) {
		mEditorCamera->update(evt.timeSinceLastFrame);
	}

	// Start ImGui frame
	if (mImGuiOverlay) {
		mImGuiOverlay->NewFrame();
	}

	// Update UI system (this will render all ImGui windows)
	if (mUISystem) {
		mUISystem->update();
	}

	return OgreBites::ApplicationContext::frameRenderingQueued(evt);
}

bool EditorApp::mouseMoved(const OgreBites::MouseMotionEvent &evt)
{
	if (mEditorCamera) {
		mEditorCamera->handleMouseMove(evt);
	}
	return true;
}

bool EditorApp::mousePressed(const OgreBites::MouseButtonEvent &evt)
{
	if (mEditorCamera) {
		mEditorCamera->handleMousePress(evt);

		// Check for Ctrl key using current modifier state
		if (evt.button == OgreBites::BUTTON_LEFT && isCtrlPressed()) {
			Ogre::Viewport *vp =
				mEditorCamera->getCamera()->getViewport();
			if (vp) {
				float screenX = static_cast<float>(evt.x) /
						static_cast<float>(
							vp->getActualWidth());
				float screenY = static_cast<float>(evt.y) /
						static_cast<float>(
							vp->getActualHeight());

				Ogre::Vector3 cursorPos =
					mEditorCamera->getMouseRay(screenX,
								   screenY);
				m3DCursorPosition = cursorPos;
			}
		}
	}
	return true;
}

bool EditorApp::mouseReleased(const OgreBites::MouseButtonEvent &evt)
{
	if (mEditorCamera) {
		mEditorCamera->handleMouseRelease(evt);
	}
	return true;
}

bool EditorApp::keyPressed(const OgreBites::KeyboardEvent &evt)
{
	if (mEditorCamera) {
		mEditorCamera->handleKeyboard(evt);
	}

	// Update current modifiers from the event
	mCurrentModifiers = evt.keysym.mod;

	// Handle delete key
	if (evt.keysym.sym == OgreBites::SDLK_DELETE) {
		if (mUISystem && mSelectedEntity) {
			// Clean up scene node
			if (mSelectedEntity.has<TransformComponent>()) {
				auto &transform =
					mSelectedEntity
						.get_mut<TransformComponent>();
				if (transform.node) {
					mSceneMgr->destroySceneNode(
						transform.node);
					transform.node = nullptr;
				}
			}

			// Clean up renderable
			if (mSelectedEntity.has<RenderableComponent>()) {
				auto &renderable =
					mSelectedEntity
						.get_mut<RenderableComponent>();
				if (renderable.entity) {
					mSceneMgr->destroyEntity(
						renderable.entity);
					renderable.entity = nullptr;
				}
			}

			mSelectedEntity.destruct();
			mUISystem->setSelectedEntity(flecs::entity::null());
		}
	}

	// Handle F5 for reloading resources
	if (evt.keysym.sym == OgreBites::SDLK_F5) {
		reloadResources();
	}

	// Handle F3 for showing/hiding grid
	if (evt.keysym.sym == OgreBites::SDLK_F3) {
		toggleGrid();
	}

	// Handle F4 for showing/hiding axes
	if (evt.keysym.sym == OgreBites::SDLK_F4) {
		toggleAxes();
	}

	return true;
}

bool EditorApp::keyReleased(const OgreBites::KeyboardEvent &evt)
{
	// Update current modifiers from the event
	mCurrentModifiers = evt.keysym.mod;

	return true;
}

void EditorApp::toggleGrid()
{
	// Use getChild and cast to SceneNode
	Ogre::Node *node = mSceneMgr->getRootSceneNode()->getChild("GridNode");
	if (node) {
		Ogre::SceneNode *gridNode =
			static_cast<Ogre::SceneNode *>(node);
		mGridVisible = !mGridVisible;
		gridNode->setVisible(mGridVisible);
		Ogre::LogManager::getSingleton().logMessage(
			"Grid visibility: " +
			std::string(mGridVisible ? "ON" : "OFF"));
	} else {
		Ogre::LogManager::getSingleton().logMessage(
			"Grid node not found");
	}
}

void EditorApp::toggleAxes()
{
	// Use getChild and cast to SceneNode
	Ogre::Node *node = mSceneMgr->getRootSceneNode()->getChild("AxisNode");
	if (node) {
		Ogre::SceneNode *axisNode =
			static_cast<Ogre::SceneNode *>(node);
		mAxesVisible = !mAxesVisible;
		axisNode->setVisible(mAxesVisible);
		Ogre::LogManager::getSingleton().logMessage(
			"Axes visibility: " +
			std::string(mAxesVisible ? "ON" : "OFF"));
	} else {
		Ogre::LogManager::getSingleton().logMessage(
			"Axis node not found");
	}
}

void EditorApp::reloadResources()
{
	// Reload all materials
	Ogre::LogManager::getSingleton().logMessage("Reloading materials...");
	Ogre::MaterialManager::getSingleton().reloadAll();

	Ogre::LogManager::getSingleton().logMessage("Resources reloaded");
}
