using MaddoShared; using Microsoft.Extensions.Logging; namespace CatalogLite; public sealed class LiteCatalogViewModel : ViewModelBase { private readonly CatalogConfigurationLoader _configurationLoader; private readonly PicSettings _picSettings; private readonly ImageCreationService _imageCreationService; private readonly ImageProcessingCoordinator _imageProcessingCoordinator; private readonly ILogger _logger; private CatalogLiteConfiguration? _configuration; private CancellationTokenSource? _processingTokenSource; private string _configurationPath = string.Empty; private string _sourcePath = string.Empty; private string _destinationPath = string.Empty; private string _processingStatus = "Carica una configurazione XML."; private string _speedCounter = "-"; private int _processedImagesCount; private int _totalImagesCount; private int _progressBarValue; private int _progressBarMaximum = 100; private bool _isProcessing; public LiteCatalogViewModel( CatalogConfigurationLoader configurationLoader, PicSettings picSettings, ImageCreationService imageCreationService, ImageProcessingCoordinator imageProcessingCoordinator, ILogger logger) { _configurationLoader = configurationLoader; _picSettings = picSettings; _imageCreationService = imageCreationService; _imageProcessingCoordinator = imageProcessingCoordinator; _logger = logger; LoadConfigurationCommand = new AsyncCommand(RequestLoadConfigurationAsync, () => !IsProcessing); SelectSourceFolderCommand = new AsyncCommand(RequestSourceFolderAsync, () => !IsProcessing); SelectDestinationFolderCommand = new AsyncCommand(RequestDestinationFolderAsync, () => !IsProcessing); StartProcessingCommand = new AsyncCommand(StartProcessingAsync, CanStartProcessing); StopProcessingCommand = new AsyncCommand(StopProcessingAsync, () => IsProcessing); } public event EventHandler? LoadConfigurationRequested; public event EventHandler? SelectSourceFolderRequested; public event EventHandler? SelectDestinationFolderRequested; public event EventHandler? ShowMessageRequested; public Action? UiInvoker { get; set; } public AsyncCommand LoadConfigurationCommand { get; } public AsyncCommand SelectSourceFolderCommand { get; } public AsyncCommand SelectDestinationFolderCommand { get; } public AsyncCommand StartProcessingCommand { get; } public AsyncCommand StopProcessingCommand { get; } public string ConfigurationPath { get => _configurationPath; private set { _configurationPath = value; NotifyPropertyChanged(); } } public string SourcePath { get => _sourcePath; set { _sourcePath = NormalizeDirectoryPath(value); NotifyPropertyChanged(); RaiseCommandStates(); } } public string DestinationPath { get => _destinationPath; set { _destinationPath = NormalizeDirectoryPath(value); NotifyPropertyChanged(); RaiseCommandStates(); } } public string ProcessingStatus { get => _processingStatus; private set { _processingStatus = value; NotifyPropertyChanged(); } } public string SpeedCounter { get => _speedCounter; private set { _speedCounter = value; NotifyPropertyChanged(); } } public int ProcessedImagesCount { get => _processedImagesCount; private set { _processedImagesCount = value; NotifyPropertyChanged(); } } public int TotalImagesCount { get => _totalImagesCount; private set { _totalImagesCount = value; NotifyPropertyChanged(); } } public int ProgressBarValue { get => _progressBarValue; private set { _progressBarValue = value; NotifyPropertyChanged(); } } public int ProgressBarMaximum { get => _progressBarMaximum; private set { _progressBarMaximum = Math.Max(1, value); NotifyPropertyChanged(); } } public bool IsProcessing { get => _isProcessing; private set { _isProcessing = value; NotifyPropertyChanged(); RaiseCommandStates(); } } public async Task LoadConfigurationFromFileAsync(string filePath) { try { var configuration = await Task.Run(() => _configurationLoader.Load(filePath, _picSettings)).ConfigureAwait(false); RunOnUiThread(() => { _configuration = configuration; ConfigurationPath = configuration.FilePath; SourcePath = configuration.SourcePath; DestinationPath = configuration.DestinationPath; ResetProgress("Configurazione caricata."); }); } catch (Exception ex) { _logger.LogError(ex, "Errore durante il caricamento della configurazione"); ShowMessage("Configurazione", $"Impossibile caricare la configurazione: {ex.GetBaseException().Message}"); } } public static string NormalizeDirectoryPath(string? path) { if (string.IsNullOrWhiteSpace(path)) { return string.Empty; } var trimmed = path.Trim().TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); return trimmed + Path.DirectorySeparatorChar; } private Task RequestLoadConfigurationAsync() { LoadConfigurationRequested?.Invoke(this, EventArgs.Empty); return Task.CompletedTask; } private Task RequestSourceFolderAsync() { SelectSourceFolderRequested?.Invoke(this, EventArgs.Empty); return Task.CompletedTask; } private Task RequestDestinationFolderAsync() { SelectDestinationFolderRequested?.Invoke(this, EventArgs.Empty); return Task.CompletedTask; } private bool CanStartProcessing() { return !IsProcessing && _configuration is not null && !string.IsNullOrWhiteSpace(SourcePath) && !string.IsNullOrWhiteSpace(DestinationPath); } private async Task StartProcessingAsync() { if (_configuration is null) { ShowMessage("Configurazione", "Carica prima una configurazione XML."); return; } if (!Directory.Exists(SourcePath)) { ShowMessage("Sorgente", "La cartella sorgente non esiste."); return; } try { Directory.CreateDirectory(DestinationPath); } catch (Exception ex) { ShowMessage("Destinazione", $"Impossibile usare la cartella destinazione: {ex.GetBaseException().Message}"); return; } _processingTokenSource?.Dispose(); _processingTokenSource = new CancellationTokenSource(); var token = _processingTokenSource.Token; var options = CatalogConfigurationLoader.CloneOptions(_configuration.Options, SourcePath, DestinationPath); _picSettings.DirectorySorgente = SourcePath; _picSettings.DirectoryDestinazione = DestinationPath; _picSettings.DestDir = new DirectoryInfo(DestinationPath); _picSettings.ImageCreatorProvider = "ImageSharp"; IsProcessing = true; ResetProgress("Analisi immagini..."); try { var total = await Task.Run(() => _imageCreationService.GetFilesToProcessPublic(options).Count, token).ConfigureAwait(false); RunOnUiThread(() => { TotalImagesCount = total; ProgressBarMaximum = Math.Max(1, total); ProcessingStatus = total == 0 ? "Nessuna immagine trovata." : "Elaborazione in corso..."; }); if (total == 0) { return; } var result = await _imageProcessingCoordinator.RunAsync( options, token, update => RunOnUiThread(() => { ProcessedImagesCount = update.Processed; TotalImagesCount = update.Total; ProgressBarMaximum = update.Total; ProgressBarValue = update.Processed; ProcessingStatus = update.Status; }), speed => RunOnUiThread(() => SpeedCounter = speed)).ConfigureAwait(false); RunOnUiThread(() => { SpeedCounter = result.FinalSpeedCounter; ProcessingStatus = "Finito."; }); } catch (OperationCanceledException) { RunOnUiThread(() => ProcessingStatus = "Operazione annullata."); } catch (Exception ex) { _logger.LogError(ex, "Errore durante l'elaborazione"); RunOnUiThread(() => ProcessingStatus = $"Errore: {ex.GetBaseException().Message}"); } finally { _processingTokenSource?.Dispose(); _processingTokenSource = null; RunOnUiThread(() => IsProcessing = false); } } private Task StopProcessingAsync() { _processingTokenSource?.Cancel(); ProcessingStatus = "Arresto in corso..."; return Task.CompletedTask; } private void ResetProgress(string status) { ProcessingStatus = status; ProcessedImagesCount = 0; TotalImagesCount = 0; ProgressBarValue = 0; ProgressBarMaximum = 100; SpeedCounter = "-"; } private void RunOnUiThread(Action action) { if (UiInvoker is null) { action(); return; } UiInvoker(action); } private void ShowMessage(string title, string message) { RunOnUiThread(() => ShowMessageRequested?.Invoke(this, new LiteMessageEventArgs(title, message))); } private void RaiseCommandStates() { LoadConfigurationCommand.RaiseCanExecuteChanged(); SelectSourceFolderCommand.RaiseCanExecuteChanged(); SelectDestinationFolderCommand.RaiseCanExecuteChanged(); StartProcessingCommand.RaiseCanExecuteChanged(); StopProcessingCommand.RaiseCanExecuteChanged(); } }