using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Input;
using Ookii.Dialogs.Wpf;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using CtrEditor.ObjetosSim;
using LibS7Adv;
using System.IO;
using Newtonsoft.Json;
using System.Windows;
using CtrEditor.Simulacion;
using System.Diagnostics;
using System.Reflection;
using CommunityToolkit.Mvvm.ComponentModel;
using Xceed.Wpf.Toolkit.PropertyGrid;
using CtrEditor.ObjetosSim.Extraccion_Datos;
using ClosedXML.Excel;
using CtrEditor.PopUps;
using System.Windows.Data;
using CommunityToolkit.Mvvm.Input;
using System.Text.RegularExpressions;
using System.Collections.Specialized;


namespace CtrEditor
{

    public partial class MainViewModel : ObservableObject
    {
        public Stopwatch stopwatch_Sim;

        private double stopwatch_SimPLC_last;
        private double stopwatch_SimModel_last;

        private float TiempoDesdeStartSimulacion;
        private bool Debug_SimulacionCreado = false;

        public SimulationManagerFP simulationManager = new SimulationManagerFP();

        private readonly DispatcherTimer _timerSimulacion;
        private readonly DispatcherTimer _timerPLCUpdate;

        public Canvas MainCanvas;

        [ObservableProperty]
        private bool isConnected;

        partial void OnPLCViewModelChanged(PLCViewModel value)
        {
            if (value != null)
            {
                value.PropertyChanged += PLCViewModel_PropertyChanged;
                IsConnected = value.IsConnected;
            }
        }

