using CtrEditor.ObjetosSim;
using CtrEditor.Simulacion;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using UserControl = System.Windows.Controls.UserControl;
using CtrEditor.Controls;  // Add this using statement
using Newtonsoft.Json;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System;
using System.Linq;

namespace CtrEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        // Para el Canvas
        private Point _lastMousePosition;
        private bool _isDrawingCanvas = false;
        private bool _isDraggingCanvas = false;
        private Image imagenDeFondo;
        internal ObjectManipulationManager _objectManager;
        private Rectangle _panningArea;

        // Temporizadores y animación
        private DispatcherTimer _zoomTimer;
        private double _targetZoomFactor;
        private double _initialZoomFactor;
        private double _currentZoomStep;
        private Point _zoomCursorPosition;
        private const int ZoomDuration = 500; // Duración del zoom en milisegundos
        private int _ZoomDuration;
        private const double MinZoomScale = 0.1; // Límite mínimo de zoom
        private Stopwatch _stopwatch;

        private dataDebug dataDebug = new dataDebug();

        // Manager para la visualización 3D
        private BEPUVisualization3DManager _visualization3DManager;

        public MainWindow()
        {
            InitializeComponent();

            _objectManager = new ObjectManipulationManager(this, ImagenEnTrabajoCanvas);

            _panningArea = new Rectangle
            {
                Fill = Brushes.Transparent,
                IsHitTestVisible = true
            };

            // Inicializar temporizador de zoom
            _zoomTimer = new DispatcherTimer();
            _zoomTimer.Interval = TimeSpan.FromMilliseconds(1);
            _zoomTimer.Tick += ZoomTimer_Tick;
            _stopwatch = new Stopwatch();

            // Suscribir eventos
            this.Loaded += MainWindow_Loaded;
            ImagenEnTrabajoScrollViewer.PreviewMouseWheel += ImagenEnTrabajoCanvas_MouseWheel;

            ImagenEnTrabajoCanvas.MouseDown += Canvas_MouseDown_Panning;
            ImagenEnTrabajoCanvas.MouseMove += Canvas_MouseMove_Panning;
            ImagenEnTrabajoCanvas.MouseUp += Canvas_MouseUp_Panning;

            _panningArea.MouseDown += Canvas_MouseDown_Panning;
            _panningArea.MouseRightButtonDown += Canvas_MouseRightButtonDown;
            this.KeyDown += MainWindow_KeyDown;
            ImagenEnTrabajoCanvas.MouseEnter += Canvas_MouseEnter;
            this.Closed += MainWindow_Closed;

            // Importante: Agregar el evento para el menú contextual
            ImagenEnTrabajoCanvas.MouseRightButtonDown += Canvas_MouseRightButtonDown;

            // Asegurarse de que estos eventos estén conectados
            ImagenEnTrabajoCanvas.MouseUp += Canvas_MouseUp_Panning;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            if (DataContext is MainViewModel viewModel)
            {
                viewModel.MainWindow = this;
                viewModel.ImageSelected += ViewModel_ImageSelected;
                viewModel?.LoadInitialData();

                viewModel.MainCanvas = ImagenEnTrabajoCanvas;

                // Inicializar ObjectHierarchyView
                ObjectHierarchy.Initialize(viewModel);

                // Inicializar el manager de visualización 3D
                _visualization3DManager = new BEPUVisualization3DManager(Debug3DViewport, viewModel.simulationManager);
                viewModel.Visualization3DManager = _visualization3DManager;

                // Conectar el manager 3D con el simulation manager (orden correcto de inicialización)
                viewModel.simulationManager.Visualization3DManager = _visualization3DManager;

                // ✅ NUEVO: Si ya hay una imagen de fondo cargada, agregarla al viewport 3D
                if (imagenDeFondo?.Source is BitmapImage existingImage)
                {
                    // Necesitamos la ruta original de la imagen, usar el UriSource si está disponible
                    var imageUri = existingImage.UriSource?.LocalPath ?? existingImage.UriSource?.ToString();
                    if (!string.IsNullOrEmpty(imageUri))
                    {
                        _visualization3DManager.SetBackgroundImage(imageUri, existingImage.PixelWidth, existingImage.PixelHeight);
                        System.Diagnostics.Debug.WriteLine($"[3D Background] ✅ Imagen existente agregada al viewport 3D: {imageUri}");
                    }
                }
            }
        }

        public void SuscribirEventos(UserControl userControl)
        {
            _objectManager.SuscribirEventos(userControl);
        }

        public void AgregarRegistrarUserControlCanvas(UserControl userControl)
        {
            if (userControl is IDataContainer dataContainer)
            {
                SuscribirEventos(userControl);
                Canvas.SetZIndex(userControl, ((int)dataContainer.ZIndex_Base() + dataContainer.zIndex_fromFrames));
                ImagenEnTrabajoCanvas.Children.Add(userControl);
            }
        }

        public void EliminarUserControlDelCanvas(UserControl userControl)
        {
            if (ImagenEnTrabajoCanvas.Children.Contains(userControl))
            {
                ImagenEnTrabajoCanvas.Children.Remove(userControl);
            }
        }

        private void LoadImageToCanvas(string imagePath)
        {
            BitmapImage bitmap = new BitmapImage(new Uri(imagePath, UriKind.Absolute));

            if (imagenDeFondo == null)
            {
                imagenDeFondo = new Image();
                ImagenEnTrabajoCanvas.Children.Add(imagenDeFondo);
            }
            imagenDeFondo.Source = bitmap;
            RenderOptions.SetBitmapScalingMode(imagenDeFondo, BitmapScalingMode.HighQuality);

            // Actualizar dimensiones del canvas
            ImagenEnTrabajoCanvas.Width = bitmap.Width;
            ImagenEnTrabajoCanvas.Height = bitmap.Height;

            // Configurar el área de panning
            _panningArea.Width = bitmap.Width * 4;
            _panningArea.Height = bitmap.Height * 4;

            // Posicionar el área de panning centrada respecto a la imagen
            Canvas.SetLeft(_panningArea, -bitmap.Width / 4);
            Canvas.SetTop(_panningArea, -bitmap.Height / 4);
            Canvas.SetZIndex(_panningArea, -1); // Asegurar que está detrás de todo

            // Asegurarse de que el área de panning está en el canvas
            if (!ImagenEnTrabajoCanvas.Children.Contains(_panningArea))
            {
                ImagenEnTrabajoCanvas.Children.Add(_panningArea);
            }

            // Posicionar la imagen
            Canvas.SetLeft(imagenDeFondo, 0);
            Canvas.SetTop(imagenDeFondo, 0);

            // ✅ NUEVO: Agregar la imagen de fondo al viewport 3D con la misma escala
            if (_visualization3DManager != null)
            {
                _visualization3DManager.SetBackgroundImage(imagePath, bitmap.Width, bitmap.Height);
            }

            // Eliminar solo los ROIs, no la imagen de fondo ni el área de panning
            for (int i = ImagenEnTrabajoCanvas.Children.Count - 1; i >= 0; i--)
            {
                var child = ImagenEnTrabajoCanvas.Children[i];
                if (child != imagenDeFondo && child != _panningArea)
                {
                    ImagenEnTrabajoCanvas.Children.RemoveAt(i);
                }
            }
        }

        private void Canvas_MouseUp_Panning(object sender, MouseButtonEventArgs e)
        {
            if (_isDraggingCanvas)
            {
                _isDraggingCanvas = false;
                ImagenEnTrabajoCanvas.ReleaseMouseCapture();
                Mouse.OverrideCursor = null;

                if (_objectManager.SelectedObjects.Count > 0)
                {
                    _objectManager.MakeResizeRectanglesNormal();
                    _objectManager.UpdateSelectionVisuals(); // Agregar esta línea
                }
                e.Handled = true;
            }
        }

        private void Canvas_MouseDown_Panning(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed && !_isDrawingCanvas)
            {
                // Permitir el panning cuando se hace clic en el área de panning o en la imagen
                if (e.Source == _panningArea ||
                    e.Source == imagenDeFondo ||
                    e.Source == ImagenEnTrabajoCanvas ||
                    (e.Source is Rectangle rect && rect == _objectManager._selectionRectangle))
                {
                    ImagenEnTrabajoCanvas.Focus();

                    if (_objectManager.IsRectangleSelectionActive)
                    {
                        if (!_objectManager.HasActiveSelectionRectangle)
                        {
                            _objectManager.StartRectangleSelection(e.GetPosition(ImagenEnTrabajoCanvas));
                        }
                        else
                        {
                            _objectManager.FinishRectangleSelection(e.GetPosition(ImagenEnTrabajoCanvas));
                        }
                    }
                    else
                    {
                        _isDraggingCanvas = true;
                        _lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
                        ImagenEnTrabajoCanvas.CaptureMouse(); // Importante: siempre capturar en el Canvas principal
                        Mouse.OverrideCursor = Cursors.Hand;

                        if (DataContext is MainViewModel viewModel)
                        {
                            viewModel.SelectedItemOsList = null;
                            _objectManager.RemoveResizeRectangles();
                        }
                    }
                    e.Handled = true;
                }
            }
        }

        private void Canvas_MouseMove_Panning(object sender, MouseEventArgs e)
        {
            if (_objectManager.IsRectangleSelectionActive && _objectManager.HasActiveSelectionRectangle)
            {
                var currentPoint = e.GetPosition(ImagenEnTrabajoCanvas);
                _objectManager.UpdateRectangleSelection(currentPoint);
            }
            else if (_isDraggingCanvas && e.LeftButton == MouseButtonState.Pressed)
            {
                var currentPosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
                var dx = currentPosition.X - _lastMousePosition.X;
                var dy = currentPosition.Y - _lastMousePosition.Y;

                _objectManager.MakeResizeRectanglesTransparent();
                _objectManager.RemoveHighlightRectangles();

                var transform = (TranslateTransform)((TransformGroup)ImagenEnTrabajoCanvas.RenderTransform)
                    .Children.First(t => t is TranslateTransform);
                transform.X += dx;
                transform.Y += dy;

                _lastMousePosition = currentPosition;
                e.Handled = true;
            }
        }

        private void ImagenEnTrabajoCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            _objectManager.MakeResizeRectanglesTransparent();
            _objectManager.RemoveHighlightRectangles();

            _initialZoomFactor = ((ScaleTransform)((TransformGroup)ImagenEnTrabajoCanvas.RenderTransform)
                .Children.First(t => t is ScaleTransform)).ScaleX;

            double minZoomFactor = Math.Min(
                ImagenEnTrabajoScrollViewer.ViewportWidth / ImagenEnTrabajoCanvas.ActualWidth,
                ImagenEnTrabajoScrollViewer.ViewportHeight / ImagenEnTrabajoCanvas.ActualHeight) / 1.5;

            _targetZoomFactor = e.Delta > 0 ?
                _initialZoomFactor * 1.4 :
                Math.Max(_initialZoomFactor * 0.75, minZoomFactor);

            _zoomCursorPosition = e.GetPosition(ImagenEnTrabajoCanvas);

            RenderOptions.SetBitmapScalingMode(ImagenEnTrabajoCanvas, BitmapScalingMode.LowQuality);

            _ZoomDuration = !_zoomTimer.IsEnabled ? ZoomDuration : ZoomDuration / 3;

            _stopwatch.Restart();
            _zoomTimer.Start();

            e.Handled = true;
        }

        private void ZoomTimer_Tick(object sender, EventArgs e)
        {
            double elapsedMilliseconds = _stopwatch.Elapsed.TotalMilliseconds;

            if (elapsedMilliseconds >= _ZoomDuration)
            {
                _zoomTimer.Stop();
                _stopwatch.Stop();

                RenderOptions.SetBitmapScalingMode(ImagenEnTrabajoCanvas, BitmapScalingMode.HighQuality);

                if (_objectManager.SelectedObjects.Count > 0)
                {
                    _objectManager.MakeResizeRectanglesNormal();
                    _objectManager.UpdateSelectionVisuals(); // Agregar esta línea
                }

                return;
            }

            var tg = (TransformGroup)ImagenEnTrabajoCanvas.RenderTransform;
            var st = (ScaleTransform)tg.Children.First(t => t is ScaleTransform);
            var tt = (TranslateTransform)tg.Children.First(t => t is TranslateTransform);

            double t = elapsedMilliseconds / _ZoomDuration;
            double easeOutT = t * (2 - t);
            double zoomFactor = _initialZoomFactor + (_targetZoomFactor - _initialZoomFactor) * easeOutT;

            zoomFactor = Math.Max(zoomFactor, MinZoomScale);

            Point cursorPosition = _zoomCursorPosition;

            var relativeX = cursorPosition.X * st.ScaleX + tt.X;
            var relativeY = cursorPosition.Y * st.ScaleY + tt.Y;

            st.ScaleX = zoomFactor;
            st.ScaleY = zoomFactor;

            tt.X = relativeX - cursorPosition.X * st.ScaleX;
            tt.Y = relativeY - cursorPosition.Y * st.ScaleY;
        }

        private void ListaOs_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject)
            {
                // Siempre trabajar con selección única para las propiedades
                CargarPropiedadesosDatos(selectedObject);

                _objectManager.SelectObject(selectedObject);
            }
            else if (e.RemovedItems.Count > 0 && e.AddedItems.Count == 0)
            {
                // This handles the case when an item is deselected and no new item is selected
                CargarPropiedadesosDatos(null);
                _objectManager.RemoveResizeRectangles();
            }
        }

        private void ImagePathButton_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new Ookii.Dialogs.Wpf.VistaOpenFileDialog
            {
                Filter = "Image files (*.png;*.jpg;*.bmp)|*.png;*.jpg;*.bmp|All files (*.*)|*.*"
            };

            if (dlg.ShowDialog() == true)
            {
                // El DataContext de este botón es el PropertyItem asociado
                if ((sender as Button)?.DataContext is Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
                {
                    propertyItem.Value = dlg.FileName;
                }
            }
        }

        private void MainWindow_KeyDown(object sender, KeyEventArgs e)
        {
            // Only force canvas focus if PanelEdicion doesn't have focus AND PLCControl doesn't have focus
            if (!PanelEdicion.IsKeyboardFocusWithin && !PLCSim.IsKeyboardFocusWithin)
            {
                if (!ImagenEnTrabajoCanvas.IsFocused)
                {
                    ImagenEnTrabajoCanvas.Focus();
                }
            }

            if (DataContext is MainViewModel viewModel)
            {
                if (e.Key == Key.Delete)
                {
                    // This will delete all selected objects
                    _objectManager.EliminarObjetosSeleccionados();
                    e.Handled = true;
                }
                else if (e.Key == Key.Escape)
                {
                    viewModel.SelectedItemOsList = null;
                    _objectManager.ClearSelection();
                    _objectManager.RemoveResizeRectangles();
                    e.Handled = true;
                }
                else if (_objectManager.SelectedObjects.Any())
                {
                    const float moveDistance = 0.1f;
                    switch (e.Key)
                    {
                        case Key.Left:
                            MoveSelectedObjects(-moveDistance, 0);
                            e.Handled = true;
                            break;
                        case Key.Right:
                            MoveSelectedObjects(moveDistance, 0);
                            e.Handled = true;
                            break;
                        case Key.Up:
                            MoveSelectedObjects(0, -moveDistance);
                            e.Handled = true;
                            break;
                        case Key.Down:
                            MoveSelectedObjects(0, moveDistance);
                            e.Handled = true;
                            break;
                    }
                }
            }
        }

        private void MoveSelectedObjects(float deltaX, float deltaY)
        {
            // Capturar estado antes de mover
            _objectManager.CaptureUndoState();

            // Mover todos los objetos primero
            foreach (var obj in _objectManager.SelectedObjects)
            {
                obj.Left += deltaX;
                obj.Top += deltaY;
            }

            // Forzar una actualización del layout antes de actualizar los visuales
            ImagenEnTrabajoCanvas.UpdateLayout();

            // Ahora actualizar los visuales de selección
            _objectManager.UpdateSelectionVisuals();

            if (DataContext is MainViewModel viewModel)
            {
                viewModel.HasUnsavedChanges = true;
            }
        }

        private void LockSelectedObjects(bool lockState)
        {
            foreach (var selectedObject in _objectManager.SelectedObjects)
            {
                selectedObject.Lock_movement = lockState;
            }

            // Actualizar los visuales de selección para reflejar el cambio de estado
            _objectManager.UpdateSelectionVisuals();

            // Marcar como cambios sin guardar
            if (DataContext is MainViewModel viewModel)
            {
                viewModel.HasUnsavedChanges = true;
            }
        }

        private void Canvas_MouseEnter(object sender, MouseEventArgs e)
        {
            if (e.OriginalSource == ImagenEnTrabajoCanvas)
            {
                //_objectManager.RemoveResizeRectangles();
            }
        }

        private void MainWindow_Closed(object sender, EventArgs e)
        {
            if (DataContext is MainViewModel viewModel)
            {
                viewModel.ImageSelected -= ViewModel_ImageSelected;
                
                // Limpiar todos los recursos antes del cierre
                viewModel.CleanupResources();
            }
        }

        private void CargarPropiedadesosDatos(osBase selectedObject)
        {
            if (DataContext is MainViewModel viewModel)
            {
                viewModel.CargarPropiedadesosDatos(selectedObject, PanelEdicion, Resources);

                // If no object is selected, make sure to clear the properties panel
                if (selectedObject == null)
                {
                    PanelEdicion.ClearProperties();
                }
            }
        }

        public (float X, float Y) ObtenerCentroCanvasMeters()
        {
            var c = ObtenerCentroCanvasPixels();
            return (PixelToMeter.Instance.calc.PixelsToMeters(c.X),
                   PixelToMeter.Instance.calc.PixelsToMeters(c.Y));
        }

        public (float X, float Y) ObtenerCentroCanvasPixels()
        {
            var tg = (TransformGroup)ImagenEnTrabajoCanvas.RenderTransform;
            var st = (ScaleTransform)tg.Children.First(t => t is ScaleTransform);
            var tt = (TranslateTransform)tg.Children.First(t => t is TranslateTransform);

            double visibleWidth = ImagenEnTrabajoScrollViewer.ViewportWidth;
            double visibleHeight = ImagenEnTrabajoScrollViewer.ViewportHeight;

            double offsetX = ImagenEnTrabajoScrollViewer.HorizontalOffset;
            double offsetY = ImagenEnTrabajoScrollViewer.VerticalOffset;

            double centerX = offsetX + (visibleWidth / 2);
            double centerY = offsetY + (visibleHeight / 2);

            double canvasCenterX = (centerX - tt.X) / st.ScaleX;
            double canvasCenterY = (centerY - tt.Y) / st.ScaleY;

            return ((float)canvasCenterX, (float)canvasCenterY);
        }

        private void ViewModel_ImageSelected(object sender, string imagePath)
        {
            LoadImageToCanvas(imagePath);
        }

        public void DebugWindow()
        {
            var debugWindow = new wDebug
            {
                Data = dataDebug
            };
            debugWindow.Show();
        }

        public void ClearUndoHistory()
        {
            _objectManager.ClearUndoHistory();
        }

        /// <summary>
        /// ✅ NUEVO: Establece la imagen de fondo en el viewport 3D
        /// </summary>
        /// <param name="imagePath">Ruta de la imagen de fondo</param>
        public void SetBackgroundImage3D(string imagePath)
        {
            if (_visualization3DManager != null && imagenDeFondo?.Source is BitmapImage bitmap)
            {
                _visualization3DManager.SetBackgroundImage(imagePath, bitmap.Width, bitmap.Height);
            }
        }

        /// <summary>
        /// ✅ NUEVO: Remueve la imagen de fondo del viewport 3D
        /// </summary>
        public void RemoveBackgroundImage3D()
        {
            _visualization3DManager?.RemoveBackgroundImage();
        }

        /// <summary>
        /// ✅ NUEVO: Sincroniza la imagen de fondo existente con el viewport 3D
        /// Útil cuando se necesita actualizar la imagen 3D basándose en la imagen 2D actual
        /// </summary>
        public void SyncBackgroundImage3D()
        {
            if (_visualization3DManager == null) return;

            if (imagenDeFondo?.Source is BitmapImage existingImage)
            {
                // Intentar obtener la ruta de la imagen desde diferentes fuentes
                var imageUri = existingImage.UriSource?.LocalPath ?? 
                              existingImage.UriSource?.ToString() ?? 
                              existingImage.BaseUri?.LocalPath;

                if (!string.IsNullOrEmpty(imageUri))
                {
                    _visualization3DManager.SetBackgroundImage(imageUri, existingImage.PixelWidth, existingImage.PixelHeight);
                    System.Diagnostics.Debug.WriteLine($"[3D Background] ✅ Imagen sincronizada con viewport 3D");
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine($"[3D Background] ⚠️ No se pudo obtener la ruta de la imagen para sincronizar");
                }
            }
            else
            {
                // Si no hay imagen cargada, remover la imagen de fondo 3D
                _visualization3DManager.RemoveBackgroundImage();
                System.Diagnostics.Debug.WriteLine($"[3D Background] ✅ Imagen de fondo 3D removida (no hay imagen 2D)");
            }
        }

        private void Canvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            // Aceptar el evento si viene del canvas o de la imagen de fondo
            if ((e.Source == ImagenEnTrabajoCanvas || e.Source == imagenDeFondo || e.Source is UserControl) &&
                DataContext is MainViewModel viewModel)
            {
                ImagenEnTrabajoCanvas.Focus(); // Asegurar que el canvas tiene el foco
                e.Handled = true;
                ShowContextMenu(e.GetPosition(ImagenEnTrabajoCanvas));
            }
        }

        private void ShowContextMenu(Point position)
        {
            var contextMenu = new ContextMenu();

            if (DataContext is MainViewModel viewModel)
            {
                // Multi-selection checkbox
                var multiSelectMenuItem = new MenuItem
                {
                    Header = "Modo Multi-Selección",
                    IsCheckable = true,
                    StaysOpenOnClick = false,
                    IsChecked = viewModel.IsMultiSelectionActive
                };

                multiSelectMenuItem.Click += (s, e) =>
                {
                    viewModel.IsMultiSelectionActive = multiSelectMenuItem.IsChecked;

                    // Si se desactiva la multi-selección, desactivar también la selección por rectángulo
                    if (!multiSelectMenuItem.IsChecked)
                        _objectManager.IsRectangleSelectionActive = false;
                };

                contextMenu.Items.Add(multiSelectMenuItem);

                // Mostrar opción de selección por rectángulo solo cuando multi-selección está activa
                if (viewModel.IsMultiSelectionActive)
                {
                    var rectangleSelectMenuItem = new MenuItem
                    {
                        Header = "Selección por Rectángulo",
                        IsCheckable = true,
                        IsChecked = _objectManager.IsRectangleSelectionActive,
                        StaysOpenOnClick = false
                    };

                    rectangleSelectMenuItem.Click += (s, e) =>
                    {
                        _objectManager.IsRectangleSelectionActive = rectangleSelectMenuItem.IsChecked;
                        _objectManager.IsDraggingCanvas = false;  // Desactivar panning cuando se activa la selección por rectángulo
                    };

                    contextMenu.Items.Add(rectangleSelectMenuItem);
                }

                // Si hay objetos seleccionados, agregar opciones de alineación
                if (_objectManager.SelectedObjects.Count > 1)
                {
                    contextMenu.Items.Add(new Separator());

                    var alignSubmenu = new MenuItem { Header = "Alinear" };

                    // Alineación horizontal
                    var alignLeftItem = new MenuItem { Header = "Izquierda" };
                    alignLeftItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Left);
                    alignSubmenu.Items.Add(alignLeftItem);

                    var alignRightItem = new MenuItem { Header = "Derecha" };
                    alignRightItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Right);
                    alignSubmenu.Items.Add(alignRightItem);

                    var alignCenterHItem = new MenuItem { Header = "Centro Horizontal" };
                    alignCenterHItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.CenterHorizontally);
                    alignSubmenu.Items.Add(alignCenterHItem);

                    // Alineación vertical
                    alignSubmenu.Items.Add(new Separator());

                    var alignTopItem = new MenuItem { Header = "Arriba" };
                    alignTopItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Top);
                    alignSubmenu.Items.Add(alignTopItem);

                    var alignBottomItem = new MenuItem { Header = "Abajo" };
                    alignBottomItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Bottom);
                    alignSubmenu.Items.Add(alignBottomItem);

                    var alignCenterVItem = new MenuItem { Header = "Centro Vertical" };
                    alignCenterVItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.CenterVertically);
                    alignSubmenu.Items.Add(alignCenterVItem);

                    // Distribución
                    alignSubmenu.Items.Add(new Separator());

                    var distributeHItem = new MenuItem { Header = "Distribuir Horizontalmente" };
                    distributeHItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.DistributeHorizontally);
                    alignSubmenu.Items.Add(distributeHItem);

                    var distributeVItem = new MenuItem { Header = "Distribuir Verticalmente" };
                    distributeVItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.DistributeVertically);
                    alignSubmenu.Items.Add(distributeVItem);

                    // Igualar tamaños
                    alignSubmenu.Items.Add(new Separator());

                    var equalWidthItem = new MenuItem { Header = "Igualar Ancho" };
                    equalWidthItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.EqualWidth);
                    alignSubmenu.Items.Add(equalWidthItem);

                    var equalHeightItem = new MenuItem { Header = "Igualar Alto" };
                    equalHeightItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.EqualHeight);
                    alignSubmenu.Items.Add(equalHeightItem);

                    var equalAngleItem = new MenuItem { Header = "Igualar Ángulo" };
                    equalAngleItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.EqualAngle);
                    alignSubmenu.Items.Add(equalAngleItem);

                    contextMenu.Items.Add(alignSubmenu);
                }

                // Add Edit Properties option when objects are selected
                if (_objectManager.SelectedObjects.Count > 0)
                {
                    var editPropertiesItem = new MenuItem { Header = "Edit Properties" };
                    editPropertiesItem.Click += (s, e) => viewModel.ShowMultiPropertyEditor(_objectManager.SelectedObjects);
                    contextMenu.Items.Add(editPropertiesItem);

                    contextMenu.Items.Add(new Separator());

                    // Agregar opción de copiar
                    var copyMenuItem = new MenuItem { Header = "Copiar (Ctrl+C)" };
                    copyMenuItem.Click += (s, e) => CopySelectedObjectsAsJson();
                    contextMenu.Items.Add(copyMenuItem);

                    contextMenu.Items.Add(new Separator());
                }

                // Agregar opciones de pegado
                if (CanPasteFromClipboard())
                {
                    var pasteMenuItem = new MenuItem { Header = "Pegar (Ctrl+V)" };
                    pasteMenuItem.Click += (s, e) => PasteObjectsFromJson();
                    contextMenu.Items.Add(pasteMenuItem);

                    var pasteReplaceMenuItem = new MenuItem { Header = "Pegar con Reemplazo" };
                    pasteReplaceMenuItem.Click += (s, e) => PasteObjectsFromJson(true);
                    contextMenu.Items.Add(pasteReplaceMenuItem);

                    contextMenu.Items.Add(new Separator());
                }

                // Opciones de bloqueo/desbloqueo - mostrar siempre que haya objetos seleccionados
                if (_objectManager.SelectedObjects.Count > 0)
                {
                    var lockSubmenu = new MenuItem { Header = "Bloqueo" };

                    // Verificar el estado actual de los objetos
                    int totalObjects = _objectManager.SelectedObjects.Count;
                    int lockedObjects = _objectManager.SelectedObjects.Count(obj => obj.Lock_movement);
                    int unlockedObjects = totalObjects - lockedObjects;

                    var lockAllItem = new MenuItem { Header = $"Bloquear Objetos ({unlockedObjects} desbloqueados)" };
                    lockAllItem.Click += (s, e) => LockSelectedObjects(true);
                    lockAllItem.IsEnabled = unlockedObjects > 0;
                    lockSubmenu.Items.Add(lockAllItem);

                    var unlockAllItem = new MenuItem { Header = $"Desbloquear Objetos ({lockedObjects} bloqueados)" };
                    unlockAllItem.Click += (s, e) => LockSelectedObjects(false);
                    unlockAllItem.IsEnabled = lockedObjects > 0;
                    lockSubmenu.Items.Add(unlockAllItem);

                    // Mostrar estado actual si hay mezcla
                    if (lockedObjects > 0 && unlockedObjects > 0)
                    {
                        lockSubmenu.Items.Add(new Separator());
                        var statusItem = new MenuItem { Header = $"Estado: {lockedObjects} bloqueados, {unlockedObjects} libres", IsEnabled = false };
                        lockSubmenu.Items.Add(statusItem);
                    }

                    contextMenu.Items.Add(lockSubmenu);
                    contextMenu.Items.Add(new Separator());
                }

                // Agregar opción de configurar escala
                var scaleConfigItem = new MenuItem { Header = "Configurar Escala..." };
                scaleConfigItem.Click += (s, e) => viewModel.ConfigureScale();
                contextMenu.Items.Add(scaleConfigItem);

                contextMenu.Items.Add(new Separator());

                // Agregar información del sistema de undo
                var undoHistoryCount = _objectManager.GetUndoHistoryCount();
                var canUndo = _objectManager.CanUndo();
                var undoInfoItem = new MenuItem
                {
                    Header = $"Undo: {undoHistoryCount}/3 estados ({(canUndo ? "Ctrl+Z disponible" : "No disponible")})",
                    IsEnabled = false,
                    FontStyle = FontStyles.Italic
                };
                contextMenu.Items.Add(undoInfoItem);

                contextMenu.IsOpen = true;
                contextMenu.PlacementTarget = ImagenEnTrabajoCanvas;
                contextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint;
            }
        }

        private void Canvas_KeyDown(object sender, KeyEventArgs e)
        {
            // Only handle if PanelEdicion doesn't have focus AND PLCControl doesn't have focus
            if (!PanelEdicion.IsKeyboardFocusWithin && !PLCSim.IsKeyboardFocusWithin)
            {
                HandleKeyDown(e);
            }
        }

        private void ScrollViewer_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            // Only handle if PanelEdicion doesn't have focus AND PLCControl doesn't have focus
            if (!PanelEdicion.IsKeyboardFocusWithin && !PLCSim.IsKeyboardFocusWithin &&
                (e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down))
            {
                HandleKeyDown(e);
                e.Handled = true;
            }
        }

        private void HandleKeyDown(KeyEventArgs e)
        {
            if (DataContext is MainViewModel viewModel)
            {
                if (e.Key == Key.Delete)
                {
                    _objectManager.EliminarObjetosSeleccionados(); // Cambiar aquí
                    e.Handled = true;
                }
                else if (e.Key == Key.Escape)
                {
                    viewModel.SelectedItemOsList = null;
                    _objectManager.ClearSelection();
                    _objectManager.RemoveResizeRectangles();
                    e.Handled = true;
                }
                else if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.C)
                {
                    CopySelectedObjectsAsJson();
                    e.Handled = true;
                }
                else if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.V)
                {
                    PasteObjectsFromJson();
                    e.Handled = true;
                }
                else if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.Z)
                {
                    _objectManager.PerformUndo();
                    e.Handled = true;
                }
                else if (_objectManager.SelectedObjects.Any())
                {
                    const float moveDistance = 0.01f;
                    switch (e.Key)
                    {
                        case Key.Left:
                            MoveSelectedObjects(-moveDistance, 0);
                            e.Handled = true;
                            break;
                        case Key.Right:
                            MoveSelectedObjects(moveDistance, 0);
                            e.Handled = true;
                            break;
                        case Key.Up:
                            MoveSelectedObjects(0, -moveDistance);
                            e.Handled = true;
                            break;
                        case Key.Down:
                            MoveSelectedObjects(0, moveDistance);
                            e.Handled = true;
                            break;
                    }
                }
            }
        }

        public Image ImagenDeFondo => imagenDeFondo;

        private void CopySelectedObjectsAsJson()
        {
            if (!_objectManager.SelectedObjects.Any())
                return;

            try
            {
                if (DataContext is MainViewModel viewModel)
                {
                    // Usar la misma lógica que DuplicarObjeto pero solo para crear copias para serialización
                    var objectsCopy = new List<osBase>();

                    foreach (var originalObj in _objectManager.SelectedObjects)
                    {
                        try
                        {
                            // Reutilizar la lógica existente de DuplicarObjeto sin añadir a ObjetosSimulables
                            var tempCopy = CreateCopyUsingExistingLogic(viewModel, originalObj);
                            if (tempCopy != null)
                            {
                                objectsCopy.Add(tempCopy);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Error al crear copia de {originalObj.Nombre}: {ex.Message}");
                        }
                    }

                    if (objectsCopy.Count == 0)
                    {
                        MessageBox.Show("No se pudieron copiar los objetos seleccionados.", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }

                    // Serializar usando las mismas configuraciones que DuplicarObjeto
                    var settings = new JsonSerializerSettings
                    {
                        Formatting = Formatting.Indented,
                        NullValueHandling = NullValueHandling.Ignore,
                        TypeNameHandling = TypeNameHandling.All  // Igual que en DuplicarObjeto
                    };

                    string jsonString = JsonConvert.SerializeObject(objectsCopy, settings);
                    Clipboard.SetText(jsonString);

                    Console.WriteLine($"Copiados {objectsCopy.Count} objeto(s) al portapapeles");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error al copiar objetos: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private osBase CreateCopyUsingExistingLogic(MainViewModel viewModel, osBase original)
        {
            // Usar exactamente la misma lógica que DuplicarObjeto pero sin añadir a la colección
            osBase copy = null;

            original.SalvarDatosNoSerializables();

            var settings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore,
                TypeNameHandling = TypeNameHandling.All  // Igual que en DuplicarObjeto
            };

            try
            {
                var serializedData = JsonConvert.SerializeObject(original, settings);
                copy = JsonConvert.DeserializeObject<osBase>(serializedData, settings);
            }
            finally
            {
                original.RestaurarDatosNoSerializables();
            }

            return copy;
        }

        private void PasteObjectsFromJson(bool replaceExistingIds = false)
        {
            if (!Clipboard.ContainsText())
                return;

            try
            {
                string jsonString = Clipboard.GetText();

                // Validación básica del JSON
                if (string.IsNullOrWhiteSpace(jsonString) || (!jsonString.TrimStart().StartsWith("[") && !jsonString.TrimStart().StartsWith("{")))
                {
                    MessageBox.Show("El contenido del portapapeles no parece ser un JSON válido.", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                // Crear configuración de deserialización
                var settings = new JsonSerializerSettings
                {
                    Formatting = Formatting.Indented,
                    NullValueHandling = NullValueHandling.Ignore,
                    TypeNameHandling = TypeNameHandling.Auto,
                    ObjectCreationHandling = ObjectCreationHandling.Replace,
                    ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
                };

                List<osBase> pastedObjects = null;

                // Intentar deserializar como lista de objetos
                try
                {
                    pastedObjects = JsonConvert.DeserializeObject<List<osBase>>(jsonString, settings);
                }
                catch
                {
                    // Si falla, intentar como objeto individual
                    try
                    {
                        var singleObject = JsonConvert.DeserializeObject<osBase>(jsonString, settings);
                        if (singleObject != null)
                        {
                            pastedObjects = new List<osBase> { singleObject };
                        }
                    }
                    catch
                    {
                        MessageBox.Show("No se pudo deserializar el contenido del portapapeles como objetos válidos.", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                }

                if (pastedObjects == null || !pastedObjects.Any())
                {
                    MessageBox.Show("No se encontraron objetos válidos en el portapapeles.", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                // Obtener posición del mouse en el canvas
                var mousePosition = Mouse.GetPosition(ImagenEnTrabajoCanvas);
                var mousePositionMeters = (
                    PixelToMeter.Instance.calc.PixelsToMeters((float)mousePosition.X),
                    PixelToMeter.Instance.calc.PixelsToMeters((float)mousePosition.Y)
                );

                // Calcular el centro de los objetos pegados para posicionarlos en el mouse
                var centerX = pastedObjects.Average(obj => obj.Left);
                var centerY = pastedObjects.Average(obj => obj.Top);
                var offsetX = mousePositionMeters.Item1 - centerX;
                var offsetY = mousePositionMeters.Item2 - centerY;

                if (DataContext is MainViewModel viewModel)
                {
                    viewModel.StopSimulation();
                    viewModel.DisconnectPLC();

                    // Limpiar selección actual
                    _objectManager.ClearSelection();

                    var newlyPastedObjects = new List<osBase>();

                    foreach (var obj in pastedObjects)
                    {
                        try
                        {
                            // Aplicar offset de posición
                            obj.Left += offsetX;
                            obj.Top += offsetY;

                            // Manejar IDs usando la misma lógica que DuplicarObjeto
                            if (!replaceExistingIds)
                            {
                                // Generar nuevo ID y actualizar nombre (igual que en DuplicarObjeto)
                                obj.Id.ObtenerNuevaID();

                                string nombre = Regex.IsMatch(obj.Nombre, @"_\d+$")
                                    ? Regex.Replace(obj.Nombre, @"_\d+$", $"_{obj.Id.Value}")
                                    : obj.Nombre + "_" + obj.Id.Value;

                                obj.Nombre = nombre;
                            }
                            else
                            {
                                // Reemplazar objeto existente con mismo ID
                                var existingObj = viewModel.ObjetosSimulables.FirstOrDefault(o => o.Id.Value == obj.Id.Value);
                                if (existingObj != null)
                                {
                                    viewModel.RemoverObjetoSimulable(existingObj);
                                }
                            }

                            obj.CheckData();
                            viewModel.ObjetosSimulables.Add(obj);
                            viewModel.CrearUserControlDesdeObjetoSimulable(obj);
                            viewModel.HasUnsavedChanges = true;

                            newlyPastedObjects.Add(obj);
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show($"Error al procesar objeto {obj.Nombre}: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
                        }
                    }

                    // Usar la misma lógica de selección que DuplicarUserControl
                    // Limpiar selección antes de seleccionar los nuevos objetos
                    _objectManager.ClearSelection();

                    // Forzar actualización completa del layout
                    ImagenEnTrabajoCanvas.UpdateLayout();

                    // Usar dispatcher con la misma prioridad que DuplicarUserControl
                    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
                    {
                        // Seleccionar todos los objetos pegados
                        foreach (var newObj in newlyPastedObjects)
                        {
                            if (newObj.VisualRepresentation != null)
                            {
                                double left = Canvas.GetLeft(newObj.VisualRepresentation);
                                double top = Canvas.GetTop(newObj.VisualRepresentation);

                                // Solo añadir a selección si el objeto tiene coordenadas válidas
                                if (!double.IsNaN(left) && !double.IsNaN(top) && !double.IsInfinity(left) && !double.IsInfinity(top))
                                {
                                    _objectManager.SelectObject(newObj);
                                }
                            }
                        }

                        // Forzar otra actualización del layout antes de actualizar visuales
                        ImagenEnTrabajoCanvas.UpdateLayout();

                        // Actualizar SelectedItemOsList si hay objetos pegados
                        if (newlyPastedObjects.Count > 0)
                        {
                            if (DataContext is MainViewModel vm)
                            {
                                vm.SelectedItemOsList = newlyPastedObjects.LastOrDefault();
                            }
                        }

                        // Actualizar visuales de selección
                        _objectManager.UpdateSelectionVisuals();

                        Console.WriteLine($"Pegados y seleccionados {newlyPastedObjects.Count} objeto(s)");
                    }));

                    Console.WriteLine($"Pegados {newlyPastedObjects.Count} objeto(s) desde el portapapeles");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error al pegar objetos: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private bool CanPasteFromClipboard()
        {
            if (!Clipboard.ContainsText())
                return false;

            try
            {
                string jsonString = Clipboard.GetText();

                // Validación básica del JSON
                if (string.IsNullOrWhiteSpace(jsonString) ||
                    (!jsonString.TrimStart().StartsWith("[") && !jsonString.TrimStart().StartsWith("{")))
                {
                    return false;
                }

                // Intentar una deserialización rápida para validar
                var settings = new JsonSerializerSettings
                {
                    TypeNameHandling = TypeNameHandling.Auto,
                    ObjectCreationHandling = ObjectCreationHandling.Replace,
                    ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
                };

                // Intentar deserializar como lista de objetos
                try
                {
                    var pastedObjects = JsonConvert.DeserializeObject<List<osBase>>(jsonString, settings);
                    return pastedObjects != null && pastedObjects.Any();
                }
                catch
                {
                    // Si falla como lista, intentar como objeto individual
                    try
                    {
                        var singleObject = JsonConvert.DeserializeObject<osBase>(jsonString, settings);
                        return singleObject != null;
                    }
                    catch
                    {
                        return false;
                    }
                }
            }
            catch
            {
                return false;
            }
        }
    }

    public class FloatValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            if (string.IsNullOrEmpty(value?.ToString()))
                return new ValidationResult(false, "El campo no puede estar vacío.");

            if (float.TryParse(value.ToString(), NumberStyles.Float, cultureInfo, out float result))
                return ValidationResult.ValidResult;
            else
                return new ValidationResult(false, "Ingrese un número válido.");
        }
    }
}