#include "EditorCamera.hpp"
#include <OgreViewport.h>

EditorCamera::EditorCamera(Ogre::SceneManager *sceneMgr,
			   Ogre::RenderWindow *window)
	: m_sceneMgr(sceneMgr)
	, m_camera(nullptr)
	, m_cameraNode(nullptr)
	, m_targetNode(nullptr)
	, m_position(0, 5, 15)
	, m_target(0, 0, 0)
	, m_distance(15.0f)
	, m_yaw(0.0f)
	, m_pitch(-20.0f)
	, m_rotating(false)
	, m_panning(false)
	, m_fpsMode(false)
	, m_lastMouseX(0)
	, m_lastMouseY(0)
	, m_keyW(false)
	, m_keyS(false)
	, m_keyA(false)
	, m_keyD(false)
	, m_keyQ(false)
	, m_keyE(false)
{
	// Create camera
	m_camera = sceneMgr->createCamera("EditorCamera");
	m_camera->setNearClipDistance(0.1f);
	m_camera->setFarClipDistance(1000.0f);
	m_camera->setAutoAspectRatio(true);

	// Create camera node
	m_cameraNode = sceneMgr->getRootSceneNode()->createChildSceneNode(
		"EditorCameraNode");
	m_cameraNode->attachObject(m_camera);

	// Create target node
	m_targetNode = sceneMgr->getRootSceneNode()->createChildSceneNode(
		"EditorCameraTarget");

	// Setup viewport
	Ogre::Viewport *vp = window->addViewport(m_camera);
	vp->setBackgroundColour(Ogre::ColourValue(0.1f, 0.1f, 0.1f));

	// Setup CameraMan for proper camera control (no roll)
	m_cameraMan = std::make_unique<OgreBites::CameraMan>(m_cameraNode);
	m_cameraMan->setStyle(OgreBites::CS_ORBIT);
	m_cameraMan->setTarget(m_targetNode);
	m_cameraMan->setYawPitchDist(Ogre::Degree(m_yaw), Ogre::Degree(m_pitch),
				     m_distance);

	updateCameraPosition();
}

EditorCamera::~EditorCamera() = default;

void EditorCamera::update(float deltaTime)
{
	if (m_fpsMode) {
		updateFPSMovement(deltaTime);
	}
	// CameraMan handles the positioning
	m_cameraMan->setYawPitchDist(Ogre::Degree(m_yaw), Ogre::Degree(m_pitch),
				     m_distance);
	m_cameraMan->frameRendered(Ogre::FrameEvent{ deltaTime });
}

void EditorCamera::updateFPSMovement(float deltaTime)
{
	// Get camera's forward and right vectors from CameraMan's derived orientation
	Ogre::Quaternion orientation = m_camera->getDerivedOrientation();
	Ogre::Vector3 forward = orientation * Ogre::Vector3::UNIT_Z;
	Ogre::Vector3 right = orientation * Ogre::Vector3::UNIT_X;
	Ogre::Vector3 up = Ogre::Vector3::UNIT_Y; // World up for Q/E

	// Flatten forward vector to horizontal plane for WSAD movement
	Ogre::Vector3 forwardHorizontal =
		Ogre::Vector3(forward.x, 0, forward.z);
	if (forwardHorizontal.squaredLength() > 0.0001f) {
		forwardHorizontal.normalise();
	}

	Ogre::Vector3 movement = Ogre::Vector3::ZERO;

	// WSAD movement (horizontal plane)
	if (m_keyW)
		movement -= forwardHorizontal;
	if (m_keyS)
		movement += forwardHorizontal;
	if (m_keyA)
		movement -= right;
	if (m_keyD)
		movement += right;

	// Q/E for vertical movement
	if (m_keyQ)
		movement -= up;
	if (m_keyE)
		movement += up;

	// Apply movement
	if (movement.squaredLength() > 0.0001f) {
		movement.normalise();
		m_target += movement * FPS_SPEED * deltaTime;
		m_targetNode->setPosition(m_target);
		m_cameraMan->setTarget(m_targetNode);
	}
}