        private void PLCViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(PLCViewModel.IsConnected))
            {
                IsConnected = PLCViewModel.IsConnected;
            }
        }

        [ObservableProperty]
        private DatosDeTrabajo datosDeTrabajo;

        [ObservableProperty]
        private ObservableCollection<string> listaImagenes;  // Publicación de las claves del diccionario        

        [ObservableProperty]
        public ObservableCollection<TipoSimulable> listaOsBase;

        public ICommand StartSimulationCommand { get; }
        public ICommand StopSimulationCommand { get; }
        public ICommand ItemDoubleClickCommand { get; private set; }

        public ICommand TBStartSimulationCommand { get; }
        public ICommand TBStopSimulationCommand { get; }
        public ICommand TBSaveCommand { get; }
        public ICommand TBExtractTagsCommand { get; }

        public ICommand TBEliminarUserControlCommand { get; }
        public ICommand TBDuplicarUserControlCommand { get; }

        public ICommand OpenWorkDirectoryCommand { get; }

        public ICommand TBEliminarTodosCommand { get; }
        public ICommand TBEliminarAutoCreatedCommand { get; }
        public ICommand TBEliminarClonedCommand { get; }
        public ICommand TBAssingPagesCommand { get; }
        public ICommand TBMultiPageExtractTagsCommand { get; }
        public ICommand TBMultiPageAnalizeCommand { get; }
        public ICommand TBMultiPageMatrixCommand { get; }


        public ICommand TBTogglePLCConnectionCommand => new RelayCommand(() =>
        {
            if (IsConnected)
                DisconnectPLC();
            else
                ConnectPLC();
        });

        // Evento que se dispara cuando se selecciona una nueva imagen
        public event EventHandler<string> ImageSelected;
        public event EventHandler<TickSimulacionEventArgs> TickSimulacion;

        // Propiedades

        private bool habilitarEliminarUserControl;

        private MainWindow mainWindow;
        private ObjectManipulationManager _objectManager;  // Add this line

        public MainWindow MainWindow
        { 
            get => mainWindow;
            set
            {
                mainWindow = value;
                _objectManager = mainWindow._objectManager;  // Initialize _objectManager
            }
        }

        [ObservableProperty]
        public ICollectionView vistaFiltrada;

        [ObservableProperty]
        private float canvasLeft;

        [ObservableProperty]
        private float canvasTop;

        [ObservableProperty]
        private bool isSimulationRunning;

        [ObservableProperty]
        private bool hasUnsavedChanges;

        [ObservableProperty]
        private ObservableCollection<string> recentDirectories;

        private bool inhibitSaveChangesControl;

        public ICommand OpenRecentDirectoryCommand { get; private set; }

        partial void OnIsSimulationRunningChanged(bool value)
        {
            CommandManager.InvalidateRequerySuggested(); // Notificar que el estado de los comandos ha cambiado
        }

        partial void OnHasUnsavedChangesChanged(bool value)
        {
            // Notificar el cambio del título indirectamente a través de directorioTrabajo
            OnPropertyChanged(nameof(directorioTrabajo));
        }

        public string directorioTrabajo
        {
            get => EstadoPersistente.Instance.directorio;
            set
            {
                if (value != null)
                {
                    EstadoPersistente.Instance.directorio = value; // Actualizar el estado persistente
                    EstadoPersistente.Instance.GuardarEstado();  // Guardar el estado actualizado
                    DatosDeTrabajo.CargarImagenes();
                    ListaImagenes = new ObservableCollection<string>(DatosDeTrabajo.Imagenes.Keys);  // Actualizar claves
                                                                                                     // Si no hay imágenes en el directorio, copiar base.png desde los recursos
                    if (!ListaImagenes.Any())
                    {
                        CopiarImagenBase(value);
                        // Recargar las imágenes después de copiar la imagen base
                        DatosDeTrabajo.CargarImagenes();
                        ListaImagenes = new ObservableCollection<string>(DatosDeTrabajo.Imagenes.Keys);  // Actualizar claves nuevamente
                    }

                    SelectedImage = null;
                    var x = ListaImagenes.FirstOrDefault(o => o == EstadoPersistente.Instance.imagen, null);
                    if (EstadoPersistente.Instance.imagen != null && EstadoPersistente.Instance.imagen.Length > 0 && x != null)
                        SelectedImage = EstadoPersistente.Instance.imagen;
                    else if (ListaImagenes.FirstOrDefault() != null)
                        SelectedImage = ListaImagenes.FirstOrDefault();

                    OnPropertyChanged(nameof(directorioTrabajo));  // Notificar el cambio de propiedad
                    OnPropertyChanged(nameof(ListaImagenes)); // Notificar que la lista de imágenes ha cambiado

                    AddToRecentDirectories(value);
                }
            }
        }

        // Función para copiar la imagen base desde los recursos
        private void CopiarImagenBase(string directorio)
        {
            try
            {
                // Obtener el path del archivo base.png desde los recursos (dentro del ensamblado)
                var assembly = Assembly.GetExecutingAssembly();
                var resourcePath = "CtrEditor.Images.base.png";  // Ajusta el namespace y el path según tu proyecto

                using (Stream resourceStream = assembly.GetManifestResourceStream(resourcePath))
                {
                    if (resourceStream != null)
                    {
                        string destino = Path.Combine(directorio, "base.png");

                        using (FileStream fileStream = new FileStream(destino, FileMode.Create, FileAccess.Write))
                        {
                            resourceStream.CopyTo(fileStream);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error al copiar la imagen base: {ex.Message}");
            }
        }

        [RelayCommand]
        public void DebugWindow()
        {
            MainWindow.DebugWindow();
        }

        [ObservableProperty]
        private PLCViewModel pLCViewModel;

        [ObservableProperty]
        private string selectedImage;

        partial void OnSelectedImageChanging(string? oldValue, string newValue)
        {
            if (HasUnsavedChanges && !inhibitSaveChangesControl)
            {
                var result = MessageBox.Show("There are unsaved changes. Do you want to save them?", 
                    "Save Changes", 
                    MessageBoxButton.YesNo);
                
                if (result == MessageBoxResult.Yes)
                {
                    SaveStateObjetosSimulables();
                }
            }
        }
        partial void OnSelectedImageChanged(string value)
        {
            if (value != null)
            {
                StopSimulation();
                ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]);
                LoadStateObjetosSimulables();
                EstadoPersistente.Instance.imagen = value;
                EstadoPersistente.Instance.GuardarEstado();
                HasUnsavedChanges = false;
            }
        }

        [ObservableProperty]
        private osBase selectedItemOsList;

        partial void OnSelectedItemOsListChanged(osBase value)
        {
            if (value != null)
                habilitarEliminarUserControl = true;
            else
                habilitarEliminarUserControl = false;
        }

        [ObservableProperty]
        public ObjetosSimulablesFilterTypes osListFilter;        

        [ObservableProperty]
        private TipoSimulable selectedItem;

        public ICollectionView ObjetosSimulablesFiltered { get; }
        public ICollectionView ObjetosSimulablesAllPages { get; }

        [ObservableProperty]
        public ObservableCollection<osBase> objetosSimulables;

        [ObservableProperty]
        private bool isMultiSelectionActive;

        partial void OnIsMultiSelectionActiveChanged(bool value)
        {
            _objectManager?.OnMultiSelectionModeChanged(value);
        }

        partial void OnObjetosSimulablesChanged(ObservableCollection<osBase> value)
        {
            VistaFiltrada = CollectionViewSource.GetDefaultView(ObjetosSimulables);
            VistaFiltrada.Filter = FiltrarObjetos;
            ObjetosSimulables.CollectionChanged += (s, e) => 
            {
                VistaFiltrada.Refresh();
                if (!inhibitSaveChangesControl && e.Action != NotifyCollectionChangedAction.Move)
                    HasUnsavedChanges = true;
            };
        }

        //
        // Constructor
        //

        private StateManager _stateManager;

        public MainViewModel()
        {
            OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory);
            datosDeTrabajo = new DatosDeTrabajo();

            OsListFilter = new ObjetosSimulablesFilterTypes();
            OsListFilter.All = true;

            osListFilter.PropertyChanged += OsListFilter_PropertyChanged;

            ObjetosSimulables = new ObservableCollection<osBase>();            

            ListaOsBase = new ObservableCollection<TipoSimulable>();

            // Inicializa el PLCViewModel
            PLCViewModel = new PLCViewModel();

            _timerPLCUpdate = new DispatcherTimer();
            _timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(10); // ajusta el intervalo según sea necesario
            _timerPLCUpdate.Tick += OnRefreshEvent;          

            InitializeTipoSimulableList();

            ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick);

            _timerSimulacion = new DispatcherTimer();
            _timerSimulacion.Interval = TimeSpan.FromMilliseconds(10); // ajusta el intervalo según sea necesario
            _timerSimulacion.Tick += OnTickSimulacion;

            StartSimulationCommand = new RelayCommand(StartSimulation);
            StopSimulationCommand = new RelayCommand(StopSimulation);

            TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning);
            TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning);
            TBSaveCommand = new RelayCommand(Save);

            TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl);
            TBDuplicarUserControlCommand = new RelayCommand(DuplicarUserControl, () => habilitarEliminarUserControl);

            TBEliminarTodosCommand = new RelayCommand(EliminarTodosCommand);
            TBEliminarAutoCreatedCommand = new RelayCommand(EliminarAutoCreatedCommand);
            TBEliminarClonedCommand = new RelayCommand(EliminarClonedCommand);
            TBAssingPagesCommand = new RelayCommand(AssingPagesCommand);
            TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand);
            TBMultiPageMatrixCommand = new RelayCommand(MultiPageMatrixCommand);

            stopwatch_Sim = new Stopwatch();
            stopwatch_Sim.Start();

            ObjetosSimulables.CollectionChanged += (s, e) =>
            {
                if (e.Action != NotifyCollectionChangedAction.Move)
                    HasUnsavedChanges = true;
            };

            recentDirectories = new ObservableCollection<string>(EstadoPersistente.Instance.RecentDirectories);
            OpenRecentDirectoryCommand = new RelayCommand<string>(OpenRecentDirectory);

            _stateManager = new StateManager(EstadoPersistente.Instance.directorio, this);
        }

        private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e)
        {
            VistaFiltrada.Refresh();
        }

        private bool FiltrarObjetos(object item)
        {
            var objeto = item as osBase;

            return osListFilter.All || (objeto.Cloned == osListFilter.Cloned) &&
                   (objeto.Enable_On_All_Pages == osListFilter.Enable_On_All_Pages) &&
                   (objeto.Show_On_This_Page == osListFilter.Show_On_This_Page);
        }

        public void LoadInitialData()
        {
            // Suponiendo que "SelectedImage" es una propiedad que al establecerse dispara "ImageSelected"
            directorioTrabajo = EstadoPersistente.Instance.directorio;
        }

        // Crear un nuevo Objeto
        private void ExecuteDoubleClick(object parameter)
        {
            if (parameter is TipoSimulable tipoSimulable)
            {
                CrearObjetoSimulableEnCentroCanvas(tipoSimulable.Tipo);
            }
        }

        public void CrearObjetoSimulableEnCentroCanvas(Type tipoSimulable)
        {
            var CentroCanvas = MainWindow.ObtenerCentroCanvasMeters();
            CrearObjetoSimulable(tipoSimulable, CentroCanvas.X, CentroCanvas.Y);
        }

        public osBase CrearObjetoSimulable(Type tipoSimulable, float Left, float Top)
        {
            // Crear una nueva instancia del osBase correspondiente
            osBase? NuevoOsBase = UserControlFactory.GetInstanceForType(tipoSimulable);

            NuevoOsBase.Left = Left;
            NuevoOsBase.Top = Top;

            if (NuevoOsBase != null)
            {
                if (CrearUserControlDesdeObjetoSimulable(NuevoOsBase))
                {
                    // Añadir el nuevo osBase a la colección de objetos simulables
                    ObjetosSimulables.Add(NuevoOsBase);
                    HasUnsavedChanges = true;
                }
            }
            return NuevoOsBase;
        }

        // Crear UserControl desde osBase : Nuevo o desde Deserealizacion
        private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto)
        {
            Type tipoObjeto = osObjeto.GetType();

            // Obtén el UserControl correspondiente para el tipo de objeto
            UserControl? userControl = UserControlFactory.GetControlForType(tipoObjeto);

            if (userControl != null)
            {
                // Asignar los datos al UserControl
                UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager);
                osObjeto._mainViewModel = this;
                if (osObjeto.Id == null) // Para los objetos salvados antes de usar UniqueID
                    osObjeto.Id = new UniqueId().ObtenerNuevaID();

                MainWindow.AgregarRegistrarUserControlCanvas(userControl);

                return true;
            }
            return false;
        }

        public void RemoverObjetoSimulable(osBase osObjeto)
        {
            if (osObjeto != null && ObjetosSimulables.Contains(osObjeto))
            {
                ObjetosSimulables.Remove(osObjeto);
                if (osObjeto.VisualRepresentation != null)
                    MainWindow.EliminarUserControlDelCanvas(osObjeto.VisualRepresentation);
                HasUnsavedChanges = true;
            }
        }

        private void DuplicarUserControl()
        {
            if (SelectedItemOsList is osBase objDuplicar)
                DuplicarObjeto(objDuplicar, 0.5f, 0.5f);
        }

        public osBase DuplicarObjeto(osBase objDuplicar, float OffsetX, float OffsetY)
        {
            StopSimulation();
            DisconnectPLC();

            osBase? NuevoObjetoDuplicado = null;

            objDuplicar.SalvarDatosNoSerializables();

            var settings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore,
                TypeNameHandling = TypeNameHandling.All
            };

            try
            {
                // Serializar
                var serializedData = JsonConvert.SerializeObject(objDuplicar, settings);
                // Duplicar
                NuevoObjetoDuplicado = JsonConvert.DeserializeObject<osBase>(serializedData, settings);
                if (NuevoObjetoDuplicado != null)
                {
                    NuevoObjetoDuplicado.Id.ObtenerNuevaID();
                    string nombre = Regex.IsMatch(NuevoObjetoDuplicado.Nombre, @"_\d+$")
                        ? Regex.Replace(NuevoObjetoDuplicado.Nombre, @"_\d+$", $"_{NuevoObjetoDuplicado.Id.Value}")
                        : NuevoObjetoDuplicado.Nombre + "_" + NuevoObjetoDuplicado.Id.Value;

                    NuevoObjetoDuplicado.Nombre = nombre;
                    NuevoObjetoDuplicado.Left += OffsetX;
                    NuevoObjetoDuplicado.Top += OffsetY;
                    ObjetosSimulables.Add(NuevoObjetoDuplicado);
                    CrearUserControlDesdeObjetoSimulable(NuevoObjetoDuplicado);
                    HasUnsavedChanges = true;
                }
            }
            catch
            {
                // Log error or handle it accordingly
            }
            finally
            {
                objDuplicar.RestaurarDatosNoSerializables();
            }
            return NuevoObjetoDuplicado;
        }


        public void EliminarObjetoSeleccionado()
        {
            if (SelectedItemOsList is osBase objEliminar)
            {
                var result = MessageBox.Show($"¿Está seguro que desea eliminar el objeto '{objEliminar.Nombre}'?", 
                    "Confirmar eliminación", 
                    MessageBoxButton.YesNo, 
                    MessageBoxImage.Question);

                if (result == MessageBoxResult.Yes)
                {
                    RemoverObjetoSimulable(objEliminar);
                }
            }
        }

        private void EliminarUserControl()
        {
            EliminarObjetoSeleccionado();
        }


        private void EliminarTodosCommand()
        {
            var objetosSimulablesCopy = new List<osBase>(ObjetosSimulables);
            foreach (var obj in objetosSimulablesCopy)
                RemoverObjetoSimulable(obj);
        }

        private void EliminarAutoCreatedCommand()
        {
            var osAutoCreated_List = ObjetosSimulables
                                            .Where(o => o.Show_On_This_Page && o.AutoCreated)
                                            .ToList();
            foreach (var obj in osAutoCreated_List)
                    RemoverObjetoSimulable(obj);
        }

        private void EliminarClonedCommand()
        {
            var osCloned_List = ObjetosSimulables
                                                .Where(o => o.Show_On_This_Page && o.Cloned)
                                                .ToList();
            foreach (var obj in osCloned_List)
                    RemoverObjetoSimulable(obj);
        }

        private void AssingPagesCommand()
        {
            var assignImagesWindow = new AssignImagesWindow();
            var assignImagesViewModel = new AssignImagesViewModel();
            assignImagesViewModel.Initialize(this, assignImagesWindow);
            assignImagesWindow.DataContext = assignImagesViewModel;
            assignImagesWindow.ShowDialog();
            if (assignImagesWindow.DataContext is AssignImagesViewModel dialog && dialog.CloseOK)
                SaveStateObjetosSimulables();
        }

        public async Task WaitForUIUpdateAsync()
        {
            await Task.Yield();
            Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);
        }

        private async void MultiPageAnalizeCommand()
        {
            var ImagenesSeleccionadas = new ObservableCollection<string>
            {
                SelectedImage
            };

            StopSimulation();

            var selectPagesWindow = new SelectPages();
            var selectPagesViewModel = new SelectPagesViewModel();
            selectPagesViewModel.Initialize(this, selectPagesWindow, ref ImagenesSeleccionadas);
            selectPagesWindow.DataContext = selectPagesViewModel;
            selectPagesWindow.ShowDialog();

            bool originalHasUnsavedChanges = HasUnsavedChanges;
            HasUnsavedChanges = false;

            try
            {
                if (selectPagesWindow.DataContext is SelectPagesViewModel dialog && dialog.CloseOK)
                {
                    SaveStateObjetosSimulables();  // Guarda el estado antes de cambiar la imagen

                    foreach (var page in ImagenesSeleccionadas)
                    {
                        SelectedImage = page;
                        await WaitForUIUpdateAsync(); // Espera a que la UI se actualice
                        AnalizePageCommand();
                        await WaitForUIUpdateAsync(); // Espera a que la UI se actualice

                        SaveStateObjetosSimulables();  // Guarda el estado antes de cambiar la imagen
                    }
                }
            }
            finally
            {
                HasUnsavedChanges = originalHasUnsavedChanges;
            }

        }

        private void AnalizePageCommand()
        {
            foreach (var obj in ObjetosSimulables)
                if (obj is osBuscarCoincidencias objBC)
                    if (objBC.Show_On_This_Page)
                        objBC.BuscarCoincidencias();

        }


        private void InitializeTipoSimulableList()
        {
            var baseType = typeof(osBase);
            var types = AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(assembly => assembly.GetTypes())
                .Where(type => type.IsSubclassOf(baseType) && !type.IsAbstract && typeof(IosBase).IsAssignableFrom(type));

            foreach (var type in types)
            {
                var methodInfo = type.GetMethod("NombreClase", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
                string nombre = methodInfo != null ? methodInfo.Invoke(null, null)?.ToString() : "Nombre no encontrado";

                ListaOsBase.Add(new TipoSimulable { Nombre = nombre, Tipo = type });
            }
        }


        private void StartSimulation()
        {
            IsSimulationRunning = true;

            foreach (var objetoSimulable in ObjetosSimulables)
                objetoSimulable.UpdateGeometryStart();

            simulationManager.Debug_DrawInitialBodies();
            TiempoDesdeStartSimulacion = 0;
            Debug_SimulacionCreado = true;

            _timerSimulacion.Start();
            simulationManager.Start();
        }

        private void StopSimulation()
        {
            IsSimulationRunning = false;

            foreach (var objetoSimulable in ObjetosSimulables)
                objetoSimulable.SimulationStop();

            if (Debug_SimulacionCreado)
            {
                simulationManager.Debug_ClearSimulationShapes();
                Debug_SimulacionCreado = false;
            }
            _timerSimulacion.Stop();
        }

        private void OnTickSimulacion(object sender, EventArgs e)
        {
            var stopwatch = Stopwatch.StartNew(); // Start measuring time

            // Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
            var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimModel_last;
            stopwatch_SimModel_last = stopwatch_Sim.Elapsed.TotalMilliseconds;

            // Eliminar el diseño de Debug luego de 2 segundos
            if (TiempoDesdeStartSimulacion > 12000)
                simulationManager.Debug_ClearSimulationShapes();
            else
                TiempoDesdeStartSimulacion += (float)elapsedMilliseconds;

            foreach (var objetoSimulable in ObjetosSimulables)
                objetoSimulable.UpdateGeometryStep();

            simulationManager.Step();

            var objetosSimulablesCopy = new List<osBase>(ObjetosSimulables);

            foreach (var objetoSimulable in objetosSimulablesCopy)
            {
                if (!objetoSimulable.RemoverDesdeSimulacion)
                    objetoSimulable.UpdateControl((int)elapsedMilliseconds);
                else
                    RemoverObjetoSimulable(objetoSimulable);
            }

            stopwatch.Stop(); // Stop measuring time
            //Debug.WriteLine($"OnTickSimulacion execution time: {stopwatch.TotalMilliseconds} ms");
        }

        private void ConnectPLC()
        {
            _timerPLCUpdate.Start();
            PLCViewModel.Connect();
            foreach (var objetoSimulable in ObjetosSimulables)
                objetoSimulable.SetPLC(PLCViewModel);
        }

        private void DisconnectPLC()
        {
            PLCViewModel.Disconnect();
            _timerPLCUpdate.Stop();
            foreach (var objetoSimulable in ObjetosSimulables)
                objetoSimulable.SetPLC(null);

        }

        private List<osBase> objetosSimulablesLlamados = new List<osBase>();

        private void OnRefreshEvent(object sender, EventArgs e)
        {
            var stopwatch = Stopwatch.StartNew(); // Start measuring time

            if (PLCViewModel.IsConnected)
            {
                // Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
                var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimPLC_last;
                stopwatch_SimPLC_last = stopwatch_Sim.Elapsed.TotalMilliseconds;

                // Reiniciar el cronómetro para la próxima medición
                var remainingObjetosSimulables = ObjetosSimulables.Except(objetosSimulablesLlamados).ToList();

                foreach (var objetoSimulable in remainingObjetosSimulables)
                {
                    var objStopwatch = Stopwatch.StartNew();
                    objetoSimulable.UpdatePLC(PLCViewModel, (int)elapsedMilliseconds);
                    objStopwatch.Stop();

                    objetosSimulablesLlamados.Add(objetoSimulable);

                    if (stopwatch.Elapsed.TotalMilliseconds >= 10)
                        break;
                }

                if (remainingObjetosSimulables.Count == 0)
                {
                    objetosSimulablesLlamados.Clear();
                }
            }

            stopwatch.Stop(); // Stop measuring time
            Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms");
        }

        private void OpenWorkDirectory()
        {
            var dialog = new VistaFolderBrowserDialog();
            if (dialog.ShowDialog() == true) // Mostrar el diálogo y comprobar si el resultado es positivo
            {
                directorioTrabajo = dialog.SelectedPath;  // Actualizar la propiedad que también actualiza el estado persistente
            }
        }

        private void OpenRecentDirectory(string path)
        {
            if (Directory.Exists(path))
            {
                directorioTrabajo = path;
            }
            else
            {
                MessageBox.Show($"Directory not found: {path}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                RecentDirectories.Remove(path);
                UpdateRecentDirectories();
            }
        }

        private void UpdateRecentDirectories()
        {
            EstadoPersistente.Instance.RecentDirectories = RecentDirectories.ToList();
            EstadoPersistente.Instance.GuardarEstado();
        }

        private void AddToRecentDirectories(string path)
        {
            // Remove the path if it already exists
            RecentDirectories.Remove(path);
            
            // Add the new path at the beginning
            RecentDirectories.Insert(0, path);
            
            // Keep only the last 10 entries
            while (RecentDirectories.Count > 10)
            {
                RecentDirectories.RemoveAt(RecentDirectories.Count - 1);
            }
            
            UpdateRecentDirectories();
        }

        // 
        //  Lista de osBase
        //

        public void Save()
        {
            SaveStateObjetosSimulables();
        }

        public void SaveStateObjetosSimulables()
        {
            if (SelectedImage != null)
            {
                StopSimulation();
                DisconnectPLC();

                ObservableCollection<osBase> _objetosSimulables = new ObservableCollection<osBase>();
                ObservableCollection<osBase> _objetosSimulablesAllPages = new ObservableCollection<osBase>();

                foreach (var obj in ObjetosSimulables)
                {
                    // Guardar referencias temporales
                    obj.SalvarDatosNoSerializables();
                    if (!obj.Enable_On_All_Pages)
                        _objetosSimulables.Add(obj);
                    else
                        _objetosSimulablesAllPages.Add(obj);
                }

                // Salvar los objetos de la pagina actual

                // Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter y PLC_ConnectionData
                var dataToSerialize = new SimulationData
                {
                    ObjetosSimulables = _objetosSimulables,
                    UnitConverter = PixelToMeter.Instance.calc,
                    PLC_ConnectionData = PLCViewModel
                };

                // Ruta del archivo a ser guardado
                var path = DatosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json");
                if (path != null)
                    SerializarYSalvar(dataToSerialize, path);

                // Salvar los objetos de todas las paginas

                // Ruta del archivo a ser guardado
                path = DatosDeTrabajo.ObtenerPathAllPages(".json");
                if (path != null)
                    SerializarYSalvar(_objetosSimulablesAllPages, path);

                // Restaurar las propiedades originales de los objetos
                foreach (var obj in ObjetosSimulables)
                    obj.RestaurarDatosNoSerializables();

                HasUnsavedChanges = false;
            }
        }

        private void SerializarYSalvar(object listaObjetos, string path)
        {

            // Verificar si el archivo ya existe y crear un respaldo
            if (File.Exists(path))
            {
                var backupPath = Path.ChangeExtension(path, ".bak");
                File.Copy(path, backupPath, true);  // Copia el archivo existente a un nuevo archivo .bak, sobrescribiendo si es necesario
            }


            var settings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented,
                // PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                NullValueHandling = NullValueHandling.Ignore,
                TypeNameHandling = TypeNameHandling.Auto
            };

            // Serializar
            var serializedData = JsonConvert.SerializeObject(listaObjetos, settings);
            File.WriteAllText(path, serializedData);  // Escribir el nuevo archivo JSON
        }

        public void LoadStateObjetosSimulables()
        {
            try
            {
                StopSimulation();
                DisconnectPLC();
                ObjetosSimulables.Clear();
                simulationManager.Clear();
                if (SelectedImage != null)
                {
                    var settings = new JsonSerializerSettings
                    {
                        TypeNameHandling = TypeNameHandling.Auto,
                        ObjectCreationHandling = ObjectCreationHandling.Replace,
                        // PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                        ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
                    };

                    string jsonPath = datosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json");
                    if (File.Exists(jsonPath))
                    {
                        string jsonString = File.ReadAllText(jsonPath);


                        var simulationData = JsonConvert.DeserializeObject<SimulationData>(jsonString, settings);
                        if (simulationData != null)
                        {
                            if (simulationData.ObjetosSimulables is not null)
                                ObjetosSimulables = simulationData.ObjetosSimulables;

                            if (simulationData.PLC_ConnectionData is not null)
                                PLCViewModel = simulationData.PLC_ConnectionData;
                            else
                                PLCViewModel = new PLCViewModel();

                            PixelToMeter.Instance.calc = simulationData.UnitConverter;

                        }
                    }

                    jsonPath = DatosDeTrabajo.ObtenerPathAllPages(".json");
                    if (File.Exists(jsonPath))
                    {
                        string jsonString = File.ReadAllText(jsonPath);

                        ObservableCollection<osBase> _objetosSimulablesAllPages = new ObservableCollection<osBase>();

                        _objetosSimulablesAllPages = JsonConvert.DeserializeObject<ObservableCollection<osBase>>(jsonString, settings);
                        if (_objetosSimulablesAllPages != null)
                            foreach (var obj in _objetosSimulablesAllPages)
                                ObjetosSimulables.Add(obj);
                    }
                    // Recorrer la colección de objetos simulables
                    foreach (var objetoSimulable in ObjetosSimulables)
                        if (objetoSimulable != null)
                        {
                        objetoSimulable.CheckData();
                            CrearUserControlDesdeObjetoSimulable(objetoSimulable);
                        }
                }
            }
            catch { /* Consider logging the error or handling it appropriately */ }
        }

        // Se cargan los datos de cada UserControl en el StackPanel
        public void CargarPropiedadesosDatos(osBase selectedObject, PropertyGrid PanelEdicion, ResourceDictionary Resources)
        {
            UserControlFactory.CargarPropiedadesosDatos(selectedObject, PanelEdicion);
        }

        private RelayCommand saveCommand;
        public ICommand SaveCommand => saveCommand ??= new RelayCommand(Save);

        private void Save(object commandParameter)
        {
        }

        private RelayCommand exitCommand;
        public ICommand ExitCommand => exitCommand ??= new RelayCommand(Exit);

        private void Exit()
        {
            if (HasUnsavedChanges)
            {
                var result = MessageBox.Show("There are unsaved changes. Do you want to save them?",
                    "Save Changes",
                    MessageBoxButton.YesNoCancel);

                if (result == MessageBoxResult.Cancel)
                    return;
                else if (result == MessageBoxResult.Yes)
                    SaveStateObjetosSimulables();
            }
            Application.Current.Shutdown();
        }

        private async void MultiPageMatrixCommand()
        {
            if (HasUnsavedChanges)
            {
                var result = MessageBox.Show("There are unsaved changes. Do you want to save them?",
                    "Save Changes",
                    MessageBoxButton.YesNoCancel);

                if (result == MessageBoxResult.Cancel)
                    return;
                else if (result == MessageBoxResult.Yes)
                    SaveStateObjetosSimulables();
            }

            var ImagenesSeleccionadas = new ObservableCollection<string>
            {
                SelectedImage
            };

            StopSimulation();

            var selectPagesWindow = new SelectPages();
            var selectPagesViewModel = new SelectPagesViewModel();
            selectPagesViewModel.Initialize(this, selectPagesWindow, ref ImagenesSeleccionadas);
            selectPagesWindow.DataContext = selectPagesViewModel;
            selectPagesWindow.ShowDialog();

            inhibitSaveChangesControl = true;
            try
            {
                if (selectPagesWindow.DataContext is SelectPagesViewModel dialog && dialog.CloseOK)
                {
                    var matrixPreviewWindow = new MatrixPreviewWindow();
                    var matrixPreviewViewModel = new MatrixPreviewViewModel();
                    matrixPreviewViewModel.Initialize(this, matrixPreviewWindow, ImagenesSeleccionadas);
                    matrixPreviewWindow.DataContext = matrixPreviewViewModel;
                    matrixPreviewWindow.ShowDialog();
                }
            }
            finally
            {
                inhibitSaveChangesControl = false;
            }
        }
    }
    public class SimulationData
    {
        public ObservableCollection<osBase>? ObjetosSimulables { get; set; }
        public UnitConverter? UnitConverter { get; set; }
        public PLCViewModel? PLC_ConnectionData { get; set; }
    }

    public class TipoSimulable
    {
        public string? Nombre { get; set; }
        public Type? Tipo { get; set; }
    }

    public class TickSimulacionEventArgs : EventArgs
    {
        // Aquí puedes agregar propiedades o campos para pasar información adicional
        // en el evento TickSimulacion
    }

}