#include "PhysicsColliderEditor.hpp"
#include "../components/RigidBody.hpp"
#include <imgui.h>
#include <OgreResourceGroupManager.h>
#include <algorithm>

PhysicsColliderEditor::PhysicsColliderEditor(Ogre::SceneManager *sceneMgr)
	: m_sceneMgr(sceneMgr)
{
}

void PhysicsColliderEditor::scanMeshFiles()
{
	if (m_meshesScanned)
		return;

	m_meshFiles.clear();

	// Get all resource groups
	Ogre::ResourceGroupManager &rgm = Ogre::ResourceGroupManager::getSingleton();
	Ogre::StringVector groups = rgm.getResourceGroups();

	for (const auto &group : groups) {
		// Get file info for this group
		Ogre::FileInfoListPtr fileList = rgm.findResourceFileInfo(group, "*");

		if (fileList) {
			for (const auto &fileInfo : *fileList) {
				const std::string &filename = fileInfo.filename;
				// Check for supported extensions
				if (filename.size() > 5) {
					std::string ext = filename.substr(filename.find_last_of('.') + 1);
					// Convert to lowercase
					std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
					
					if (ext == "mesh" || ext == "glb" || ext == "gltf") {
						m_meshFiles.push_back(filename);
					}
				}
			}
		}
	}

	// Sort and remove duplicates
	std::sort(m_meshFiles.begin(), m_meshFiles.end());
	m_meshFiles.erase(std::unique(m_meshFiles.begin(), m_meshFiles.end()),
			  m_meshFiles.end());

	m_meshesScanned = true;
}

bool PhysicsColliderEditor::matchesSearch(const std::string &meshName,
					  const std::string &search)
{
	if (search.empty())
		return true;

	std::string lowerMesh = meshName;
	std::string lowerSearch = search;
	std::transform(lowerMesh.begin(), lowerMesh.end(), lowerMesh.begin(),
		       ::tolower);
	std::transform(lowerSearch.begin(), lowerSearch.end(), lowerSearch.begin(),
		       ::tolower);

	return lowerMesh.find(lowerSearch) != std::string::npos;
}

void PhysicsColliderEditor::markParentRigidBodiesDirty(flecs::entity entity)
{
	// Mark this entity's collider as dirty
	if (entity.has<PhysicsColliderComponent>()) {
		auto &collider = entity.get_mut<PhysicsColliderComponent>();
		collider.markDirty();
	}

	// Check if parent has RigidBody component and mark it dirty
	flecs::entity parent = entity.parent();
	if (parent.is_valid() && parent.is_alive() && parent.has<RigidBodyComponent>()) {
		auto &rigidBody = parent.get_mut<RigidBodyComponent>();
		rigidBody.markDirty();
	}
}