void EditorCamera::handleMouseMove(const OgreBites::MouseMotionEvent &evt)
{
	int dx = evt.x - m_lastMouseX;
	int dy = evt.y - m_lastMouseY;
	m_lastMouseX = evt.x;
	m_lastMouseY = evt.y;

	if (m_rotating) {
		m_yaw -= dx * ROTATION_SPEED;
		m_pitch -= dy * ROTATION_SPEED;

		// Clamp pitch
		if (m_pitch > 89.0f)
			m_pitch = 89.0f;
		if (m_pitch < -89.0f)
			m_pitch = -89.0f;
	}

	if (m_panning) {
		// Get right and up vectors from camera's orientation
		Ogre::Quaternion orientation =
			m_camera->getDerivedOrientation();
		Ogre::Vector3 right = orientation * Ogre::Vector3::UNIT_X;
		Ogre::Vector3 up = orientation * Ogre::Vector3::UNIT_Y;

		Ogre::Vector3 pan = right * (-dx * PAN_SPEED * m_distance) +
				    up * (dy * PAN_SPEED * m_distance);

		m_target += pan;
		m_targetNode->setPosition(m_target);
		m_cameraMan->setTarget(m_targetNode);
	}
}

void EditorCamera::handleMousePress(const OgreBites::MouseButtonEvent &evt)
{
	m_lastMouseX = evt.x;
	m_lastMouseY = evt.y;

	if (evt.button == OgreBites::BUTTON_RIGHT) {
		m_rotating = true;
		m_fpsMode = true; // Enable FPS mode when right mouse is held
	} else if (evt.button == OgreBites::BUTTON_MIDDLE) {
		m_panning = true;
	}
}

void EditorCamera::handleMouseRelease(const OgreBites::MouseButtonEvent &evt)
{
	if (evt.button == OgreBites::BUTTON_RIGHT) {
		m_rotating = false;
		m_fpsMode =
			false; // Disable FPS mode when right mouse is released
		// Reset key states
		m_keyW = m_keyS = m_keyA = m_keyD = m_keyQ = m_keyE = false;
	} else if (evt.button == OgreBites::BUTTON_MIDDLE) {
		m_panning = false;
	}
}

void EditorCamera::handleKeyboard(const OgreBites::KeyboardEvent &evt)
{
	// Track key states for FPS movement
	bool pressed = (evt.type == OgreBites::KEYDOWN);

	switch (evt.keysym.sym) {
	case 'w':
	case 'W':
		m_keyW = pressed;
		break;
	case 's':
	case 'S':
		m_keyS = pressed;
		break;
	case 'a':
	case 'A':
		m_keyA = pressed;
		break;
	case 'd':
	case 'D':
		m_keyD = pressed;
		break;
	case 'q':
	case 'Q':
		m_keyQ = pressed;
		break;
	case 'e':
	case 'E':
		m_keyE = pressed;
		break;
	}
}

void EditorCamera::focusOn(const Ogre::Vector3 &point)
{
	m_target = point;
	m_targetNode->setPosition(m_target);
	m_cameraMan->setTarget(m_targetNode);
}

void EditorCamera::setPosition(const Ogre::Vector3 &pos)
{
	m_target = pos;
	m_targetNode->setPosition(m_target);
	m_cameraMan->setTarget(m_targetNode);
}

Ogre::Ray EditorCamera::getMouseRay(float screenX, float screenY) const
{
	// Convert pixel coordinates to normalized viewport coordinates (0-1)
	Ogre::Viewport *viewport = m_camera->getViewport();
	if (!viewport)
		return Ogre::Ray();

	float normX = screenX / viewport->getActualWidth();
	float normY = screenY / viewport->getActualHeight();

	return m_camera->getCameraToViewportRay(normX, normY);
}

void EditorCamera::updateCameraPosition()
{
	// CameraMan handles the positioning
	m_targetNode->setPosition(m_target);
	m_cameraMan->setYawPitchDist(Ogre::Degree(m_yaw), Ogre::Degree(m_pitch),
				     m_distance);
}
