using System; using System.Collections.ObjectModel; using System.IO; using System.Threading.Tasks; using Newtonsoft.Json; using System.Linq; using System.Windows.Controls; using System.Windows; using CtrEditor.ObjetosSim; using LibS7Adv; namespace CtrEditor { /// /// Metadata de una página, incluyendo información sobre la imagen y configuración /// public class PageMetadata { public string PageId { get; set; } public string PageName { get; set; } public string ImagePath { get; set; } public DateTime LastModified { get; set; } public Dictionary CustomMetadata { get; set; } = new(); } /// /// Estado de un objeto dentro de una página específica /// public class PageObjectState { public string GlobalObjectId { get; set; } public bool IsVisible { get; set; } = true; public float Left { get; set; } public float Top { get; set; } public float Rotation { get; set; } public Dictionary CustomProperties { get; set; } = new(); } /// /// Estado de una página específica /// public class PageState { public string PageId { get; set; } public string PageName { get; set; } public List LocalObjects { get; set; } = new(); public Dictionary GlobalObjectStates { get; set; } = new(); public Dictionary PageMetadata { get; set; } = new(); } /// /// Estado global de la aplicación /// public class GlobalState { public List SharedObjects { get; set; } = new(); public Dictionary PagesMetadata { get; set; } = new(); public PLCViewModel PLCConfiguration { get; set; } public UnitConverter UnitConverter { get; set; } public int LastUsedId { get; set; } } /// /// Gestor principal de estados de la aplicación /// public class StateManager : IDisposable { private readonly string _basePath; private GlobalState _globalState; private Dictionary _loadedPages; private string _currentPageId; private readonly object _lockObject = new(); private bool _hasUnsavedChanges; private MainViewModel _mainViewModel; public event EventHandler PageStateChanged; public event EventHandler GlobalStateChanged; public StateManager(string basePath, MainViewModel mainViewModel) { _basePath = basePath; _mainViewModel = mainViewModel; _loadedPages = new Dictionary(); Directory.CreateDirectory(basePath); } public async Task InitializeAsync() { await LoadGlobalStateAsync(); if (_mainViewModel.SelectedImage != null) { await LoadPageStateAsync(_mainViewModel.SelectedImage); } } private JsonSerializerSettings GetSerializerSettings() { return new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, ObjectCreationHandling = ObjectCreationHandling.Replace, Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore }; } public async Task LoadGlobalStateAsync() { var path = Path.Combine(_basePath, "global.json"); if (File.Exists(path)) { try { var json = await File.ReadAllTextAsync(path); _globalState = JsonConvert.DeserializeObject(json, GetSerializerSettings()); // Restaurar configuración global _mainViewModel.PLCViewModel = _globalState.PLCConfiguration ?? new PLCViewModel(); if (_globalState.UnitConverter != null) PixelToMeter.Instance.calc = _globalState.UnitConverter; else PixelToMeter.Instance.calc = new UnitConverter(1.0f); // Valor por defecto // Restaurar objetos globales foreach (var obj in _globalState.SharedObjects) { await RestoreObjectAsync(obj); } } catch (Exception ex) { // Log error y crear nuevo estado global _globalState = new GlobalState(); } } else { _globalState = new GlobalState(); } } public async Task SaveGlobalStateAsync() { var path = Path.Combine(_basePath, "global.json"); var backupPath = Path.ChangeExtension(path, ".bak"); // Actualizar estado global _globalState.PLCConfiguration = _mainViewModel.PLCViewModel; _globalState.UnitConverter = PixelToMeter.Instance.calc; // Crear backup if (File.Exists(path)) { File.Copy(path, backupPath, true); } try { // Preparar objetos para serialización foreach (var obj in _globalState.SharedObjects) { obj.SalvarDatosNoSerializables(); } var json = JsonConvert.SerializeObject(_globalState, GetSerializerSettings()); await File.WriteAllTextAsync(path, json); _hasUnsavedChanges = false; GlobalStateChanged?.Invoke(this, EventArgs.Empty); } finally { // Restaurar estado de objetos foreach (var obj in _globalState.SharedObjects) { obj.RestaurarDatosNoSerializables(); } } } public async Task LoadPageStateAsync(string pageId) { // Retornar página cacheada si existe if (_loadedPages.TryGetValue(pageId, out var cachedState)) { return cachedState; } var path = Path.Combine(_basePath, $"page_{pageId}.json"); try { PageState pageState; if (File.Exists(path)) { var json = await File.ReadAllTextAsync(path); pageState = JsonConvert.DeserializeObject(json, GetSerializerSettings()); } else { pageState = new PageState { PageId = pageId, PageName = _globalState.PagesMetadata.GetValueOrDefault(pageId)?.PageName ?? pageId }; } _loadedPages[pageId] = pageState; // Restaurar objetos locales foreach (var obj in pageState.LocalObjects) { await RestoreObjectAsync(obj); } // Aplicar estados de objetos globales ApplyGlobalObjectStates(pageState); return pageState; } catch (Exception ex) { // Log error throw; } } private void ApplyGlobalObjectStates(PageState pageState) { foreach (var kvp in pageState.GlobalObjectStates) { var globalObj = _globalState.SharedObjects.FirstOrDefault(o => o.Id.Value.ToString() == kvp.Key); if (globalObj != null) { var state = kvp.Value; globalObj.Left = state.Left; globalObj.Top = state.Top; globalObj.Angulo = state.Rotation; // Actualizar Show_On_This_Page que ahora maneja internamente showOnThisPagesList if (state.IsVisible && !globalObj.Show_On_This_Page) globalObj.Show_On_This_Page = true; else if (!state.IsVisible && globalObj.Show_On_This_Page) globalObj.Show_On_This_Page = false; // Aplicar propiedades personalizadas foreach (var prop in state.CustomProperties) { var property = globalObj.GetType().GetProperty(prop.Key); if (property != null && property.CanWrite) { property.SetValue(globalObj, prop.Value); } } } } } private async Task RestoreObjectAsync(osBase obj) { if (obj != null) { obj.CheckData(); await Task.Run(() => { Application.Current.Dispatcher.Invoke(() => { CrearUserControlDesdeObjetoSimulable(obj); }); }); } } private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto) { Type tipoObjeto = osObjeto.GetType(); UserControl userControl = UserControlFactory.GetControlForType(tipoObjeto); if (userControl != null) { UserControlFactory.AssignDatos(userControl, osObjeto, _mainViewModel.simulationManager); osObjeto._mainViewModel = _mainViewModel; if (osObjeto.Id == null) { osObjeto.Id = new UniqueId().ObtenerNuevaID(); } _mainViewModel.MainWindow.AgregarRegistrarUserControlCanvas(userControl); return true; } return false; } public async Task SavePageStateAsync(string pageId) { if (!_loadedPages.TryGetValue(pageId, out var pageState)) return; var path = Path.Combine(_basePath, $"page_{pageId}.json"); var backupPath = Path.ChangeExtension(path, ".bak"); // Crear backup if (File.Exists(path)) { File.Copy(path, backupPath, true); } // Actualizar estado de objetos globales pageState.GlobalObjectStates.Clear(); foreach (var obj in _mainViewModel.ObjetosSimulables.Where(o => o.Enable_On_All_Pages)) { var currentPageId = _mainViewModel.SelectedImage; pageState.GlobalObjectStates[obj.Id.Value.ToString()] = new PageObjectState { GlobalObjectId = obj.Id.Value.ToString(), IsVisible = obj.Show_On_This_Page, Left = obj.Left, Top = obj.Top, Rotation = obj.Angulo, CustomProperties = CaptureCustomProperties(obj) }; } try { // Preparar objetos para serialización foreach (var obj in pageState.LocalObjects) { obj.SalvarDatosNoSerializables(); } var json = JsonConvert.SerializeObject(pageState, GetSerializerSettings()); await File.WriteAllTextAsync(path, json); _hasUnsavedChanges = false; PageStateChanged?.Invoke(this, pageId); } finally { // Restaurar estado de objetos foreach (var obj in pageState.LocalObjects) { obj.RestaurarDatosNoSerializables(); } } } private Dictionary CaptureCustomProperties(osBase obj) { var customProps = new Dictionary(); var properties = obj.GetType().GetProperties() .Where(p => p.GetCustomAttributes(typeof(SerializeAttribute), true).Any()); foreach (var prop in properties) { customProps[prop.Name] = prop.GetValue(obj); } return customProps; } public async Task SaveAllAsync() { foreach (var pageId in _loadedPages.Keys) { await SavePageStateAsync(pageId); } await SaveGlobalStateAsync(); } public void Dispose() { if (_hasUnsavedChanges) { SaveAllAsync().Wait(); } } } /// /// Atributo para marcar propiedades que deben ser serializadas como propiedades personalizadas /// [AttributeUsage(AttributeTargets.Property)] public class SerializeAttribute : Attribute { } }