void PhysicsColliderEditor::renderMeshBrowser(PhysicsColliderComponent &collider)
{
	if (ImGui::BeginPopupModal("Mesh Browser (Collider)", &m_showMeshBrowser,
				   ImGuiWindowFlags_AlwaysAutoResize)) {
		ImGui::Text("Search:");
		ImGui::SameLine();
		ImGui::InputText("##search", m_searchBuffer, sizeof(m_searchBuffer));

		ImGui::Separator();

		// Show filtered mesh list
		ImGui::BeginChild("MeshList", ImVec2(400, 300), true);

		std::string searchStr(m_searchBuffer);

		for (const auto &meshName : m_meshFiles) {
			if (!matchesSearch(meshName, searchStr))
				continue;

			// Highlight current selection
			bool isCurrent = (collider.meshName == meshName);
			if (isCurrent) {
				ImGui::PushStyleColor(ImGuiCol_Text,
						      ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
			}

			if (ImGui::Selectable(meshName.c_str(), isCurrent)) {
				collider.meshName = meshName;
			}

			if (isCurrent) {
				ImGui::PopStyleColor();
			}
		}

		if (m_meshFiles.empty()) {
			ImGui::TextDisabled("No mesh files found");
		}

		ImGui::EndChild();

		ImGui::Separator();

		// Show count
		int visibleCount = 0;
		for (const auto &meshName : m_meshFiles) {
			if (matchesSearch(meshName, searchStr))
				visibleCount++;
		}
		ImGui::Text("Showing %d of %zu meshes", visibleCount, m_meshFiles.size());

		// Buttons
		if (ImGui::Button("Select", ImVec2(120, 0))) {
			m_showMeshBrowser = false;
			ImGui::CloseCurrentPopup();
		}

		ImGui::SameLine();

		if (ImGui::Button("Cancel", ImVec2(120, 0))) {
			m_showMeshBrowser = false;
			ImGui::CloseCurrentPopup();
		}

		ImGui::SameLine();

		if (ImGui::Button("Refresh", ImVec2(120, 0))) {
			m_meshesScanned = false;
			scanMeshFiles();
		}

		ImGui::EndPopup();
	}
}

bool PhysicsColliderEditor::renderComponent(flecs::entity entity,
					    PhysicsColliderComponent &collider)
{
	bool modified = false;

	if (ImGui::CollapsingHeader("Physics Collider",
				    ImGuiTreeNodeFlags_DefaultOpen)) {
		ImGui::Indent();

		// Shape type selector
		const char *shapeTypes[] = {
			"Box", "Sphere", "Capsule", "Cylinder", "Mesh", "Convex Hull"
		};
		int currentType = static_cast<int>(collider.shapeType);
		if (ImGui::Combo("Shape Type", &currentType, shapeTypes,
				 IM_ARRAYSIZE(shapeTypes))) {
			collider.shapeType =
				static_cast<PhysicsColliderComponent::ShapeType>(
					currentType);
			collider.markDirty();
			markParentRigidBodiesDirty(entity);
			modified = true;
		}

		// Shape-specific parameters
		switch (collider.shapeType) {
		case PhysicsColliderComponent::ShapeType::Box: {
			float extents[3] = { collider.parameters.x,
					     collider.parameters.y,
					     collider.parameters.z };
			if (ImGui::DragFloat3("Half Extents", extents, 0.01f,
					      0.001f)) {
				collider.parameters =
					Ogre::Vector3(extents[0], extents[1],
						    extents[2]);
				collider.markDirty();
				markParentRigidBodiesDirty(entity);
				modified = true;
			}
			break;
		}
		case PhysicsColliderComponent::ShapeType::Sphere: {
			if (ImGui::DragFloat("Radius", &collider.radius, 0.01f,
					     0.001f)) {
				collider.markDirty();
				markParentRigidBodiesDirty(entity);
				modified = true;
			}
			break;
		}
		case PhysicsColliderComponent::ShapeType::Capsule:
		case PhysicsColliderComponent::ShapeType::Cylinder: {
			if (ImGui::DragFloat("Half Height", &collider.halfHeight, 0.01f,
					     0.001f)) {
				collider.markDirty();
				markParentRigidBodiesDirty(entity);
				modified = true;
			}
			if (ImGui::DragFloat("Radius", &collider.radius, 0.01f,
					     0.001f)) {
				collider.markDirty();
				markParentRigidBodiesDirty(entity);
				modified = true;
			}
			break;
		}
		case PhysicsColliderComponent::ShapeType::Mesh:
		case PhysicsColliderComponent::ShapeType::ConvexHull: {
			char meshName[256];
			std::strncpy(meshName, collider.meshName.c_str(),
				     sizeof(meshName) - 1);
			meshName[sizeof(meshName) - 1] = '\0';
			if (ImGui::InputText("Mesh Name", meshName,
					     sizeof(meshName))) {
				collider.meshName = meshName;
				collider.markDirty();
				markParentRigidBodiesDirty(entity);
				modified = true;
			}

			ImGui::SameLine();
			if (ImGui::Button("Browse...")) {
				scanMeshFiles();
				m_showMeshBrowser = true;
				m_searchBuffer[0] = '\0';
			}

			// Mesh Browser Popup
			if (m_showMeshBrowser) {
				ImGui::OpenPopup("Mesh Browser (Collider)");
			}

			renderMeshBrowser(collider);
			break;
		}
		}

		// Offset from parent
		float offset[3] = { collider.offset.x, collider.offset.y,
				    collider.offset.z };
		if (ImGui::DragFloat3("Position Offset", offset, 0.01f)) {
			collider.offset =
				Ogre::Vector3(offset[0], offset[1], offset[2]);
			collider.markDirty();
			markParentRigidBodiesDirty(entity);
			modified = true;
		}

		// Rotation offset
		float yaw = 0.0f, pitch = 0.0f, roll = 0.0f;
		if (ImGui::TreeNode("Rotation Offset")) {
			Ogre::Radian y = collider.rotationOffset.getYaw();
			Ogre::Radian p = collider.rotationOffset.getPitch();
			Ogre::Radian r = collider.rotationOffset.getRoll();
			yaw = y.valueDegrees();
			pitch = p.valueDegrees();
			roll = r.valueDegrees();
			bool rotModified = false;
			if (ImGui::DragFloat("Yaw", &yaw, 0.5f)) rotModified = true;
			if (ImGui::DragFloat("Pitch", &pitch, 0.5f)) rotModified = true;
			if (ImGui::DragFloat("Roll", &roll, 0.5f)) rotModified = true;
			if (rotModified) {
				Ogre::Quaternion q1(Ogre::Degree(yaw),
						    Ogre::Vector3::UNIT_Y);
				Ogre::Quaternion q2(Ogre::Degree(pitch),
						    Ogre::Vector3::UNIT_X);
				Ogre::Quaternion q3(Ogre::Degree(roll),
						    Ogre::Vector3::UNIT_Z);
				collider.rotationOffset = q1 * q2 * q3;
				collider.markDirty();
				markParentRigidBodiesDirty(entity);
				modified = true;
			}
			ImGui::TreePop();
		}

		ImGui::Unindent();
	}

	return modified;
}
