#include "CameraSystem.hpp"
#include "../components/Camera.hpp"
#include "../components/Transform.hpp"
#include <OgreLogManager.h>

EditorCameraSystem::EditorCameraSystem(flecs::world& world, Ogre::SceneManager* sceneMgr,
                                         Ogre::RenderWindow* renderWindow)
	: m_world(world)
	, m_sceneMgr(sceneMgr)
	, m_renderWindow(renderWindow)
	, m_cameraQuery(world.query<CameraComponent, TransformComponent>())
{
}

EditorCameraSystem::~EditorCameraSystem() = default;

void EditorCameraSystem::update()
{
	m_cameraQuery.each([&](flecs::entity entity, CameraComponent& camera, TransformComponent& transform) {
		if (camera.needsRebuild || !camera.camera) {
			updateCamera(entity, camera, transform);
		}
		
		// Update preview if needed
		if (camera.showPreview && camera.needsPreviewUpdate && camera.previewTarget) {
			camera.previewTarget->update();
			camera.needsPreviewUpdate = false;
		}
	});
}

void EditorCameraSystem::processAllCameras()
{
	m_cameraQuery.each([&](flecs::entity entity, CameraComponent& camera, TransformComponent& transform) {
		updateCamera(entity, camera, transform);
	});
}

Ogre::Camera* EditorCameraSystem::getActiveCamera() const
{
	// Return the viewport camera if set, otherwise find first available
	if (m_viewportCamera) {
		return m_viewportCamera;
	}
	
	Ogre::Camera* result = nullptr;
	m_cameraQuery.each([&](flecs::entity entity, CameraComponent& camera, TransformComponent& transform) {
		if (!result && camera.camera) {
			result = camera.camera;
		}
	});
	return result;
}

void EditorCameraSystem::setViewportCamera(Ogre::Camera* camera)
{
	// Get the main viewport from the stored render window
	if (!m_renderWindow || m_renderWindow->getNumViewports() == 0) return;
	
	Ogre::Viewport* viewport = m_renderWindow->getViewport(0);
	
	// Store original camera on first switch
	if (!m_originalCamera) {
		m_originalCamera = viewport->getCamera();
	}
	
	// Set new camera
	m_viewportCamera = camera;
	viewport->setCamera(camera);
	
	Ogre::LogManager::getSingleton().logMessage(
		"CameraSystem: Switched viewport to camera '" + 
		(camera ? camera->getName() : "default") + "'");
}

void EditorCameraSystem::restoreOriginalCamera()
{
	if (m_originalCamera) {
		setViewportCamera(m_originalCamera);
		m_originalCamera = nullptr;
		m_viewportCamera = nullptr;
	}
}

void EditorCameraSystem::updateCamera(flecs::entity entity, CameraComponent& camera, 
                                      TransformComponent& transform)
{
	// Remove existing camera if any
	if (camera.camera) {
		removeCamera(camera);
	}

	// Need a valid scene node to attach the camera
	if (!transform.node) {
		Ogre::LogManager::getSingleton().logMessage(
			"CameraSystem: Cannot create camera - entity has no SceneNode");
		return;
	}

	// Create unique name for the camera
	std::string cameraName = "Camera_" + std::to_string(entity.id());

	// Create the Ogre camera
	camera.camera = m_sceneMgr->createCamera(cameraName);
	
	// Attach to the entity's scene node
	transform.node->attachObject(camera.camera);

	// Set projection type
	if (camera.orthographic) {
		camera.camera->setProjectionType(Ogre::PT_ORTHOGRAPHIC);
		camera.camera->setOrthoWindow(camera.orthoWidth, camera.orthoHeight);
	} else {
		camera.camera->setProjectionType(Ogre::PT_PERSPECTIVE);
		camera.camera->setFOVy(Ogre::Degree(camera.fovY));
	}

	// Set clip distances
	camera.camera->setNearClipDistance(camera.nearClip);
	camera.camera->setFarClipDistance(camera.farClip);

	// Set aspect ratio
	camera.camera->setAspectRatio(camera.aspectRatio);

	camera.needsRebuild = false;
	
	Ogre::LogManager::getSingleton().logMessage(
		"CameraSystem: Created " + 
		std::string(camera.orthographic ? "orthographic" : "perspective") +
		" camera '" + cameraName + "'");
}

void EditorCameraSystem::removeCamera(CameraComponent& camera)
{
	if (camera.camera) {
		// Clean up preview resources
		if (camera.previewTarget) {
			camera.previewTarget->removeAllListeners();
			camera.previewTarget = nullptr;
		}
		if (camera.previewTexture) {
			Ogre::TextureManager::getSingleton().remove(camera.previewTexture->getName());
			camera.previewTexture.reset();
		}
		
		// Detach from parent node first
		Ogre::SceneNode* parent = camera.camera->getParentSceneNode();
		if (parent) {
			parent->detachObject(camera.camera);
		}
		
		m_sceneMgr->destroyCamera(camera.camera);
		camera.camera = nullptr;
	}
}
