using System;
using System.Collections.Generic;
using System.Numerics;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using BepuPhysics;
using BepuPhysics.Collidables;
using HelixToolkit.Wpf;
using System.Linq;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows;
using System.Windows.Media.Imaging;

namespace CtrEditor.Simulacion
{
    /// <summary>
    /// Estructura para almacenar las dimensiones de las formas para detectar cambios
    /// </summary>
    public struct ShapeDimensions
    {
        public float Width { get; set; }
        public float Height { get; set; }
        public float Length { get; set; }
        public float Radius { get; set; }
        public int ShapeType { get; set; }

        // ✅ NUEVO: Parámetros específicos para curvas
        public float InnerRadius { get; set; }
        public float OuterRadius { get; set; }
        public float StartAngle { get; set; }
        public float EndAngle { get; set; }
        
        // ✅ NUEVO: Estado de movimiento para transportes
        public bool IsMoving { get; set; }

        public bool Equals(ShapeDimensions other)
        {
            return Math.Abs(Width - other.Width) < 0.001f &&
                   Math.Abs(Height - other.Height) < 0.001f &&
                   Math.Abs(Length - other.Length) < 0.001f &&
                   Math.Abs(Radius - other.Radius) < 0.001f &&
                   Math.Abs(InnerRadius - other.InnerRadius) < 0.001f &&
                   Math.Abs(OuterRadius - other.OuterRadius) < 0.001f &&
                   Math.Abs(StartAngle - other.StartAngle) < 0.001f &&
                   Math.Abs(EndAngle - other.EndAngle) < 0.001f &&
                   ShapeType == other.ShapeType &&
                   IsMoving == other.IsMoving;
        }
    }

    /// <summary>
    /// Manager que sincroniza el mundo 3D de BEPU con la visualización HelixToolkit
    /// Usa simBase como clave única para evitar problemas de reutilización de BodyHandle
    /// 
    /// ✅ NUEVO: Funcionalidad de Debug para Triángulos
    /// Para activar la visualización de triángulos individuales de curvas:
    /// 
    /// // Activar modo debug
    /// visualizationManager.SetDebugTrianglesMode(true);
    /// 
    /// // Desactivar modo debug (volver a superficie continua)
    /// visualizationManager.SetDebugTrianglesMode(false);
    /// 
    /// // Verificar si está activo
    /// bool isDebugActive = visualizationManager.IsDebugTrianglesModeEnabled();
    /// 
    /// En modo debug:
    /// - Cada triángulo de BEPU se muestra separado con un pequeño offset
    /// - Los triángulos tienen bordes wireframe para mejor visualización
    /// - Se usa un material naranja semi-transparente para distinguir del modo normal
    /// - Se muestran mensajes de debug en la consola con información detallada
    /// </summary>
    public class BEPUVisualization3DManager
    {
        private HelixViewport3D viewport3D;
        private SimulationManagerBEPU simulationManager;
        private Dictionary<simBase, ModelVisual3D> simBaseToModelMap;
        private Dictionary<simBase, ShapeDimensions> lastKnownDimensions;
        
        // ✅ NUEVO: Diccionario para gestionar animaciones activas
        private Dictionary<simBase, Storyboard> activeAnimations;

        // ✅ NUEVO: Referencia al modelo visual de la imagen de fondo
        private ModelVisual3D? _backgroundImageVisual;

        // ✅ CORREGIDO: Flag de debug para mostrar triángulos individuales de curvas (true temporalmente para verificar)
        public static bool DebugShowIndividualTriangles { get; set; } = false;

        public HelixViewport3D Viewport3D
        {
            get => viewport3D;
            set => viewport3D = value;
        }

        public BEPUVisualization3DManager(HelixViewport3D viewport, SimulationManagerBEPU simManager)
        {
            if (viewport == null)
                throw new ArgumentNullException(nameof(viewport), "HelixViewport3D cannot be null");

            if (simManager == null)
                throw new ArgumentNullException(nameof(simManager), "SimulationManagerBEPU cannot be null");

            viewport3D = viewport;
            simulationManager = simManager;
            simBaseToModelMap = new Dictionary<simBase, ModelVisual3D>();
            lastKnownDimensions = new Dictionary<simBase, ShapeDimensions>();
            activeAnimations = new Dictionary<simBase, Storyboard>();

            InitializeViewport();
        }

        private void InitializeViewport()
        {
            if (viewport3D == null)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Init] ERROR: viewport3D is null");
                return;
            }

