using ImageCatalog_2.Commands; using ImageCatalog_2.Services; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing.Text; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Input; using AutoMapper; using MaddoShared; using Microsoft.Extensions.Logging; namespace ImageCatalog_2 { public class DataModel : ViewModelBase { public ICommand TestCommand { get; } public ICommand AsyncTestCommand { get; } public ICommand AsyncCancelOperationCommand { get; } public ICommand ProcessImagesCommand { get; } public ICommand SelectSourceFolderCommand { get; } public ICommand SelectDestinationFolderCommand { get; } public ICommand SelectLogoFileCommand { get; } public ICommand SaveSettingsCommand { get; } public ICommand LoadSettingsCommand { get; } public ICommand SelectColorCommand { get; } private readonly ITestService _service; private readonly ILogger _logger; private readonly ISettingsService _settingsService; private readonly ImageCreationStuff _imageCreationService; private readonly PicSettings _picSettings; private readonly IMapper _mapper; // ComboBox collections public List AvailableFonts { get; } public List VerticalPositions { get; } = new() { "Alto", "Centro", "Basso" }; public List HorizontalAlignments { get; } = new() { "Sinistra", "Centro", "Destra" }; public DataModel(ITestService testService, ISettingsService settingsService, ImageCreationStuff imageCreationService, PicSettings picSettings, IMapper mapper, ILogger logger) { _service = testService; _logger = logger; _settingsService = settingsService; _imageCreationService = imageCreationService; _picSettings = picSettings; _mapper = mapper; TestCommand = new RelayCommand(Test); AsyncTestCommand = new AsyncCommand(TestAsync); AsyncCancelOperationCommand = new AsyncCommand(CancelOperation); ProcessImagesCommand = new AsyncCommand(ProcessImages); SelectSourceFolderCommand = new RelayCommand(SelectSourceFolder); SelectDestinationFolderCommand = new RelayCommand(SelectDestinationFolder); SelectLogoFileCommand = new RelayCommand(SelectLogoFile); SaveSettingsCommand = new RelayCommand(SaveSettings); LoadSettingsCommand = new RelayCommand(LoadSettings); SelectColorCommand = new RelayCommand(SelectColor); // Load available fonts AvailableFonts = LoadAvailableFonts(); } private List LoadAvailableFonts() { var fonts = new List(); using (var installedFonts = new InstalledFontCollection()) { fonts.AddRange(installedFonts.Families.Select(f => f.Name)); } return fonts; } private CancellationTokenSource? _mainToken; public CancellationTokenSource? MainToken { get => _mainToken; set { _mainToken = value; NotifyPropertyChanged(); } } private string _sourcePath; public string SourcePath { get => _sourcePath; set { _sourcePath = value; NotifyPropertyChanged(); } } private string _destinationPath; public string DestinationPath { get => _destinationPath; set { _destinationPath = value; NotifyPropertyChanged(); } } private string _horizontalText; public string HorizontalText { get => _horizontalText; set { _horizontalText = value; NotifyPropertyChanged(); } } private string _verticalText; public string VerticalText { get => _verticalText; set { _verticalText = value; NotifyPropertyChanged(); } } private bool _overwriteImages; public bool OverwriteImages { get => _overwriteImages; set { _overwriteImages = value; NotifyPropertyChanged(); } } private bool _uiEnabled = true; public bool UiEnabled { get => _uiEnabled; set { _uiEnabled = value; NotifyPropertyChanged(); NotifyPropertyChanged(nameof(UiDisabled)); } } public bool UiDisabled => !_uiEnabled; private string _speedCounter = "-"; public string SpeedCounter { get => _speedCounter; set { _speedCounter = value; NotifyPropertyChanged(); } } private int _chunkSize; public int ChunkSize { get => _chunkSize; set { _chunkSize = value; NotifyPropertyChanged(); } } private int _threadsCount; public int ThreadsCount { get => _threadsCount; set { _threadsCount = value; NotifyPropertyChanged(); } } // Thumbnail settings private string _thumbnailPrefix = "tn_"; public string ThumbnailPrefix { get => _thumbnailPrefix; set { _thumbnailPrefix = value; NotifyPropertyChanged(); } } private int _thumbnailHeight = 350; public int ThumbnailHeight { get => _thumbnailHeight; set { _thumbnailHeight = value; NotifyPropertyChanged(); } } private int _thumbnailWidth = 350; public int ThumbnailWidth { get => _thumbnailWidth; set { _thumbnailWidth = value; NotifyPropertyChanged(); } } // Big photo settings private int _photoBigHeight = 2240; public int PhotoBigHeight { get => _photoBigHeight; set { _photoBigHeight = value; NotifyPropertyChanged(); } } private int _photoBigWidth = 2240; public int PhotoBigWidth { get => _photoBigWidth; set { _photoBigWidth = value; NotifyPropertyChanged(); } } // Font settings private int _fontSize = 20; public int FontSize { get => _fontSize; set { _fontSize = value; NotifyPropertyChanged(); } } private int _fontSizeThumbnail = 50; public int FontSizeThumbnail { get => _fontSizeThumbnail; set { _fontSizeThumbnail = value; NotifyPropertyChanged(); } } private string _fontName = "Arial"; public string FontName { get => _fontName; set { _fontName = value; NotifyPropertyChanged(); } } private bool _fontBold = false; public bool FontBold { get => _fontBold; set { _fontBold = value; NotifyPropertyChanged(); } } // Text settings private int _textTransparency = 0; public int TextTransparency { get => _textTransparency; set { _textTransparency = value; NotifyPropertyChanged(); } } private int _textMargin = 8; public int TextMargin { get => _textMargin; set { _textMargin = value; NotifyPropertyChanged(); } } private string _textColorRGB = "Yellow"; public string TextColorRGB { get => _textColorRGB; set { _textColorRGB = value; NotifyPropertyChanged(); } } // Logo/Watermark settings private string _logoFile = ""; public string LogoFile { get => _logoFile; set { _logoFile = value; NotifyPropertyChanged(); } } private int _logoHeight = 430; public int LogoHeight { get => _logoHeight; set { _logoHeight = value; NotifyPropertyChanged(); } } private int _logoWidth = 430; public int LogoWidth { get => _logoWidth; set { _logoWidth = value; NotifyPropertyChanged(); } } private int _logoMargin = 290; public int LogoMargin { get => _logoMargin; set { _logoMargin = value; NotifyPropertyChanged(); } } private int _logoTransparency = 100; public int LogoTransparency { get => _logoTransparency; set { _logoTransparency = value; NotifyPropertyChanged(); } } // Folder division settings private int _filesPerFolder = 99; public int FilesPerFolder { get => _filesPerFolder; set { _filesPerFolder = value; NotifyPropertyChanged(); } } private string _folderSuffix = ""; public string FolderSuffix { get => _folderSuffix; set { _folderSuffix = value; NotifyPropertyChanged(); } } private int _counterDigits = 2; public int CounterDigits { get => _counterDigits; set { _counterDigits = value; NotifyPropertyChanged(); } } // Vertical text settings private int _verticalTextSize = 20; public int VerticalTextSize { get => _verticalTextSize; set { _verticalTextSize = value; NotifyPropertyChanged(); } } private int _verticalTextMargin = 6; public int VerticalTextMargin { get => _verticalTextMargin; set { _verticalTextMargin = value; NotifyPropertyChanged(); } } // JPEG compression settings private int _jpegQuality = 85; public int JpegQuality { get => _jpegQuality; set { _jpegQuality = value; NotifyPropertyChanged(); } } private int _jpegQualityThumbnail = 30; public int JpegQualityThumbnail { get => _jpegQualityThumbnail; set { _jpegQualityThumbnail = value; NotifyPropertyChanged(); } } // CheckBox settings private bool _createThumbnails = true; public bool CreateThumbnails { get => _createThumbnails; set { _createThumbnails = value; NotifyPropertyChanged(); } } private bool _automaticRotation; public bool AutomaticRotation { get => _automaticRotation; set { _automaticRotation = value; NotifyPropertyChanged(); } } private bool _forceJpeg; public bool ForceJpeg { get => _forceJpeg; set { _forceJpeg = value; NotifyPropertyChanged(); } } private bool _updateSubdirectories; public bool UpdateSubdirectories { get => _updateSubdirectories; set { _updateSubdirectories = value; NotifyPropertyChanged(); } } private bool _createSubfolders; public bool CreateSubfolders { get => _createSubfolders; set { _createSubfolders = value; NotifyPropertyChanged(); } } private bool _addTime; public bool AddTime { get => _addTime; set { _addTime = value; NotifyPropertyChanged(); } } private bool _addRaceTime; public bool AddRaceTime { get => _addRaceTime; set { _addRaceTime = value; NotifyPropertyChanged(); } } private bool _addLogo; public bool AddLogo { get => _addLogo; set { _addLogo = value; NotifyPropertyChanged(); } } private bool _keepOriginalDimensions; public bool KeepOriginalDimensions { get => _keepOriginalDimensions; set { _keepOriginalDimensions = value; NotifyPropertyChanged(); } } private bool _showDate; public bool ShowDate { get => _showDate; set { _showDate = value; NotifyPropertyChanged(); } } private bool _showPhotoNumber; public bool ShowPhotoNumber { get => _showPhotoNumber; set { _showPhotoNumber = value; NotifyPropertyChanged(); } } private bool _shutdownSystem; public bool ShutdownSystem { get => _shutdownSystem; set { _shutdownSystem = value; NotifyPropertyChanged(); } } // ComboBox position/alignment settings private string _verticalPosition = "Basso"; public string VerticalPosition { get => _verticalPosition; set { _verticalPosition = value; NotifyPropertyChanged(); } } private string _horizontalAlignment = "Centro"; public string HorizontalAlignment { get => _horizontalAlignment; set { _horizontalAlignment = value; NotifyPropertyChanged(); } } private string _logoHorizontalPosition = "Destra"; public string LogoHorizontalPosition { get => _logoHorizontalPosition; set { _logoHorizontalPosition = value; NotifyPropertyChanged(); } } private string _logoVerticalPosition = "Basso"; public string LogoVerticalPosition { get => _logoVerticalPosition; set { _logoVerticalPosition = value; NotifyPropertyChanged(); } } // RadioButton settings private bool _useProgressiveNumbering = true; public bool UseProgressiveNumbering { get => _useProgressiveNumbering; set { _useProgressiveNumbering = value; NotifyPropertyChanged(); } } private bool _useFileNumbering; public bool UseFileNumbering { get => _useFileNumbering; set { _useFileNumbering = value; NotifyPropertyChanged(); } } private bool _useParallelProcessing = true; public bool UseParallelProcessing { get => _useParallelProcessing; set { _useParallelProcessing = value; NotifyPropertyChanged(); } } private bool _useSequentialProcessing; public bool UseSequentialProcessing { get => _useSequentialProcessing; set { _useSequentialProcessing = value; NotifyPropertyChanged(); } } // Additional settings that were missing private bool _addTimeToThumbnails; public bool AddTimeToThumbnails { get => _addTimeToThumbnails; set { _addTimeToThumbnails = value; NotifyPropertyChanged(); } } private bool _showFileNameOnThumbnails; public bool ShowFileNameOnThumbnails { get => _showFileNameOnThumbnails; set { _showFileNameOnThumbnails = value; NotifyPropertyChanged(); } } private DateTime _raceStartDate = DateTime.Now; public DateTime RaceStartDate { get => _raceStartDate; set { _raceStartDate = value; NotifyPropertyChanged(); } } private string _timeLabel = ""; public string TimeLabel { get => _timeLabel; set { _timeLabel = value; NotifyPropertyChanged(); } } private string _bigPhotoSuffix = ""; public string BigPhotoSuffix { get => _bigPhotoSuffix; set { _bigPhotoSuffix = value; NotifyPropertyChanged(); } } private bool _addTextToThumbnails; public bool AddTextToThumbnails { get => _addTextToThumbnails; set { _addTextToThumbnails = value; NotifyPropertyChanged(); } } private bool _addRaceTimeToThumbnails; public bool AddRaceTimeToThumbnails { get => _addRaceTimeToThumbnails; set { _addRaceTimeToThumbnails = value; NotifyPropertyChanged(); } } private bool _addNumberAndTimeToThumbnails; public bool AddNumberAndTimeToThumbnails { get => _addNumberAndTimeToThumbnails; set { _addNumberAndTimeToThumbnails = value; NotifyPropertyChanged(); } } // Image processing progress and status private string _processingStatus = ""; public string ProcessingStatus { get => _processingStatus; set { _processingStatus = value; NotifyPropertyChanged(); } } private int _processedImagesCount = 0; public int ProcessedImagesCount { get => _processedImagesCount; set { _processedImagesCount = value; NotifyPropertyChanged(); } } private int _totalImagesCount = 0; public int TotalImagesCount { get => _totalImagesCount; set { _totalImagesCount = value; NotifyPropertyChanged(); } } private int _progressBarValue = 0; public int ProgressBarValue { get => _progressBarValue; set { _progressBarValue = value; NotifyPropertyChanged(); } } private int _progressBarMaximum = 100; public int ProgressBarMaximum { get => _progressBarMaximum; set { _progressBarMaximum = value; NotifyPropertyChanged(); } } private ConcurrentBag _results = new(); private int _currentAmount = 0; private int _previousAmount = 0; // Atomic counter for processed images — avoids expensive ConcurrentBag.Count enumerations private int _processedAtomic = 0; private System.Threading.Timer? _speedTimer; private void Test(object parameter) { Debug.WriteLine("Yep"); this.UiEnabled = !this.UiEnabled; } private async Task TestAsync() { Debug.WriteLine("Yep c"); } private async Task ProcessImages() { _logger.LogInformation("Avvio elaborazione..."); UiEnabled = false; MainToken?.Dispose(); MainToken = new CancellationTokenSource(); var token = MainToken.Token; // Fix paths FixPaths(); // Reset counters ProcessingStatus = "Elaborazione in corso..."; TotalImagesCount = 0; ProcessedImagesCount = 0; SpeedCounter = "-f/s"; ProgressBarValue = 0; ProgressBarMaximum = 100; // Update PicSettings from DataModel using AutoMapper _mapper.Map(this, _picSettings); var imageCreationOptions = new ImageCreationStuff.Options { AggiornaSottodirectory = UpdateSubdirectories, CreaSottocartelle = CreateSubfolders, FilePerCartella = FilesPerFolder, SuffissoCartelle = FolderSuffix, CifreContatore = CounterDigits, NumerazioneType = UseProgressiveNumbering ? NumerazioneType.Progressiva : NumerazioneType.Files, SourcePath = SourcePath, DestinationPath = DestinationPath, MaxThreads = ThreadsCount, ChunksSize = ChunkSize, LinearExecution = UseSequentialProcessing }; try { _results = new ConcurrentBag(); _currentAmount = 0; _previousAmount = 0; _processedAtomic = 0; // Start speed timer (sample every second using lightweight atomic reads) _speedTimer = new System.Threading.Timer(UpdateSpeedCounter, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); var time = await _imageCreationService.CreaCatalogoParallel( imageCreationOptions, _results, OnImageProcessed, token); SpeedCounter = time; _speedTimer?.Dispose(); _speedTimer = null; } catch (OperationCanceledException) { _logger.LogInformation("Operazione Cancellata"); } catch (Exception ex) { _logger.LogError(ex, "Errore durante l'elaborazione delle immagini"); ProcessingStatus = $"Errore: {ex.Message}"; } finally { MainToken?.Dispose(); MainToken = null; _speedTimer?.Dispose(); _speedTimer = null; } ProcessingStatus = "Finito"; UiEnabled = true; } private void UpdateSpeedCounter(object? state) { _previousAmount = _currentAmount; // Read the atomic counter without enumerating the ConcurrentBag _currentAmount = System.Threading.Volatile.Read(ref _processedAtomic); int diff = _currentAmount - _previousAmount; // Report files per second (timer runs every 1s) SpeedCounter = $"{diff} f/s"; } private void OnImageProcessed(object? sender, Tuple args) { // Increment atomic processed counter once and use its value for all UI updates var processed = System.Threading.Interlocked.Increment(ref _processedAtomic); ProcessedImagesCount = processed; TotalImagesCount = args.Item2; ProgressBarMaximum = args.Item2; ProgressBarValue = processed; ProcessingStatus = args.Item1; } private void FixPaths() { SourcePath = FixPath(SourcePath); DestinationPath = FixPath(DestinationPath); } private string FixPath(string path) { if (string.IsNullOrWhiteSpace(path)) { return string.Empty; } // Trim leading/trailing quotes path = path.Trim().Trim('"'); // Normalize directory separators path = path.Replace('/', System.IO.Path.DirectorySeparatorChar) .Replace('\\', System.IO.Path.DirectorySeparatorChar); // Remove trailing separators then add one back path = path.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar; return path; } private async Task CancelOperation() { try { await MainToken?.CancelAsync(); UiEnabled = true; } catch (Exception e) { _logger.LogError(e.Message, "Error canceling the token"); _logger.LogInformation("Ignora questo errore"); } } // Note: These commands will trigger events that the View will handle to show dialogs // since dialogs require UI context public event EventHandler SelectSourceFolderRequested; public event EventHandler SelectDestinationFolderRequested; public event EventHandler SelectLogoFileRequested; public event EventHandler SaveSettingsRequested; public event EventHandler LoadSettingsRequested; public event EventHandler SelectColorRequested; // Request that the View shows a message to the user (message, caption, icon) public event EventHandler> ShowMessageRequested; private void SelectSourceFolder(object parameter) { SelectSourceFolderRequested?.Invoke(this, EventArgs.Empty); } private void SelectDestinationFolder(object parameter) { SelectDestinationFolderRequested?.Invoke(this, EventArgs.Empty); } private void SelectLogoFile(object parameter) { SelectLogoFileRequested?.Invoke(this, EventArgs.Empty); } private void SaveSettings(object parameter) { SaveSettingsRequested?.Invoke(this, null); } private void LoadSettings(object parameter) { LoadSettingsRequested?.Invoke(this, null); } private void SelectColor(object parameter) { SelectColorRequested?.Invoke(this, EventArgs.Empty); } public async Task SaveSettingsToFileAsync(string filePath) { await _settingsService.SaveSettingsAsync(filePath, this); } public async Task LoadSettingsFromFileAsync(string filePath) { await _settingsService.LoadSettingsAsync(filePath, this); } } }