            try
            {
                // Configurar la cámara usando el estado guardado
                LoadCameraState();

                // Agregar luces
                var directionalLight = new DirectionalLight
                {
                    Color = Color.FromRgb(150, 150, 150), // Luz direccional menos intensa
                    Direction = new Vector3D(-0.2, -0.3, -1) // Ligeramente angulada para evitar reflejos directos
                };
                viewport3D.Children.Add(new ModelVisual3D { Content = directionalLight });

                var ambientLight = new AmbientLight
                {
                    Color = Color.FromRgb(120, 120, 120) // Más luz ambiente para suavizar sombras
                };
                viewport3D.Children.Add(new ModelVisual3D { Content = ambientLight });

                // Suscribirse a eventos para detectar cambios en la cámara
                SubscribeToCameraEvents();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Init] ERROR initializing viewport: {ex.Message}");
            }
        }

        /// <summary>
        /// Sincroniza usando los objetos simBase del SimulationManager como fuente de verdad
        /// Esto garantiza que cada objeto único tiene su propia visualización
        /// </summary>
        public void SynchronizeWorld()
        {
            // Validaciones críticas de null
            if (simulationManager == null)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Sync] ERROR: simulationManager is null");
                return;
            }

            if (simulationManager.simulation == null)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Sync] ERROR: simulation is null");
                return;
            }

            if (viewport3D == null)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Sync] ERROR: viewport3D is null");
                return;
            }

            // Limpiar objetos que ya no existen
            CleanupRemovedObjects();

            // Sincronizar cada objeto simBase
            foreach (var simObj in simulationManager.Cuerpos)
            {
                // ✅ NUEVO: Caso especial para simBarrera (no tiene BodyHandle físico)
                if (simObj is simBarrera barrera)
                {
                    //System.Diagnostics.Debug.WriteLine($"[3D Sync] 🔍 Procesando simBarrera...");
                    if (simBaseToModelMap.ContainsKey(simObj))
                    {
                        //System.Diagnostics.Debug.WriteLine($"[3D Sync] 🔄 Actualizando visualización existente de barrera");
                        // Actualizar visualización existente de barrera
                        UpdateBarreraVisualization(barrera);
                    }
                    else
                    {
                        //System.Diagnostics.Debug.WriteLine($"[3D Sync] 🆕 Creando nueva visualización de barrera");
                        // Crear nueva visualización de barrera
                        CreateBarreraVisualization(barrera);
                    }
                    continue;
                }

                // Para objetos con BodyHandle físico
                if (simObj == null || !simulationManager.simulation.Bodies.BodyExists(simObj.BodyHandle))
                    continue;

                if (simBaseToModelMap.ContainsKey(simObj))
                {
                    // Actualizar visualización existente
                    UpdateVisualizationFromSimBase(simObj);
                }
                else
                {
                    // Crear nueva visualización
                    CreateVisualizationFromSimBase(simObj);
                }
            }
        }

        private void CleanupRemovedObjects()
        {
            if (simulationManager?.Cuerpos == null || viewport3D == null)
                return;

            var objectsToRemove = new List<simBase>();

            try
            {
                foreach (var kvp in simBaseToModelMap)
                {
                    var simObj = kvp.Key;
                    var model = kvp.Value;

                    // ✅ NUEVO: Caso especial para simBarrera (no tiene BodyHandle físico)
                    if (simObj is simBarrera)
                    {
                        // Verificar si la barrera aún está en la lista de cuerpos activos
                        if (!simulationManager.Cuerpos.Contains(simObj))
                        {
                            if (model != null)
                            {
                                viewport3D.Children.Remove(model);
                            }
                            objectsToRemove.Add(simObj);
                        }
                    }
                    else
                    {
                        // Para objetos con BodyHandle físico
                        if (!simulationManager.Cuerpos.Contains(simObj) ||
                            !simulationManager.simulation.Bodies.BodyExists(simObj.BodyHandle))
                        {
                            if (model != null)
                            {
                                viewport3D.Children.Remove(model);
                            }
                            objectsToRemove.Add(simObj);
                        }
                    }
                }

                foreach (var simObj in objectsToRemove)
                {
                    // ✅ NUEVO: Limpiar animaciones activas para objetos eliminados
                    if (simObj is simTransporte transporteToRemove && activeAnimations.ContainsKey(transporteToRemove))
                    {
                        StopTransportAnimation(transporteToRemove);
                    }

                    simBaseToModelMap.Remove(simObj);
                    lastKnownDimensions.Remove(simObj);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Cleanup] ERROR during cleanup: {ex.Message}");
            }
        }

        private void CreateVisualizationFromSimBase(simBase simObj)
        {
            if (simulationManager?.simulation == null || !simulationManager.simulation.Bodies.BodyExists(simObj.BodyHandle))
            {
                System.Diagnostics.Debug.WriteLine($"[3D Create] ERROR: Body does not exist for simBase");
                return;
            }

            try
            {
                var body = simulationManager.simulation.Bodies[simObj.BodyHandle];
                var collidable = body.Collidable;
                var shapeIndex = collidable.Shape;

                if (!shapeIndex.Exists)
                {
                    System.Diagnostics.Debug.WriteLine($"[3D Create] ERROR: Shape does not exist");
                    return;
                }

                ModelVisual3D visual3D = null;

                // Caso especial: simBotella usa esfera en BEPU pero se visualiza como cilindro
                if (simObj is simBotella botella)
                {
                    visual3D = CreateCylinderVisualization(botella.Radius, botella.Height, simObj);
                }
                // Caso especial: simCurve usa múltiples triángulos pero se visualiza como superficie curva
                else if (simObj is simCurve curve)
                {
                    visual3D = CreateCurveVisualization(curve);
                }
                else
                {
                    // Para otros objetos, usar la forma real de BEPU
                    var dimensions = GetShapeDimensions(shapeIndex, simObj);

                    if (dimensions.ShapeType == BepuPhysics.Collidables.Sphere.Id)
                    {
                        visual3D = CreateSphereVisualization(dimensions.Radius, simObj);
                    }
                    else if (dimensions.ShapeType == BepuPhysics.Collidables.Box.Id)
                    {
                        visual3D = CreateBoxVisualization(dimensions.Width, dimensions.Height, dimensions.Length, simObj);
                    }
                    else if (dimensions.ShapeType == BepuPhysics.Collidables.Cylinder.Id)
                    {
                        visual3D = CreateCylinderVisualization(dimensions.Radius, dimensions.Length, simObj);
                    }
                    else
                    {
                        System.Diagnostics.Debug.WriteLine($"[3D Create] WARNING: Unsupported shape type: {dimensions.ShapeType}");
                        return;
                    }
                }

                if (visual3D != null)
                {
                    // Posicionar correctamente el objeto 3D
                    var position = simObj.GetPosition();
                    var rotationZ = simObj.GetRotationZ();

                    var transform = new Transform3DGroup();
                    transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), rotationZ * 180.0 / Math.PI)));
                    transform.Children.Add(new TranslateTransform3D(position.X, position.Y, position.Z));
                    visual3D.Transform = transform;

                    // Agregar a la vista 3D y asociar con simBase
                    viewport3D.Children.Add(visual3D);
                    simBaseToModelMap[simObj] = visual3D;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Create] ERROR: {ex.Message}");
            }
        }

        private ModelVisual3D CreateSphereVisualization(float radius, simBase simObj = null)
        {
            var meshBuilder = new MeshBuilder();
            meshBuilder.AddSphere(new Point3D(0, 0, 0), (double)radius, 16, 16);

            var model = new GeometryModel3D(meshBuilder.ToMesh(), GetMaterialForSimBase(simObj));

            return new ModelVisual3D { Content = model };
        }

        private ModelVisual3D CreateBoxVisualization(float width, float height, float length, simBase simObj = null)
        {
            var meshBuilder = new MeshBuilder();
            meshBuilder.AddBox(new Point3D(0, 0, 0), (double)width, (double)height, (double)length);

            var model = new GeometryModel3D(meshBuilder.ToMesh(), GetMaterialForSimBase(simObj));

            return new ModelVisual3D { Content = model };
        }

        private ModelVisual3D CreateCapsuleVisualization(float radius, float length)
        {
            var meshBuilder = new MeshBuilder();
            meshBuilder.AddCylinder(new Point3D(0, 0, -length / 2), new Point3D(0, 0, length / 2), radius, 16);

            // Material plástico brillante para cápsulas (amarillo)
            var material = MaterialHelper.CreateMaterial(
                new SolidColorBrush(Color.FromRgb(255, 255, 80)),
                specularPower: 125,
                ambient: 210
            );
            var model = new GeometryModel3D(meshBuilder.ToMesh(), material);

            return new ModelVisual3D { Content = model };
        }

        private ModelVisual3D CreateCylinderVisualization(float radius, float length, simBase simObj = null)
        {
            var meshBuilder = new MeshBuilder();

            // Crear cilindro con tapas usando la versión mejorada
            var p1 = new Point3D(0, 0, -length / 2);
            var p2 = new Point3D(0, 0, length / 2);
            meshBuilder.AddCylinder(p1, p2, radius: (double)radius, thetaDiv: 16, cap1: true, cap2: true);

            var geometry = meshBuilder.ToMesh();
            var model = new GeometryModel3D(geometry, GetMaterialForSimBase(simObj));
            var visual = new ModelVisual3D();
            visual.Content = model;

            return visual;
        }

        private ModelVisual3D CreateCurveVisualization(simCurve curve)
        {
            try
            {
                var meshBuilder = new MeshBuilder();

                // ✅ NUEVO: Elegir entre visualización normal o debug según el flag
                if (DebugShowIndividualTriangles)
                {
                    CreateCurveDebugMeshWithIndividualTriangles(meshBuilder, curve);
                }
                else
                {
                    // ✅ CORREGIDO: Crea un arco de mesh continuo y simple desde los triángulos de BEPU
                    CreateCurveMeshFromBEPUTriangles(meshBuilder, curve);
                }

                var geometry = meshBuilder.ToMesh();
                
                // ✅ NUEVO: Usar material específico para debug si está activo
                Material material = DebugShowIndividualTriangles ? 
                    GetDebugMaterialForCurve(curve) : 
                    GetMaterialForSimBase(curve);
                    
                var model = new GeometryModel3D(geometry, material);
                var visual = new ModelVisual3D();
                visual.Content = model;

                return visual;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D CreateCurve] Error creating curve visualization: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// ✅ CORREGIDO: Crea un arco de mesh continuo y simple desde los triángulos de BEPU
        /// Genera una superficie plana que forma un arco suave y eficiente
        /// </summary>
        private void CreateCurveMeshFromBEPUTriangles(MeshBuilder meshBuilder, simCurve curve)
        {
            try
            {
                // Obtener los triángulos de BEPU
                var localTriangles = curve.GetRealBEPUTriangles();

                System.Diagnostics.Debug.WriteLine($"[3D Curve] Creating continuous arc mesh from {localTriangles.Length} BEPU triangles");

                if (localTriangles.Length == 0)
                {
                    System.Diagnostics.Debug.WriteLine($"[3D Curve] WARNING: No triangles found in BEPU curve");
                    return;
                }

                // ✅ CORREGIDO: Usar altura más visible y verificar coordenadas
                const float curveHeight = 0.0f; // 0.02f; // 2cm de altura para que sea claramente visible
                int triangleCount = 0;

                foreach (var triangle in localTriangles)
                {
                    // ✅ LOGGING: Verificar las coordenadas del primer triángulo
                    if (triangleCount == 0)
                    {
                        System.Diagnostics.Debug.WriteLine($"[3D Curve] First triangle: A({triangle.A.X:F3}, {triangle.A.Y:F3}, {triangle.A.Z:F3})");
                        System.Diagnostics.Debug.WriteLine($"[3D Curve] First triangle: B({triangle.B.X:F3}, {triangle.B.Y:F3}, {triangle.B.Z:F3})");
                        System.Diagnostics.Debug.WriteLine($"[3D Curve] First triangle: C({triangle.C.X:F3}, {triangle.C.Y:F3}, {triangle.C.Z:F3})");
                    }

                    // Crear triángulo en la superficie superior con altura visible
                    var pointA = new Point3D(triangle.A.X, triangle.A.Y, triangle.A.Z + curveHeight);
                    var pointB = new Point3D(triangle.B.X, triangle.B.Y, triangle.B.Z + curveHeight);
                    var pointC = new Point3D(triangle.C.X, triangle.C.Y, triangle.C.Z + curveHeight);
                    
                    // ✅ CORREGIDO: Probar ambos órdenes de vértices para asegurar normales correctas
                    meshBuilder.AddTriangle(pointA, pointB, pointC);
                    // También agregar el triángulo con orden inverso para que sea visible desde ambos lados
                    meshBuilder.AddTriangle(pointA, pointC, pointB);
                    
                    triangleCount++;
                }

                System.Diagnostics.Debug.WriteLine($"[3D Curve] ✅ Continuous arc mesh created with {triangleCount} triangles (height: {curveHeight})");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Curve] ERROR creating continuous arc mesh: {ex.Message}");
                System.Diagnostics.Debug.WriteLine($"[3D Curve] Stack trace: {ex.StackTrace}");
            }
        }

        /// <summary>
        /// ✅ CORREGIDO: Función de debug que muestra triángulos reales de BEPU sin offset artificial
        /// Los triángulos se muestran exactamente como están en la física (planos, sin separación)
        /// </summary>
        private void CreateCurveDebugMeshWithIndividualTriangles(MeshBuilder meshBuilder, simCurve curve)
        {
            try
            {
                // ✅ EXTRAER TRIÁNGULOS LOCALES DE BEPU PARA DEBUG
                var localTriangles = curve.GetRealBEPUTriangles();
                
                if (localTriangles.Length == 0)
                {
                    System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: No hay triángulos locales para debug, usando fallback");
                    CreateBasicDebugGeometry(meshBuilder, curve);
                    return;
                }

                System.Diagnostics.Debug.WriteLine($"[3D Debug] Creando debug de {localTriangles.Length} triángulos reales planos");

                // ✅ MOSTRAR TRIÁNGULOS REALES SIN OFFSET ARTIFICIAL
                for (int i = 0; i < localTriangles.Length; i++)
                {
                    var triangle = localTriangles[i];
                    
                    // ✅ USAR TRIÁNGULOS EXACTOS SIN MODIFICACIÓN
                    var pointA = new Point3D(triangle.A.X, triangle.A.Y, triangle.A.Z);
                    var pointB = new Point3D(triangle.B.X, triangle.B.Y, triangle.B.Z);
                    var pointC = new Point3D(triangle.C.X, triangle.C.Y, triangle.C.Z);
                    
                    // Agregar triángulo real sin modificaciones
                    meshBuilder.AddTriangle(pointA, pointB, pointC);
                    
                    // ✅ DEBUG: Agregar bordes delgados para visualizar límites entre triángulos
                    AddDebugTriangleEdges(meshBuilder, pointA, pointB, pointC, 0.002f);
                }

                System.Diagnostics.Debug.WriteLine($"[3D Debug] ✅ Debug mesh creado con triángulos reales planos (sin offset artificial)");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR creating debug mesh: {ex.Message}");
                System.Diagnostics.Debug.WriteLine($"[3D Debug] Falling back to basic debug geometry");
                CreateBasicDebugGeometry(meshBuilder, curve);
            }
        }

        /// <summary>
        /// ✅ NUEVO: Agrega bordes debug a un triángulo individual
        /// </summary>
        private void AddDebugTriangleEdges(MeshBuilder meshBuilder, Point3D a, Point3D b, Point3D c, double edgeThickness)
        {
            try
            {
                // Crear cilindros delgados para los bordes del triángulo
                meshBuilder.AddCylinder(a, b, edgeThickness, 4, false, false);
                meshBuilder.AddCylinder(b, c, edgeThickness, 4, false, false);
                meshBuilder.AddCylinder(c, a, edgeThickness, 4, false, false);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR adding triangle edges: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Agrega un borde delgado entre dos puntos para debug visual
        /// </summary>
        private void AddDebugEdge(MeshBuilder meshBuilder, Point3D p1Top, Point3D p2Top, Point3D p1Bottom, Point3D p2Bottom, float thickness)
        {
            try
            {
                // Crear un cilindro muy delgado como borde
                meshBuilder.AddCylinder(p1Top, p2Top, thickness, 4, false, false);
                meshBuilder.AddCylinder(p1Bottom, p2Bottom, thickness, 4, false, false);
                meshBuilder.AddCylinder(p1Top, p1Bottom, thickness, 4, false, false);
                meshBuilder.AddCylinder(p2Top, p2Bottom, thickness, 4, false, false);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR adding debug edge: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Geometría básica de debug cuando fallan otras opciones
        /// </summary>
        private void CreateBasicDebugGeometry(MeshBuilder meshBuilder, simCurve curve)
        {
            try
            {
                // Crear una geometría muy simple que indique que hay un problema
                float innerRadius = curve.InnerRadius;
                float outerRadius = curve.OuterRadius;
                float centerRadius = (innerRadius + outerRadius) / 2f;

                // Crear un triángulo básico como indicador de debug
                var p1 = new Point3D(centerRadius, 0, 0);
                var p2 = new Point3D(centerRadius * Math.Cos(Math.PI/3), centerRadius * Math.Sin(Math.PI/3), 0);
                var p3 = new Point3D(centerRadius * Math.Cos(-Math.PI/3), centerRadius * Math.Sin(-Math.PI/3), 0);

                meshBuilder.AddTriangle(p1, p2, p3);

                System.Diagnostics.Debug.WriteLine($"[3D Debug] Created basic debug geometry as fallback");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR creating basic debug geometry: {ex.Message}");
            }
        }

        private void UpdateVisualizationFromSimBase(simBase simObj)
        {
            if (!simBaseToModelMap.TryGetValue(simObj, out var visual))
                return;

            if (simulationManager?.simulation == null || visual == null)
                return;

            try
            {
                var body = simulationManager.simulation.Bodies[simObj.BodyHandle];
                var pose = body.Pose;
                var collidable = body.Collidable;

                // Verificar si las dimensiones han cambiado
                var currentDimensions = GetShapeDimensions(collidable.Shape, simObj);
                bool dimensionsChanged = false;

                if (lastKnownDimensions.TryGetValue(simObj, out var lastDimensions))
                {
                    dimensionsChanged = !currentDimensions.Equals(lastDimensions);

                }
                else
                {
                    dimensionsChanged = true; // Primera vez
                }

                // Si las dimensiones cambiaron, recrear la geometría
                if (dimensionsChanged)
                {
                    RecreateVisualizationGeometry(simObj, visual, currentDimensions);
                    lastKnownDimensions[simObj] = currentDimensions;
                }
                else
                {
                    // System.Diagnostics.Debug.WriteLine($"[3D Update] No geometry changes needed for {simObj.GetType().Name}");
                }

                // ✅ NUEVO: Gestionar animaciones para transportes
                if (simObj is simTransporte transporte)
                {
                    ManageTransportAnimation(transporte, visual);
                }

                // Actualizar transformación del modelo 3D
                var transform = new Transform3DGroup();

                // ✅ ORDEN CORRECTO: Primero Rotación, luego Traslación
                // Esto mantiene el pivot consistente con WPF (Top-Left)

                // 1. Rotación (primero, en el origen)
                var rotation = new QuaternionRotation3D(new System.Windows.Media.Media3D.Quaternion(
                    (double)pose.Orientation.X,
                    (double)pose.Orientation.Y,
                    (double)pose.Orientation.Z,
                    (double)pose.Orientation.W
                ));
                var rotationTransform = new RotateTransform3D(rotation);
                transform.Children.Add(rotationTransform);

                // 2. Traslación (después, mueve el objeto ya rotado)
                var translation = new TranslateTransform3D(
                    (double)pose.Position.X,
                    (double)pose.Position.Y,
                    (double)pose.Position.Z
                );
                transform.Children.Add(translation);

                visual.Transform = transform;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Update] ERROR updating visualization: {ex.Message}");
            }
        }

        private ShapeDimensions GetShapeDimensions(TypedIndex shapeIndex, simBase simObj = null)
        {
            var dimensions = new ShapeDimensions();

            if (!shapeIndex.Exists)
                return dimensions;

            dimensions.ShapeType = shapeIndex.Type;

            try
            {
                // ✅ CASO ESPECIAL: Para simCurve, obtener dimensiones específicas de la curva
                if (simObj is simCurve curve)
                {
                    dimensions.InnerRadius = curve.InnerRadius;
                    dimensions.OuterRadius = curve.OuterRadius;
                    dimensions.StartAngle = curve.StartAngle;
                    dimensions.EndAngle = curve.EndAngle;
                    dimensions.ShapeType = -1; // Tipo especial para curvas
                    return dimensions;
                }

                // ✅ CASO ESPECIAL: Para simBotella, obtener dimensiones del cilindro visual
                if (simObj is simBotella botella)
                {
                    dimensions.Radius = botella.Radius;
                    dimensions.Height = botella.Height;
                    dimensions.ShapeType = -2; // Tipo especial para botellas (cilindro visual)
                    return dimensions;
                }

                // ✅ NUEVO: Para simTransporte, detectar si está en movimiento
                if (simObj is simTransporte transporte)
                {
                    dimensions.IsMoving = Math.Abs(transporte.Speed) > 0.001f; // Consideramos movimiento si la velocidad es mayor que 0.001
                }

                // Para otros objetos, usar la forma real de BEPU
                if (shapeIndex.Type == BepuPhysics.Collidables.Sphere.Id)
                {
                    var sphere = simulationManager.simulation.Shapes.GetShape<BepuPhysics.Collidables.Sphere>(shapeIndex.Index);
                    dimensions.Radius = sphere.Radius;
                }
                else if (shapeIndex.Type == BepuPhysics.Collidables.Box.Id)
                {
                    var box = simulationManager.simulation.Shapes.GetShape<BepuPhysics.Collidables.Box>(shapeIndex.Index);
                    dimensions.Width = box.Width;
                    dimensions.Height = box.Height;
                    dimensions.Length = box.Length;
                }
                else if (shapeIndex.Type == BepuPhysics.Collidables.Cylinder.Id)
                {
                    var cylinder = simulationManager.simulation.Shapes.GetShape<BepuPhysics.Collidables.Cylinder>(shapeIndex.Index);
                    dimensions.Radius = cylinder.Radius;
                    dimensions.Length = cylinder.Length; // En BEPU, Length es la altura del cilindro
                }
                else if (shapeIndex.Type == BepuPhysics.Collidables.Capsule.Id)
                {
                    var capsule = simulationManager.simulation.Shapes.GetShape<BepuPhysics.Collidables.Capsule>(shapeIndex.Index);
                    dimensions.Radius = capsule.Radius;
                    dimensions.Length = capsule.Length;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D GetDimensions] ERROR getting dimensions: {ex.Message}");
            }

            return dimensions;
        }

        private void RecreateVisualizationGeometry(simBase simObj, ModelVisual3D visual, ShapeDimensions dimensions)
        {
            if (visual?.Content is not GeometryModel3D geometryModel)
                return;

            try
            {
                MeshGeometry3D newGeometry = null;
                Material newMaterial = null;

                // Caso especial: simBotella usa esfera en BEPU pero se visualiza como cilindro
                if (simObj is simBotella botella)
                {
                    var meshBuilder = new MeshBuilder();

                    // Crear cilindro con tapas usando la versión mejorada
                    var p1 = new Point3D(0, 0, -botella.Height / 2);
                    var p2 = new Point3D(0, 0, botella.Height / 2);
                    meshBuilder.AddCylinder(p1, p2, radius: (double)botella.Radius, thetaDiv: 16, cap1: true, cap2: true);

                    newGeometry = meshBuilder.ToMesh();
                    newMaterial = GetMaterialForSimBase(simObj);
                }
                // Caso especial: simCurve necesita recreación completa de geometría
                else if (simObj is simCurve curve)
                {
                    var meshBuilder = new MeshBuilder();

                    // ✅ CORREGIDO: Respetar el flag de debug también en recreación
                    if (DebugShowIndividualTriangles)
                    {
                        CreateCurveDebugMeshWithIndividualTriangles(meshBuilder, curve);
                        newMaterial = GetDebugMaterialForCurve(curve);
                    }
                    else
                    {
                        CreateCurveMeshFromBEPUTriangles(meshBuilder, curve);
                        newMaterial = GetMaterialForSimBase(simObj);
                    }

                    newGeometry = meshBuilder.ToMesh();
                }
                else if (dimensions.ShapeType == BepuPhysics.Collidables.Sphere.Id)
                {
                    var meshBuilder = new MeshBuilder();
                    meshBuilder.AddSphere(new Point3D(0, 0, 0), dimensions.Radius);
                    newGeometry = meshBuilder.ToMesh();
                    newMaterial = GetMaterialForSimBase(simObj);
                }
                else if (dimensions.ShapeType == BepuPhysics.Collidables.Box.Id)
                {
                    var meshBuilder = new MeshBuilder();
                    meshBuilder.AddBox(new Point3D(0, 0, 0), dimensions.Width, dimensions.Height, dimensions.Length);
                    newGeometry = meshBuilder.ToMesh();
                    newMaterial = GetMaterialForSimBase(simObj);
                }
                else if (dimensions.ShapeType == BepuPhysics.Collidables.Cylinder.Id)
                {
                    var meshBuilder = new MeshBuilder();

                    // Crear cilindro con tapas usando la versión mejorada
                    var p1 = new Point3D(0, 0, -dimensions.Length / 2);
                    var p2 = new Point3D(0, 0, dimensions.Length / 2);
                    meshBuilder.AddCylinder(p1, p2, radius: (double)dimensions.Radius, thetaDiv: 16, cap1: true, cap2: true);

                    newGeometry = meshBuilder.ToMesh();
                    newMaterial = GetMaterialForSimBase(simObj);
                }

                if (newGeometry != null)
                {
                    geometryModel.Geometry = newGeometry;
                    geometryModel.Material = newMaterial;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Recreate] ERROR recreating geometry: {ex.Message}");
            }
        }

        public void Clear()
        {
            // ✅ NUEVO: Detener todas las animaciones activas antes de limpiar
            foreach (var kvp in activeAnimations)
            {
                kvp.Value.Stop();
            }
            activeAnimations.Clear();

            foreach (var model in simBaseToModelMap.Values)
            {
                viewport3D.Children.Remove(model);
            }
            simBaseToModelMap.Clear();
            lastKnownDimensions.Clear();

            // ✅ NUEVO: También limpiar la imagen de fondo
            RemoveBackgroundImage();
        }

        /// <summary>
        /// ✅ NUEVO: Material específico para debug de curvas
        /// </summary>
        /// <param name="curve">Curva en modo debug</param>
        /// <returns>Material especial para visualización de debug</returns>
        private Material GetDebugMaterialForCurve(simCurve curve)
        {
            // Material wireframe semi-transparente para debug
            // Color naranja brillante para que sea muy visible
            var debugBrush = new SolidColorBrush(Color.FromRgb(255, 165, 0)); // Naranja
            debugBrush.Opacity = 0.7; // 70% opacidad para ver superposiciones
            
            return MaterialHelper.CreateMaterial(
                debugBrush,
                specularPower: 20,  // Menos reflectante para mejor visibilidad
                ambient: 250        // Más ambiente para que se vea bien en todas las condiciones
            );
        }

        /// <summary>
        /// ✅ NUEVO: Crea un material animable (no frozen) para poder animar sus propiedades
        /// </summary>
        /// <param name="color">Color del material</param>
        /// <param name="specularPower">Potencia especular</param>
        /// <param name="ambient">Nivel de luz ambiente</param>
        /// <returns>Material que puede ser animado</returns>
        private Material CreateAnimatableMaterial(Color color, double specularPower = 100, byte ambient = 190)
        {
            // Crear brush animable (no frozen)
            var diffuseBrush = new SolidColorBrush(color);
            
            // Crear material difuso
            var diffuseMaterial = new DiffuseMaterial(diffuseBrush);
            
            // Crear material especular - ✅ CORREGIDO: Usar un gris claro en lugar de blanco puro para reducir el brillo excesivo
            var specularBrush = new SolidColorBrush(Color.FromRgb(128, 128, 128)); 
            var specularMaterial = new SpecularMaterial(specularBrush, specularPower);
            
            // Crear material "ambiente" (usando Emissive) - ✅ CORREGIDO: Usar un color mucho más oscuro para no sobreexponer
            var ambientColor = Color.FromRgb((byte)(ambient / 4), (byte)(ambient / 4), (byte)(ambient / 4)); 
            var ambientBrush = new SolidColorBrush(ambientColor);
            var ambientMaterial = new EmissiveMaterial(ambientBrush);
            
            // Combinar todos los materiales
            var materialGroup = new MaterialGroup();
            materialGroup.Children.Add(diffuseMaterial);
            materialGroup.Children.Add(specularMaterial);
            materialGroup.Children.Add(ambientMaterial);
            
            // NO hacer freeze para mantener animable
            return materialGroup;
        }

        /// <summary>
        /// Función centralizada para obtener el material apropiado según el tipo de simBase
        /// </summary>
        /// <param name="simObj">Objeto de simulación para determinar el material</param>
        /// <returns>Material configurado para el tipo de objeto</returns>
        private Material GetMaterialForSimBase(simBase simObj)
        {
            if (simObj is simTransporte transporte)
            {
                // ✅ NUEVO: Cambiar color según si está en movimiento
                bool isMoving = Math.Abs(transporte.Speed) > 0.001f;
                if (isMoving)
                {
                    // ✅ CORREGIDO: Usar material animable para transportes en movimiento
                    return CreateAnimatableMaterial(Color.FromRgb(40, 255, 40), 180, 240);
                }
                else
                {
                    // Material plástico verde normal para transportes detenidos
                    return MaterialHelper.CreateMaterial(
                        new SolidColorBrush(Color.FromRgb(80, 180, 80)),
                        specularPower: 140,
                        ambient: 200
                    );
                }
            }
            else if (simObj is simGuia)
            {
                // Material semitransparente gris oscuro para guías
                var guiaBrush = new SolidColorBrush(Color.FromRgb(80, 80, 80));
                guiaBrush.Opacity = 0.4; // 40% opacidad para mejor visualización semitransparente
                return MaterialHelper.CreateMaterial(
                    guiaBrush,
                    specularPower: 30,
                    ambient: 100
                );
            }
            else if (simObj is simBarrera)
            {
                // ✅ MEJORADO: Material para haz de luz más realista
                var lightBeamBrush = new SolidColorBrush(Color.FromRgb(255, 255, 100)); // Amarillo cálido
                lightBeamBrush.Opacity = 0.6; // 60% opacidad para simular haz de luz visible
                return MaterialHelper.CreateMaterial(
                    lightBeamBrush,
                    specularPower: 80,  // Algo de brillo para simular luz
                    ambient: 200        // Ambiente alto para que "emita" luz
                );
            }
            else if (simObj is simBotella)
            {
                // Material plástico brillante rojo para botellas
                return MaterialHelper.CreateMaterial(
                    new SolidColorBrush(Color.FromRgb(255, 80, 80)),
                    specularPower: 120,
                    ambient: 200
                );
            }
            else if (simObj is simDescarte)
            {
                // Material semi-transparente magenta para descartes (85% transparente)
                var descarteBrush = new SolidColorBrush(Color.FromRgb(255, 100, 255));
                descarteBrush.Opacity = 0.15; // 15% opacidad = 85% transparencia
                return MaterialHelper.CreateMaterial(
                    descarteBrush,
                    specularPower: 60,
                    ambient: 120
                );
            }
            else if (simObj is simCurve)
            {
                // Material plástico brillante para curvas (azul verdoso)
                return MaterialHelper.CreateMaterial(
                    new SolidColorBrush(Color.FromRgb(80, 150, 200)),
                    specularPower: 130,
                    ambient: 190
                );
            }
            else
            {
                // Material plástico gris estándar para objetos no identificados
                return MaterialHelper.CreateMaterial(
                    new SolidColorBrush(Color.FromRgb(128, 128, 128)),
                    specularPower: 100,
                    ambient: 190
                );
            }
        }

        public void SetCameraView(CameraView view)
        {
            if (viewport3D?.Camera is not PerspectiveCamera camera) return;

            switch (view)
            {
                case CameraView.Top:
                    camera.Position = new Point3D(0, 0, 10);
                    camera.LookDirection = new Vector3D(0, 0, -1);
                    camera.UpDirection = new Vector3D(0, 1, 0);
                    break;
                case CameraView.Side:
                    camera.Position = new Point3D(10, 0, 0);
                    camera.LookDirection = new Vector3D(-1, 0, 0);
                    camera.UpDirection = new Vector3D(0, 0, 1);
                    break;
                case CameraView.Front:
                    camera.Position = new Point3D(0, 10, 0);
                    camera.LookDirection = new Vector3D(0, -1, 0);
                    camera.UpDirection = new Vector3D(0, 0, 1);
                    break;
                case CameraView.Isometric:
                    camera.Position = new Point3D(7, 7, 7);
                    camera.LookDirection = new Vector3D(-1, -1, -1);
                    camera.UpDirection = new Vector3D(0, 0, 1);
                    break;
            }
        }

        /// <summary>
        /// ✅ NUEVO: Crea una animación de movimiento para transportes usando StoryBoard
        /// Combina rotación sutil + pulsación de brillo para simular movimiento
        /// </summary>
        /// <param name="transporte">Transporte al cual aplicar la animación</param>
        /// <param name="visual">Visual3D del transporte</param>
        private void CreateTransportMovementAnimation(simTransporte transporte, ModelVisual3D visual)
        {
            try
            {
                // Detener animación anterior si existe
                StopTransportAnimation(transporte);

                // Crear nuevo StoryBoard
                var storyboard = new Storyboard();
                storyboard.RepeatBehavior = RepeatBehavior.Forever;

                // ✅ ANIMACIÓN 1: Rotación sutil continua alrededor del eje Z (muy lenta)
                var rotationAnimation = new DoubleAnimation
                {
                    From = 0,
                    To = 360,
                    Duration = TimeSpan.FromSeconds(20), // 20 segundos por vuelta completa (muy lento)
                    RepeatBehavior = RepeatBehavior.Forever
                };

                // Crear transform group si no existe
                if (visual.Transform is not Transform3DGroup transformGroup)
                {
                    transformGroup = new Transform3DGroup();
                    if (visual.Transform != null)
                        transformGroup.Children.Add(visual.Transform);
                    visual.Transform = transformGroup;
                }

                // Agregar rotación de animación
                var animationRotation = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0));
                transformGroup.Children.Add(animationRotation);

                // Configurar target para la rotación
                Storyboard.SetTarget(rotationAnimation, animationRotation);
                Storyboard.SetTargetProperty(rotationAnimation, new PropertyPath("Rotation.Angle"));
                storyboard.Children.Add(rotationAnimation);

                // ✅ ANIMACIÓN 2: Pulsación de brillo del material
                if (visual.Content is GeometryModel3D geometryModel && 
                    geometryModel.Material is MaterialGroup materialGroup)
                {
                    // Buscar el material difuso para animar (debe ser el primero en mi CreateAnimatableMaterial)
                    var diffuseMaterial = materialGroup.Children.OfType<DiffuseMaterial>().FirstOrDefault();
                    if (diffuseMaterial?.Brush is SolidColorBrush diffuseBrush)
                    {
                        // ✅ CORREGIDO: El brush ya debe ser animable si se creó con CreateAnimatableMaterial
                        var colorAnimation = new ColorAnimation
                        {
                            From = Color.FromRgb(40, 255, 40),   // Verde brillante
                            To = Color.FromRgb(80, 255, 80),     // Verde más brillante
                            Duration = TimeSpan.FromSeconds(1.5), // Pulsación cada 1.5 segundos
                            AutoReverse = true,
                            RepeatBehavior = RepeatBehavior.Forever
                        };

                        Storyboard.SetTarget(colorAnimation, diffuseBrush);
                        Storyboard.SetTargetProperty(colorAnimation, new PropertyPath("Color"));
                        storyboard.Children.Add(colorAnimation);
                    }
                }

                // Iniciar animación
                storyboard.Begin();
                
                // Almacenar referencia para poder detenerla después
                activeAnimations[transporte] = storyboard;

                System.Diagnostics.Debug.WriteLine($"[3D Animation] ✅ Animación de movimiento creada para transporte");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Animation] ❌ Error creando animación: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Detiene la animación de movimiento de un transporte
        /// </summary>
        /// <param name="transporte">Transporte cuya animación se desea detener</param>
        private void StopTransportAnimation(simTransporte transporte)
        {
            try
            {
                if (activeAnimations.TryGetValue(transporte, out var storyboard))
                {
                    storyboard.Stop();
                    activeAnimations.Remove(transporte);
                    
                    // Limpiar transforms de animación del visual
                    if (simBaseToModelMap.TryGetValue(transporte, out var visual) && 
                        visual.Transform is Transform3DGroup transformGroup)
                    {
                        // Remover solo la rotación de animación (la última agregada)
                        if (transformGroup.Children.Count > 1)
                        {
                            transformGroup.Children.RemoveAt(transformGroup.Children.Count - 1);
                        }
                    }

                    System.Diagnostics.Debug.WriteLine($"[3D Animation] ✅ Animación de transporte detenida");
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Animation] ❌ Error deteniendo animación: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Gestiona automáticamente las animaciones de transportes según su estado
        /// </summary>
        /// <param name="transporte">Transporte a gestionar</param>
        /// <param name="visual">Visual3D del transporte</param>
        private void ManageTransportAnimation(simTransporte transporte, ModelVisual3D visual)
        {
            try
            {
                bool isMoving = Math.Abs(transporte.Speed) > 0.001f;
                bool hasActiveAnimation = activeAnimations.ContainsKey(transporte);

                if (isMoving && !hasActiveAnimation)
                {
                    // Crear animación si el transporte está en movimiento y no tiene animación
                    CreateTransportMovementAnimation(transporte, visual);
                }
                else if (!isMoving && hasActiveAnimation)
                {
                    // Detener animación si el transporte está detenido y tiene animación
                    StopTransportAnimation(transporte);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Animation] ❌ Error gestionando animación: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ MODO DEBUG DE TRIÁNGULOS DE CURVAS:
        /// 
        /// TRUE (Debug Mode):  Muestra triángulos individuales reales de BEPU con bordes visibles
        /// FALSE (Normal Mode): Muestra superficie continua suave
        /// 
        /// Uso desde código:
        /// // Activar modo debug para ver triángulos reales
        /// visualizationManager.ShowRealTriangles();
        /// 
        /// // Desactivar para ver superficie continua
        /// visualizationManager.ShowContinuousSurface();
        /// </summary>
        /// <param name="enableDebug">True para mostrar triángulos reales, false para superficie continua</param>
        /// <param name="forceRefresh">True para forzar actualización inmediata de todas las curvas</param>
        public void SetDebugTrianglesMode(bool enableDebug, bool forceRefresh = true)
        {
            bool wasChanged = DebugShowIndividualTriangles != enableDebug;
            DebugShowIndividualTriangles = enableDebug;

            System.Diagnostics.Debug.WriteLine($"[3D Debug] Modo triángulos reales: {(enableDebug ? "ACTIVADO (triángulos individuales)" : "DESACTIVADO (superficie continua)")}");

            if (wasChanged && forceRefresh)
            {
                RefreshCurveVisualizations();
            }
        }

        /// <summary>
        /// ✅ NUEVO: Método simple para activar el modo debug - muestra triángulos reales planos
        /// </summary>
        public void ShowRealTriangles()
        {
            SetDebugTrianglesMode(true);
        }

        /// <summary>
        /// ✅ NUEVO: Método simple para activar el modo superficie continua
        /// </summary>
        public void ShowContinuousSurface()
        {
            SetDebugTrianglesMode(false);
        }

        /// <summary>
        /// ✅ NUEVO: Fuerza la regeneración de todas las visualizaciones de curvas
        /// Útil cuando se cambia el modo debug
        /// </summary>
        public void RefreshCurveVisualizations()
        {
            if (simulationManager?.Cuerpos == null)
                return;

            try
            {
                System.Diagnostics.Debug.WriteLine($"[3D Debug] Refreshing curve visualizations...");

                // Encontrar todas las curvas y forzar su regeneración
                var curvesToRefresh = simulationManager.Cuerpos.OfType<simCurve>().ToList();

                foreach (var curve in curvesToRefresh)
                {
                    // Remover las dimensiones conocidas para forzar recreación
                    lastKnownDimensions.Remove(curve);

                    // Forzar actualización de la visualización
                    if (simBaseToModelMap.ContainsKey(curve))
                    {
                        UpdateVisualizationFromSimBase(curve);
                    }
                }

                System.Diagnostics.Debug.WriteLine($"[3D Debug] Refreshed {curvesToRefresh.Count} curve visualizations");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR refreshing curve visualizations: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Obtiene el estado actual del modo debug
        /// </summary>
        /// <returns>True si el modo debug está activo</returns>
        public bool IsDebugTrianglesModeEnabled()
        {
            return DebugShowIndividualTriangles;
        }

        /// <summary>
        /// ✅ CORREGIDO: Activa la visualización de triángulos locales de BEPU
        /// Muestra los triángulos exactos extraídos de la simulación física en coordenadas locales
        /// </summary>
        /// <param name="enable">True para mostrar triángulos locales, false para superficie normal</param>
        public void SetRealBEPUTrianglesMode(bool enable)
        {
            SetDebugTrianglesMode(enable);
            System.Diagnostics.Debug.WriteLine($"[3D BEPU] Modo triángulos locales: {(enable ? "ACTIVADO" : "DESACTIVADO")}");
        }

        /// <summary>
        /// ✅ CORREGIDO: Verifica si una curva tiene triángulos válidos en BEPU
        /// </summary>
        /// <param name="curve">Curva a verificar</param>
        /// <returns>True si tiene triángulos válidos</returns>
        public bool HasValidBEPUTriangles(simCurve curve)
        {
            if (curve == null) return false;
            
            try
            {
                var triangles = curve.GetRealBEPUTriangles(); // Obtiene triángulos locales
                bool hasTriangles = triangles.Length > 0;
                
                System.Diagnostics.Debug.WriteLine($"[3D BEPU] Curva tiene {triangles.Length} triángulos válidos: {hasTriangles}");
                return hasTriangles;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D BEPU] Error verificando triángulos: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Carga el estado guardado de la cámara desde EstadoPersistente
        /// </summary>
        private void LoadCameraState()
        {
            try
            {
                var cameraSettings = EstadoPersistente.Instance.Camera;

                viewport3D.Camera = new PerspectiveCamera
                {
                    Position = new Point3D(cameraSettings.PositionX, cameraSettings.PositionY, cameraSettings.PositionZ),
                    LookDirection = new Vector3D(cameraSettings.LookDirectionX, cameraSettings.LookDirectionY, cameraSettings.LookDirectionZ),
                    UpDirection = new Vector3D(cameraSettings.UpDirectionX, cameraSettings.UpDirectionY, cameraSettings.UpDirectionZ),
                    FieldOfView = cameraSettings.FieldOfView
                };

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Camera] ERROR loading camera state: {ex.Message}");
                // Fallback a configuración por defecto
                viewport3D.Camera = new PerspectiveCamera
                {
                    LookDirection = new Vector3D(-3.86, 18.1, -10),
                    UpDirection = new Vector3D(-0.1, 0.48, 0.87),
                    Position = new Point3D(3.86, -18.13, 10),
                    FieldOfView = 60
                };
            }
        }

        /// <summary>
        /// Guarda el estado actual de la cámara en EstadoPersistente
        /// </summary>
        private void SaveCameraState()
        {
            try
            {
                if (viewport3D?.Camera is PerspectiveCamera camera)
                {
                    var cameraSettings = EstadoPersistente.Instance.Camera;

                    cameraSettings.PositionX = camera.Position.X;
                    cameraSettings.PositionY = camera.Position.Y;
                    cameraSettings.PositionZ = camera.Position.Z;
                    cameraSettings.LookDirectionX = camera.LookDirection.X;
                    cameraSettings.LookDirectionY = camera.LookDirection.Y;
                    cameraSettings.LookDirectionZ = camera.LookDirection.Z;
                    cameraSettings.UpDirectionX = camera.UpDirection.X;
                    cameraSettings.UpDirectionY = camera.UpDirection.Y;
                    cameraSettings.UpDirectionZ = camera.UpDirection.Z;
                    cameraSettings.FieldOfView = camera.FieldOfView;

                    EstadoPersistente.Instance.GuardarEstado();
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Camera] ERROR saving camera state: {ex.Message}");
            }
        }

        /// <summary>
        /// Se suscribe a eventos del viewport para detectar cambios en la cámara
        /// </summary>
        private void SubscribeToCameraEvents()
        {
            try
            {
                // Suscribirse a eventos del viewport para detectar cambios en la cámara
                viewport3D.CameraChanged += (sender, e) => SaveCameraState();

                // También suscribirse a eventos de mouse para detectar interacciones
                viewport3D.MouseUp += (sender, e) => SaveCameraState();
                viewport3D.MouseWheel += (sender, e) => SaveCameraState();

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Camera] ERROR subscribing to camera events: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Crea la visualización del haz de luz para simBarrera
        /// Muestra una línea semi-transparente que representa el RayCast
        /// </summary>
        /// <param name="barrera">Barrera que usa RayCast para detección</param>
        private void CreateBarreraVisualization(simBarrera barrera)
        {
            try
            {
                var visual3D = CreateLightBeamVisualization(barrera);

                if (visual3D != null)
                {
                    System.Diagnostics.Debug.WriteLine($"[3D Barrera] ✅ Visual3D creado, actualizando transformación...");
                    
                    // Posicionar y orientar el haz de luz
                    UpdateBarreraTransform(barrera, visual3D);

                    // Agregar a la vista 3D y asociar con simBase
                    viewport3D.Children.Add(visual3D);
                    simBaseToModelMap[barrera] = visual3D;

                    System.Diagnostics.Debug.WriteLine($"[3D Barrera] ✅ Haz de luz creado exitosamente y agregado al viewport");
                    System.Diagnostics.Debug.WriteLine($"   Total children en viewport: {viewport3D.Children.Count}");
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine($"[3D Barrera] ❌ CreateLightBeamVisualization devolvió null");
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Barrera] ❌ ERROR creando visualización: {ex.Message}");
                System.Diagnostics.Debug.WriteLine($"   Stack trace: {ex.StackTrace}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Actualiza la visualización existente de simBarrera
        /// </summary>
        /// <param name="barrera">Barrera a actualizar</param>
        private void UpdateBarreraVisualization(simBarrera barrera)
        {
            try
            {
                if (!simBaseToModelMap.TryGetValue(barrera, out var visual))
                    return;

                // Verificar si las dimensiones han cambiado
                var currentDimensions = GetBarreraDimensions(barrera);
                bool dimensionsChanged = false;

                if (lastKnownDimensions.TryGetValue(barrera, out var lastDimensions))
                {
                    dimensionsChanged = !currentDimensions.Equals(lastDimensions);
                }
                else
                {
                    dimensionsChanged = true; // Primera vez
                }

                // Si las dimensiones cambiaron, recrear la geometría
                if (dimensionsChanged)
                {
                    RecreateBarreraGeometry(barrera, visual, currentDimensions);
                    lastKnownDimensions[barrera] = currentDimensions;
                }

                // Actualizar transformación (posición y rotación)
                UpdateBarreraTransform(barrera, visual);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Barrera] ❌ ERROR actualizando visualización: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Crea la geometría del haz de luz (línea semi-transparente)
        /// </summary>
        /// <param name="barrera">Barrera para crear el haz</param>
        /// <returns>Modelo visual del haz de luz</returns>
        private ModelVisual3D CreateLightBeamVisualization(simBarrera barrera)
        {
            try
            {
                var meshBuilder = new MeshBuilder();

                // Crear una línea delgada que representa el haz de luz
                var halfWidth = barrera.Width / 2f;
                var beamThickness = 0.005f; // 5mm de grosor para el haz
                var beamHeight = 0.02f; // 2cm de altura para que sea visible

                // Puntos del haz de luz (de extremo a extremo)
                var startPoint = new Point3D(-halfWidth, 0, beamHeight / 2);
                var endPoint = new Point3D(halfWidth, 0, beamHeight / 2);

                // Crear cilindro delgado para el haz principal
                meshBuilder.AddCylinder(startPoint, endPoint, beamThickness, 8, false, false);

                // ✅ OPCIONAL: Agregar pequeñas esferas en los extremos para marcar emisor y receptor
                meshBuilder.AddSphere(new Point3D(-halfWidth, 0, 2 * beamThickness), beamThickness * 2, 6, 6); // Emisor
                //meshBuilder.AddSphere(new Point3D(halfWidth, 0, 2 * beamThickness), beamThickness * 2, 6, 6);  // Receptor

                // Material semi-transparente para simular haz de luz
                var material = GetMaterialForSimBase(barrera);
                var model = new GeometryModel3D(meshBuilder.ToMesh(), material);
                var visual = new ModelVisual3D { Content = model };

                return visual;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Barrera] ❌ ERROR creando geometría del haz: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// ✅ NUEVO: Actualiza la transformación (posición y rotación) de la barrera
        /// </summary>
        /// <param name="barrera">Barrera a transformar</param>
        /// <param name="visual">Visual 3D a transformar</param>
        private void UpdateBarreraTransform(simBarrera barrera, ModelVisual3D visual)
        {
            try
            {
                var transform = new Transform3DGroup();

                // ✅ CORREGIDO: rotationZ ya está en grados WPF, no necesita conversión adicional
                transform.Children.Add(new RotateTransform3D(
                    new AxisAngleRotation3D(new Vector3D(0, 0, 1), barrera.AngleRadians)));

                // Traslación
                transform.Children.Add(new TranslateTransform3D(barrera.Position.X, barrera.Position.Y, barrera.Position.Z));

                visual.Transform = transform;

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Barrera] ❌ ERROR actualizando transformación: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Obtiene las dimensiones de una barrera para detectar cambios
        /// </summary>
        /// <param name="barrera">Barrera de la cual obtener dimensiones</param>
        /// <returns>Dimensiones de la barrera</returns>
        private ShapeDimensions GetBarreraDimensions(simBarrera barrera)
        {
            return new ShapeDimensions
            {
                Width = barrera.Width,
                Height = barrera.Height,
                Length = 0,
                Radius = 0,
                ShapeType = -3, // Tipo especial para barreras
                InnerRadius = 0,
                OuterRadius = 0,
                StartAngle = 0,
                EndAngle = 0,
                IsMoving = false // Las barreras no se mueven
            };
        }

        /// <summary>
        /// ✅ NUEVO: Recrea la geometría de la barrera cuando cambian las dimensiones
        /// </summary>
        /// <param name="barrera">Barrera a recrear</param>
        /// <param name="visual">Visual 3D a actualizar</param>
        /// <param name="dimensions">Nuevas dimensiones</param>
        private void RecreateBarreraGeometry(simBarrera barrera, ModelVisual3D visual, ShapeDimensions dimensions)
        {
            if (visual?.Content is not GeometryModel3D geometryModel)
                return;

            try
            {
                var meshBuilder = new MeshBuilder();

                // Recrear la geometría del haz con las nuevas dimensiones
                var halfWidth = dimensions.Width / 2f;
                var beamThickness = 0.005f;
                var beamHeight = 0.02f;

                var startPoint = new Point3D(-halfWidth, 0, beamHeight / 2);
                var endPoint = new Point3D(halfWidth, 0, beamHeight / 2);

                meshBuilder.AddCylinder(startPoint, endPoint, beamThickness, 8, false, false);
                meshBuilder.AddSphere(new Point3D(-halfWidth, 0, 2*beamThickness), beamThickness * 2, 6, 6);
                //meshBuilder.AddSphere(new Point3D(halfWidth, 0, 2*beamThickness), beamThickness * 2, 6, 6);

                var newGeometry = meshBuilder.ToMesh();
                var newMaterial = GetMaterialForSimBase(barrera);

                geometryModel.Geometry = newGeometry;
                geometryModel.Material = newMaterial;

                System.Diagnostics.Debug.WriteLine($"[3D Barrera] ✅ Geometría recreada con ancho: {dimensions.Width}");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Barrera] ❌ ERROR recreando geometría: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Agrega la imagen de fondo como un plano 2D estático en el viewport 3D
        /// con la misma escala que el canvas WPF usando las convenciones WPF-BEPU correctas
        /// 
        /// Esta funcionalidad permite tener coherencia visual entre el canvas 2D WPF y la visualización 3D:
        /// - La imagen se posiciona en el plano XY con Z=0
        /// - Usa el sistema de conversión PixelToMeter para mantener la escala correcta
        /// - Aplica las conversiones WPF-BEPU: WPF (x,y) -> BEPU (x,-y)
        /// - Se aplica como textura difusa en un rectángulo plano
        /// - Respeta las convenciones: WPF Y hacia abajo, BEPU Y hacia arriba
        /// 
        /// Ejemplo de uso:
        /// SetBackgroundImage(@"C:\imagen.png", 1920, 1080);
        /// </summary>
        /// <param name="imagePath">Ruta absoluta de la imagen de fondo</param>
        /// <param name="canvasWidth">Ancho del canvas WPF en píxeles</param>
        /// <param name="canvasHeight">Alto del canvas WPF en píxeles</param>
        public void SetBackgroundImage(string imagePath, double canvasWidth, double canvasHeight)
        {
            try
            {
                // Remover imagen de fondo anterior si existe
                RemoveBackgroundImage();

                // Convertir dimensiones de píxeles a metros usando el sistema de conversión
                float widthInMeters = PixelToMeter.Instance.calc.PixelsToMeters((float)canvasWidth);
                float heightInMeters = PixelToMeter.Instance.calc.PixelsToMeters((float)canvasHeight);

                // Crear la geometría del plano
                var meshBuilder = new MeshBuilder();
                
                // ✅ CORREGIDO: Usar convenciones WPF-BEPU correctas
                // En WPF: (0,0) = esquina superior izquierda, Y hacia abajo
                // En BEPU: Y hacia arriba, entonces convertimos WPF (x,y) -> BEPU (x,-y)
                
                // Definir las esquinas en coordenadas WPF
                var wpfTopLeft = new System.Numerics.Vector2(0, 0);
                var wpfTopRight = new System.Numerics.Vector2(widthInMeters, 0);
                var wpfBottomRight = new System.Numerics.Vector2(widthInMeters, heightInMeters);
                var wpfBottomLeft = new System.Numerics.Vector2(0, heightInMeters);

                // Convertir a coordenadas BEPU usando CoordinateConverter
                var bepuTopLeft = CoordinateConverter.WpfToBepuVector2(wpfTopLeft);
                var bepuTopRight = CoordinateConverter.WpfToBepuVector2(wpfTopRight);
                var bepuBottomRight = CoordinateConverter.WpfToBepuVector2(wpfBottomRight);
                var bepuBottomLeft = CoordinateConverter.WpfToBepuVector2(wpfBottomLeft);

                // Crear los puntos 3D con Z=0
                var p1 = new Point3D(bepuTopLeft.X, bepuTopLeft.Y, 0);           // Esquina superior izquierda WPF
                var p2 = new Point3D(bepuTopRight.X, bepuTopRight.Y, 0);         // Esquina superior derecha WPF
                var p3 = new Point3D(bepuBottomRight.X, bepuBottomRight.Y, 0);   // Esquina inferior derecha WPF
                var p4 = new Point3D(bepuBottomLeft.X, bepuBottomLeft.Y, 0);     // Esquina inferior izquierda WPF

                // ✅ CORREGIDO: Crear quad con orientación correcta
                // Orden antihorario para normal hacia arriba (visible desde arriba)
                meshBuilder.AddQuad(p1, p2, p3, p4);

                // Crear el material con la imagen como textura
                var imageSource = new BitmapImage(new Uri(imagePath, UriKind.Absolute));
                var imageBrush = new ImageBrush(imageSource)
                {
                    Stretch = Stretch.Fill,
                    TileMode = TileMode.None
                };

                var frontMaterial = new DiffuseMaterial(imageBrush);
                var backMaterial = new DiffuseMaterial(imageBrush); // ✅ NUEVO: Material para la cara trasera

                // Crear el modelo 3D con material en ambas caras
                var geometry = meshBuilder.ToMesh();
                var model = new GeometryModel3D(geometry, frontMaterial)
                {
                    BackMaterial = backMaterial // ✅ NUEVO: Hace visible desde ambos lados
                };

                // Crear el visual 3D
                _backgroundImageVisual = new ModelVisual3D { Content = model };

                // Agregar al viewport
                viewport3D.Children.Add(_backgroundImageVisual);

                System.Diagnostics.Debug.WriteLine($"[3D Background] ✅ Imagen de fondo agregada con convenciones WPF-BEPU: {widthInMeters:F2}m x {heightInMeters:F2}m");
                System.Diagnostics.Debug.WriteLine($"[3D Background]    WPF (0,0) -> BEPU ({bepuTopLeft.X:F2},{bepuTopLeft.Y:F2})");
                System.Diagnostics.Debug.WriteLine($"[3D Background]    WPF ({widthInMeters:F2},{heightInMeters:F2}) -> BEPU ({bepuBottomRight.X:F2},{bepuBottomRight.Y:F2})");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Background] ❌ ERROR agregando imagen de fondo: {ex.Message}");
            }
        }

        /// <summary>
        /// ✅ NUEVO: Remueve la imagen de fondo del viewport 3D
        /// </summary>
        public void RemoveBackgroundImage()
        {
            try
            {
                if (_backgroundImageVisual != null && viewport3D.Children.Contains(_backgroundImageVisual))
                {
                    viewport3D.Children.Remove(_backgroundImageVisual);
                    System.Diagnostics.Debug.WriteLine($"[3D Background] ✅ Imagen de fondo removida");
                }
                _backgroundImageVisual = null;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"[3D Background] ❌ ERROR removiendo imagen de fondo: {ex.Message}");
            }
        }
    }

    public enum CameraView
    {
        Top,
        Side,
        Front,
        Isometric
    }
}