Catalog/imagecatalog/DataModel.cs

1361 lines
42 KiB
C#
Raw Normal View History

2024-10-14 23:25:35 +02:00
using ImageCatalog_2.Commands;
using ImageCatalog_2.Models;
2024-10-14 23:25:35 +02:00
using ImageCatalog_2.Services;
using ImageCatalog_2.ViewModels;
2024-10-14 23:05:18 +02:00
using System;
2024-10-14 22:55:52 +02:00
using System.Collections.Generic;
using System.ComponentModel;
2024-10-14 23:25:35 +02:00
using System.Diagnostics;
#if WINDOWS
2026-02-04 19:48:03 +01:00
using System.Drawing.Text;
#endif
2024-10-14 22:55:52 +02:00
using System.Linq;
using System.Runtime.InteropServices;
2024-10-14 22:55:52 +02:00
using System.Text;
2026-02-04 23:16:06 +01:00
using System.Threading;
2024-10-14 22:55:52 +02:00
using System.Threading.Tasks;
#if WINDOWS
2026-02-04 23:16:06 +01:00
using System.Windows.Forms;
#endif
2024-10-14 23:25:35 +02:00
using System.Windows.Input;
2026-02-04 23:16:06 +01:00
using AutoMapper;
using MaddoShared;
2025-07-29 11:07:49 +02:00
using Microsoft.Extensions.Logging;
2024-10-14 22:55:52 +02:00
namespace ImageCatalog_2
{
public class DataModel : ViewModelBase
{
2024-10-14 23:25:35 +02:00
public ICommand TestCommand { get; }
public ICommand AsyncTestCommand { get; }
2025-07-29 11:07:49 +02:00
public ICommand AsyncCancelOperationCommand { get; }
2025-07-23 17:16:06 +02:00
public ICommand ProcessImagesCommand { get; }
2026-02-04 19:48:03 +01:00
public ICommand SelectSourceFolderCommand { get; }
public ICommand SelectDestinationFolderCommand { get; }
public ICommand SelectLogoFileCommand { get; }
public ICommand SaveSettingsCommand { get; }
public ICommand LoadSettingsCommand { get; }
public ICommand SelectColorCommand { get; }
public ICommand SelectTransparentColorCommand { get; }
public ICommand SelectModelsFolderCommand { get; }
public ICommand SelectCsvOutputCommand { get; }
public ICommand StartAiCommand { get; }
2024-10-14 23:25:35 +02:00
2024-10-14 23:05:18 +02:00
private readonly ITestService _service;
2025-07-29 11:10:54 +02:00
private readonly ILogger<DataModel> _logger;
2026-02-04 19:48:03 +01:00
private readonly ISettingsService _settingsService;
2026-02-21 15:53:52 +01:00
private readonly ImageCreationService _imageCreationService;
private readonly IAiExtractionService _aiExtractionService;
private readonly IImageProcessingCoordinator _imageProcessingCoordinator;
private readonly ProcessingStateViewModel _processing;
private readonly PathSettingsViewModel _paths;
private readonly AiSettingsViewModel _ai;
private readonly RaceUploadSettingsViewModel _raceUpload;
private readonly VisualSettingsViewModel _visual;
2026-02-04 23:16:06 +01:00
private readonly PicSettings _picSettings;
private readonly IMapper _mapper;
2026-02-04 19:48:03 +01:00
// ComboBox collections
public List<string> AvailableFonts { get; }
public List<string> VerticalPositions { get; } = new() { "Alto", "Centro", "Basso" };
public List<string> HorizontalAlignments { get; } = new() { "Sinistra", "Centro", "Destra" };
2025-07-29 11:10:54 +02:00
[CLSCompliant(false)]
public DataModel(ITestService testService, ISettingsService settingsService,
ImageCreationService imageCreationService, IAiExtractionService aiExtractionService, IImageProcessingCoordinator imageProcessingCoordinator, PicSettings picSettings,
IMapper mapper, ILogger<DataModel> logger, MaddoShared.IVersionProvider? versionProvider = null)
2024-10-14 23:05:18 +02:00
{
_service = testService;
2025-07-29 11:07:49 +02:00
_logger = logger;
2026-02-04 19:48:03 +01:00
_settingsService = settingsService;
2026-02-04 23:16:06 +01:00
_imageCreationService = imageCreationService;
_aiExtractionService = aiExtractionService;
_imageProcessingCoordinator = imageProcessingCoordinator;
_processing = new ProcessingStateViewModel();
_processing.PropertyChanged += OnProcessingPropertyChanged;
_paths = new PathSettingsViewModel();
_paths.PropertyChanged += OnPathsPropertyChanged;
_ai = new AiSettingsViewModel();
_ai.PropertyChanged += OnAiPropertyChanged;
_raceUpload = new RaceUploadSettingsViewModel();
_raceUpload.PropertyChanged += OnRaceUploadPropertyChanged;
_visual = new VisualSettingsViewModel();
_visual.PropertyChanged += OnVisualPropertyChanged;
2026-02-04 23:16:06 +01:00
_picSettings = picSettings;
_mapper = mapper;
// Populate AppVersion from version provider when available
AppVersion = versionProvider?.GetVersionString() ?? string.Empty;
2024-10-14 23:25:35 +02:00
TestCommand = new RelayCommand(Test);
AsyncTestCommand = new AsyncCommand(TestAsync);
2025-07-29 11:07:49 +02:00
AsyncCancelOperationCommand = new AsyncCommand(CancelOperation);
2025-07-23 17:16:06 +02:00
ProcessImagesCommand = new AsyncCommand(ProcessImages);
SelectModelsFolderCommand = new RelayCommand(SelectModelsFolder);
SelectCsvOutputCommand = new RelayCommand(SelectCsvOutput);
StartAiCommand = new AsyncCommand(StartAiAsync);
2026-02-04 19:48:03 +01:00
SelectSourceFolderCommand = new RelayCommand(SelectSourceFolder);
SelectDestinationFolderCommand = new RelayCommand(SelectDestinationFolder);
SelectLogoFileCommand = new RelayCommand(SelectLogoFile);
SaveSettingsCommand = new RelayCommand(SaveSettings);
LoadSettingsCommand = new RelayCommand(LoadSettings);
SelectColorCommand = new RelayCommand(SelectColor);
SelectTransparentColorCommand = new RelayCommand(SelectTransparentColor);
2026-02-04 19:48:03 +01:00
// Load available fonts
AvailableFonts = LoadAvailableFonts();
}
private async Task StartAiAsync()
{
MainToken = new CancellationTokenSource();
try
{
await RunAiExtractionCoreAsync(MainToken.Token, useDestination: true, recursive: true).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// user cancelled
}
finally
{
MainToken = null;
}
}
private async Task RunAiExtractionCoreAsync(CancellationToken token, bool useDestination = false, bool recursive = false)
{
var searchRoot = useDestination ? DestinationPath : SourcePath;
if (string.IsNullOrWhiteSpace(searchRoot) || !System.IO.Directory.Exists(searchRoot))
{
_logger.LogWarning("AI extraction path invalid: {Path}", searchRoot);
return;
}
await InvokeOnUiThreadAsync(() =>
{
PreviewResults.Clear();
AiProgress = 0;
}).ConfigureAwait(false);
await _aiExtractionService.RunAsync(
new AiExtractionRequest
{
SearchRoot = searchRoot,
Recursive = recursive,
CsvOutputPath = CsvOutputPath
},
token,
result => InvokeOnUiThreadAsync(() => PreviewResults.Add(result)),
progress => InvokeOnUiThreadAsync(() => AiProgress = progress)).ConfigureAwait(false);
}
/// <summary>
/// Optional UI-thread invoker set by the active UI layer (WPF, Avalonia, etc.).
/// When set, <see cref="InvokeOnUiThreadAsync"/> uses this delegate instead of the WPF dispatcher.
/// </summary>
public Action<Action>? UiInvoker { get; set; }
private Task InvokeOnUiThreadAsync(Action action)
{
return Task.Run(() =>
{
if (UiInvoker != null)
UiInvoker(action);
else
System.Windows.Application.Current?.Dispatcher.Invoke(action);
});
}
public AiSettingsViewModel Ai => _ai;
public RaceUploadSettingsViewModel RaceUpload => _raceUpload;
// AI properties
public bool ExtractNumbers
{
get => _ai.ExtractNumbers;
set => _ai.ExtractNumbers = value;
}
public string ModelsFolderPath
{
get => _ai.ModelsFolderPath;
set => _ai.ModelsFolderPath = value;
}
public string CsvOutputPath
{
get => _ai.CsvOutputPath;
set => _ai.CsvOutputPath = value;
}
public string FaceExecutablePath
{
get => _ai.FaceExecutablePath;
set => _ai.FaceExecutablePath = value;
}
public string FaceOutputFolderPath
{
get => _ai.FaceOutputFolderPath;
set => _ai.FaceOutputFolderPath = value;
}
// Race upload settings
public string ApiLogin
{
get => _raceUpload.ApiLogin;
set => _raceUpload.ApiLogin = value;
}
public string ApiPassword
{
get => _raceUpload.ApiPassword;
set => _raceUpload.ApiPassword = value;
}
public string ApiRaceDescription
{
get => _raceUpload.ApiRaceDescription;
set => _raceUpload.ApiRaceDescription = value;
}
public string ApiRaceTypeId
{
get => _raceUpload.ApiRaceTypeId;
set => _raceUpload.ApiRaceTypeId = value;
}
public DateTime ApiRaceStartDate
{
get => _raceUpload.ApiRaceStartDate;
set => _raceUpload.ApiRaceStartDate = value;
}
public DateTime ApiRaceEndDate
{
get => _raceUpload.ApiRaceEndDate;
set => _raceUpload.ApiRaceEndDate = value;
}
public string ApiPathBase
{
get => _raceUpload.ApiPathBase;
set => _raceUpload.ApiPathBase = value;
}
public string ApiLocalita
{
get => _raceUpload.ApiLocalita;
set => _raceUpload.ApiLocalita = value;
}
public int ApiEventoInLineaIndex
{
get => _raceUpload.ApiEventoInLineaIndex;
set => _raceUpload.ApiEventoInLineaIndex = value;
}
public int ApiTipoIndexValue
{
get => _raceUpload.ApiTipoIndexValue;
set => _raceUpload.ApiTipoIndexValue = value;
}
public int ApiFreeEventIndex
{
get => _raceUpload.ApiFreeEventIndex;
set => _raceUpload.ApiFreeEventIndex = value;
}
public string ApiRaceId
{
get => _raceUpload.ApiRaceId;
set => _raceUpload.ApiRaceId = value;
}
public string ApiRemoteProcessedBasePath
{
get => _raceUpload.ApiRemoteProcessedBasePath;
set => _raceUpload.ApiRemoteProcessedBasePath = value;
}
// Preview results for DataGrid
public System.Collections.ObjectModel.ObservableCollection<AiResultItem> PreviewResults => _ai.PreviewResults;
public double AiProgress
{
get => _ai.AiProgress;
set => _ai.AiProgress = value;
}
2026-02-04 19:48:03 +01:00
private List<string> LoadAvailableFonts()
{
#if WINDOWS
2026-02-04 19:48:03 +01:00
var fonts = new List<string>();
using (var installedFonts = new InstalledFontCollection())
{
fonts.AddRange(installedFonts.Families.Select(f => f.Name));
}
return fonts;
#else
return new List<string>();
#endif
2024-10-14 23:05:18 +02:00
}
2024-10-14 22:55:52 +02:00
2025-07-29 11:07:49 +02:00
private CancellationTokenSource? _mainToken;
public CancellationTokenSource? MainToken
{
get => _mainToken;
set
{
_mainToken = value;
NotifyPropertyChanged();
}
}
2024-10-14 22:55:52 +02:00
public string SourcePath
{
get => _paths.SourcePath;
set => _paths.SourcePath = value;
2024-10-14 22:55:52 +02:00
}
public string DestinationPath
{
get => _paths.DestinationPath;
set => _paths.DestinationPath = value;
2024-10-14 22:55:52 +02:00
}
public string HorizontalText
{
get => _visual.HorizontalText;
set => _visual.HorizontalText = value;
}
2025-07-29 11:10:54 +02:00
2025-07-28 10:34:03 +02:00
public string VerticalText
{
get => _visual.VerticalText;
set => _visual.VerticalText = value;
2025-07-28 10:34:03 +02:00
}
2025-07-28 14:45:03 +02:00
public bool OverwriteImages
{
get => _visual.OverwriteImages;
set => _visual.OverwriteImages = value;
2025-07-28 14:45:03 +02:00
}
2024-10-14 23:48:21 +02:00
private bool _uiEnabled = true;
public bool UiEnabled
{
2025-07-28 10:34:03 +02:00
get => _uiEnabled;
2024-10-14 23:48:21 +02:00
set
{
_uiEnabled = value;
NotifyPropertyChanged();
2026-02-04 21:12:27 +01:00
NotifyPropertyChanged(nameof(UiDisabled));
2024-10-14 23:48:21 +02:00
}
}
2025-07-29 11:10:54 +02:00
2025-07-29 10:34:23 +02:00
public bool UiDisabled => !_uiEnabled;
public ProcessingStateViewModel Processing => _processing;
public PathSettingsViewModel Paths => _paths;
public VisualSettingsViewModel Visual => _visual;
2025-07-29 10:34:23 +02:00
private void OnProcessingPropertyChanged(object? sender, PropertyChangedEventArgs e)
2025-07-29 10:34:23 +02:00
{
if (string.IsNullOrWhiteSpace(e.PropertyName))
2025-07-29 10:34:23 +02:00
{
return;
2025-07-29 10:34:23 +02:00
}
// Keep existing DataModel bindings working while state lives in child viewmodel.
NotifyPropertyChanged(e.PropertyName);
}
private void OnPathsPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (string.IsNullOrWhiteSpace(e.PropertyName))
{
return;
}
NotifyPropertyChanged(e.PropertyName);
}
private void OnAiPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (string.IsNullOrWhiteSpace(e.PropertyName))
{
return;
}
NotifyPropertyChanged(e.PropertyName);
}
private void OnRaceUploadPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (string.IsNullOrWhiteSpace(e.PropertyName))
{
return;
}
NotifyPropertyChanged(e.PropertyName);
}
private void OnVisualPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (string.IsNullOrWhiteSpace(e.PropertyName))
{
return;
}
NotifyPropertyChanged(e.PropertyName);
}
public string SpeedCounter
{
get => _processing.SpeedCounter;
set => _processing.SpeedCounter = value;
2025-07-29 10:34:23 +02:00
}
2024-10-14 23:48:21 +02:00
2025-09-19 09:53:31 +02:00
private int _chunkSize;
public int ChunkSize
{
get => _chunkSize;
set
{
_chunkSize = value;
NotifyPropertyChanged();
}
}
private int _threadsCount;
public int ThreadsCount
{
get => _threadsCount;
set
{
_threadsCount = value;
NotifyPropertyChanged();
}
}
2026-02-04 19:48:03 +01:00
// Thumbnail settings
public string ThumbnailPrefix
{
get => _visual.ThumbnailPrefix;
set => _visual.ThumbnailPrefix = value;
2026-02-04 19:48:03 +01:00
}
public int ThumbnailHeight
{
get => _visual.ThumbnailHeight;
set => _visual.ThumbnailHeight = value;
2026-02-04 19:48:03 +01:00
}
public int ThumbnailWidth
{
get => _visual.ThumbnailWidth;
set => _visual.ThumbnailWidth = value;
2026-02-04 19:48:03 +01:00
}
// Big photo settings
public int PhotoBigHeight
{
get => _visual.PhotoBigHeight;
set => _visual.PhotoBigHeight = value;
2026-02-04 19:48:03 +01:00
}
public int PhotoBigWidth
{
get => _visual.PhotoBigWidth;
set => _visual.PhotoBigWidth = value;
2026-02-04 19:48:03 +01:00
}
// Font settings
public int FontSize
{
get => _visual.FontSize;
set => _visual.FontSize = value;
2026-02-04 19:48:03 +01:00
}
public int FontSizeThumbnail
{
get => _visual.FontSizeThumbnail;
set => _visual.FontSizeThumbnail = value;
2026-02-04 19:48:03 +01:00
}
public string FontName
{
get => _visual.FontName;
set => _visual.FontName = value;
2026-02-04 19:48:03 +01:00
}
public bool FontBold
{
get => _visual.FontBold;
set => _visual.FontBold = value;
2026-02-04 19:48:03 +01:00
}
// Text settings
public int TextTransparency
{
get => _visual.TextTransparency;
set => _visual.TextTransparency = value;
2026-02-04 19:48:03 +01:00
}
public int TextMargin
{
get => _visual.TextMargin;
set => _visual.TextMargin = value;
2026-02-04 19:48:03 +01:00
}
public string TextColorRGB
{
get => _visual.TextColorRGB;
set => _visual.TextColorRGB = value;
2026-02-04 19:48:03 +01:00
}
public string TransparentColor
{
get => _visual.TransparentColor;
set => _visual.TransparentColor = value;
}
public bool UseTransparentColor
{
get => _visual.UseTransparentColor;
set => _visual.UseTransparentColor = value;
}
2026-02-04 19:48:03 +01:00
// Logo/Watermark settings
public string LogoFile
{
get => _visual.LogoFile;
set => _visual.LogoFile = value;
2026-02-04 19:48:03 +01:00
}
public int LogoHeight
{
get => _visual.LogoHeight;
set => _visual.LogoHeight = value;
2026-02-04 19:48:03 +01:00
}
public int LogoWidth
{
get => _visual.LogoWidth;
set => _visual.LogoWidth = value;
2026-02-04 19:48:03 +01:00
}
public int LogoMargin
{
get => _visual.LogoMargin;
set => _visual.LogoMargin = value;
2026-02-04 19:48:03 +01:00
}
public int LogoTransparency
{
get => _visual.LogoTransparency;
set => _visual.LogoTransparency = value;
2026-02-04 19:48:03 +01:00
}
// Image library selection (UI radio buttons bind to the boolean helpers)
private string _imageLibrary = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "System.Graphics" : "ImageSharp";
/// <summary>
/// Whether the application is running on Windows. Used by cross-platform UIs to show/hide Windows-only options.
/// </summary>
public bool IsRunningOnWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
/// <summary>
/// The selected image processing library. Possible values: "System.Graphics" or "ImageSharp".
/// This value is mirrored into PicSettings.ImageCreatorProvider so the runtime mapper picks the implementation.
/// </summary>
public string ImageLibrary
{
get => _imageLibrary;
set
{
if (_imageLibrary == value) return;
_imageLibrary = value;
// Reflect selection into PicSettings so mapper can resolve at runtime
_picSettings.ImageCreatorProvider = string.Equals(value, "ImageSharp", StringComparison.OrdinalIgnoreCase)
? "ALTERNATE"
: "Sharp";
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(UseSystemGraphics));
NotifyPropertyChanged(nameof(UseImageSharp));
NotifyPropertyChanged(nameof(IsRunningOnWindows));
}
}
public bool UseSystemGraphics
{
get => string.Equals(ImageLibrary, "System.Graphics", StringComparison.OrdinalIgnoreCase);
set
{
if (value) ImageLibrary = "System.Graphics";
NotifyPropertyChanged();
}
}
public bool UseImageSharp
{
get => string.Equals(ImageLibrary, "ImageSharp", StringComparison.OrdinalIgnoreCase);
set
{
if (value) ImageLibrary = "ImageSharp";
NotifyPropertyChanged();
}
}
2026-02-04 19:48:03 +01:00
// 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
public int VerticalTextSize
{
get => _visual.VerticalTextSize;
set => _visual.VerticalTextSize = value;
2026-02-04 19:48:03 +01:00
}
public int VerticalTextMargin
{
get => _visual.VerticalTextMargin;
set => _visual.VerticalTextMargin = value;
2026-02-04 19:48:03 +01:00
}
// JPEG compression settings
public int JpegQuality
{
get => _visual.JpegQuality;
set => _visual.JpegQuality = value;
2026-02-04 19:48:03 +01:00
}
public int JpegQualityThumbnail
{
get => _visual.JpegQualityThumbnail;
set => _visual.JpegQualityThumbnail = value;
2026-02-04 19:48:03 +01:00
}
// 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();
}
}
public bool AddLogo
{
get => _visual.AddLogo;
set => _visual.AddLogo = value;
2026-02-04 19:48:03 +01:00
}
public bool KeepOriginalDimensions
{
get => _visual.KeepOriginalDimensions;
set => _visual.KeepOriginalDimensions = value;
2026-02-04 19:48:03 +01:00
}
private bool _showDate;
public bool ShowDate
{
get => _showDate;
set
{
_showDate = value;
NotifyPropertyChanged();
}
}
private bool _showPhotoNumber;
public bool ShowPhotoNumber
{
get => _showPhotoNumber;
set
{
if (_showPhotoNumber == value) return;
2026-02-04 19:48:03 +01:00
_showPhotoNumber = value;
if (value)
{
// ensure mutually exclusive choices
_addTimeToThumbnails = false;
_addTextToThumbnails = false;
_addNumberAndTimeToThumbnails = false;
_addRaceTimeToThumbnails = false;
NotifyPropertyChanged(nameof(AddTimeToThumbnails));
NotifyPropertyChanged(nameof(AddTextToThumbnails));
NotifyPropertyChanged(nameof(AddNumberAndTimeToThumbnails));
NotifyPropertyChanged(nameof(AddRaceTimeToThumbnails));
}
2026-02-04 19:48:03 +01:00
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(ThumbnailMode));
2026-02-04 19:48:03 +01:00
}
}
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 => _thumbnailOption == ThumbnailOptionEnum.Time;
2026-02-04 19:48:03 +01:00
set
{
if (value)
{
ThumbnailOption = ThumbnailOptionEnum.Time;
}
else if (_thumbnailOption == ThumbnailOptionEnum.Time)
{
ThumbnailOption = ThumbnailOptionEnum.None;
}
2026-02-04 19:48:03 +01:00
NotifyPropertyChanged();
}
}
private bool _showFileNameOnThumbnails;
public bool ShowFileNameOnThumbnails
{
get => _thumbnailOption == ThumbnailOptionEnum.FileName;
2026-02-04 19:48:03 +01:00
set
{
if (value)
ThumbnailOption = ThumbnailOptionEnum.FileName;
else if (_thumbnailOption == ThumbnailOptionEnum.FileName)
ThumbnailOption = ThumbnailOptionEnum.None;
2026-02-04 19:48:03 +01:00
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();
}
}
2026-02-04 22:10:16 +01:00
private string _bigPhotoSuffix = "";
public string BigPhotoSuffix
{
get => _bigPhotoSuffix;
set
{
_bigPhotoSuffix = value;
NotifyPropertyChanged();
}
}
private bool _addTextToThumbnails;
public bool AddTextToThumbnails
{
get => _thumbnailOption == ThumbnailOptionEnum.Text;
2026-02-04 22:10:16 +01:00
set
{
if (value)
ThumbnailOption = ThumbnailOptionEnum.Text;
else if (_thumbnailOption == ThumbnailOptionEnum.Text)
ThumbnailOption = ThumbnailOptionEnum.None;
2026-02-04 22:10:16 +01:00
NotifyPropertyChanged();
}
}
private bool _addRaceTimeToThumbnails;
public bool AddRaceTimeToThumbnails
{
get => _thumbnailOption == ThumbnailOptionEnum.RaceTime;
2026-02-04 22:10:16 +01:00
set
{
if (value)
ThumbnailOption = ThumbnailOptionEnum.RaceTime;
else if (_thumbnailOption == ThumbnailOptionEnum.RaceTime)
ThumbnailOption = ThumbnailOptionEnum.None;
2026-02-04 22:10:16 +01:00
NotifyPropertyChanged();
}
}
private bool _addNumberAndTimeToThumbnails;
public bool AddNumberAndTimeToThumbnails
{
get => _thumbnailOption == ThumbnailOptionEnum.FileNameAndTime;
2026-02-04 22:10:16 +01:00
set
{
if (value)
ThumbnailOption = ThumbnailOptionEnum.FileNameAndTime;
else if (_thumbnailOption == ThumbnailOptionEnum.FileNameAndTime)
ThumbnailOption = ThumbnailOptionEnum.None;
NotifyPropertyChanged();
}
}
// New enum and authoritative property for thumbnail selection
public enum ThumbnailOptionEnum
{
None = 0,
Text = 1,
FileName = 2,
Time = 3,
FileNameAndTime = 4,
RaceTime = 5
}
private ThumbnailOptionEnum _thumbnailOption = ThumbnailOptionEnum.None;
// Name matches DTO property so SettingsService mapping works
public ThumbnailOptionEnum ThumbnailOption
{
get => _thumbnailOption;
set
{
if (_thumbnailOption == value) return;
_thumbnailOption = value;
// Notify all dependent properties so UI updates
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(AddTextToThumbnails));
NotifyPropertyChanged(nameof(AddTimeToThumbnails));
NotifyPropertyChanged(nameof(ShowPhotoNumber));
NotifyPropertyChanged(nameof(AddNumberAndTimeToThumbnails));
NotifyPropertyChanged(nameof(AddRaceTimeToThumbnails));
NotifyPropertyChanged(nameof(ShowFileNameOnThumbnails));
NotifyPropertyChanged(nameof(ThumbnailMode));
}
}
// Helper int property to bind ComboBox SelectedIndex in the WinForms designer
public int ThumbnailOptionIndex
{
get => (int)ThumbnailOption;
set
{
var opt = (ThumbnailOptionEnum)value;
if (opt == ThumbnailOption) return;
ThumbnailOption = opt;
2026-02-04 22:10:16 +01:00
NotifyPropertyChanged();
}
}
// Single authoritative thumbnail mode string to avoid conflicting bindings
// Possible values: "None", "Text", "Time", "Number", "NumberAndTime", "RaceTime"
public string ThumbnailMode
{
get
{
return _thumbnailOption switch
{
ThumbnailOptionEnum.Text => "Text",
ThumbnailOptionEnum.Time => "Time",
ThumbnailOptionEnum.FileName => "Number",
ThumbnailOptionEnum.FileNameAndTime => "NumberAndTime",
ThumbnailOptionEnum.RaceTime => "RaceTime",
_ => "None",
};
}
set
{
// Map incoming string to enum and set the authoritative property
switch ((value ?? string.Empty).ToLowerInvariant())
{
case "text":
ThumbnailOption = ThumbnailOptionEnum.Text;
break;
case "time":
ThumbnailOption = ThumbnailOptionEnum.Time;
break;
case "number":
ThumbnailOption = ThumbnailOptionEnum.FileName;
break;
case "numberandtime":
ThumbnailOption = ThumbnailOptionEnum.FileNameAndTime;
break;
case "racetime":
ThumbnailOption = ThumbnailOptionEnum.RaceTime;
break;
default:
ThumbnailOption = ThumbnailOptionEnum.None;
break;
}
}
}
2026-02-04 23:16:06 +01:00
// Image processing progress and status
public string ProcessingStatus
{
get => _processing.ProcessingStatus;
set => _processing.ProcessingStatus = value;
2026-02-04 23:16:06 +01:00
}
public int ProcessedImagesCount
{
get => _processing.ProcessedImagesCount;
set => _processing.ProcessedImagesCount = value;
2026-02-04 23:16:06 +01:00
}
public int TotalImagesCount
{
get => _processing.TotalImagesCount;
set => _processing.TotalImagesCount = value;
2026-02-04 23:16:06 +01:00
}
public int ProgressBarValue
{
get => _processing.ProgressBarValue;
set => _processing.ProgressBarValue = value;
2026-02-04 23:16:06 +01:00
}
public int ProgressBarMaximum
{
get => _processing.ProgressBarMaximum;
set => _processing.ProgressBarMaximum = value;
2026-02-04 23:16:06 +01:00
}
2024-10-14 23:25:35 +02:00
private void Test(object parameter)
{
Debug.WriteLine("Yep");
2024-10-14 23:48:21 +02:00
this.UiEnabled = !this.UiEnabled;
2024-10-14 23:25:35 +02:00
}
private async Task TestAsync()
{
Debug.WriteLine("Yep c");
}
2025-07-23 17:16:06 +02:00
private async Task ProcessImages()
{
2026-02-04 23:16:06 +01:00
_logger.LogInformation("Avvio elaborazione...");
UiEnabled = false;
MainToken?.Dispose();
MainToken = new CancellationTokenSource();
var token = MainToken.Token;
// Normalize paths
_paths.NormalizePaths();
2026-02-04 23:16:06 +01:00
// Reset counters
_processing.ResetForRun();
2026-02-04 23:16:06 +01:00
// Update PicSettings from DataModel using AutoMapper
_mapper.Map(this, _picSettings);
// Explicitly ensure thumbnail-related flags are applied to PicSettings
// because AutoMapper may not map differently-named properties.
try
{
_picSettings.AggiungiScritteMiniature = this.AddTextToThumbnails;
_picSettings.UsaOrarioMiniatura = this.AddTimeToThumbnails;
_picSettings.AggNumTempMin = this.AddNumberAndTimeToThumbnails;
_picSettings.AggTempoGaraMin = this.AddRaceTimeToThumbnails;
_picSettings.CreaMiniature = this.CreateThumbnails;
_picSettings.LarghezzaSmall = this.ThumbnailWidth;
_picSettings.AltezzaSmall = this.ThumbnailHeight;
_picSettings.DimMin = this.FontSizeThumbnail;
_picSettings.JpegQualityMin = this.JpegQualityThumbnail;
}
catch
{
// Best-effort; do not fail processing on mapping issues
}
2026-02-21 15:53:52 +01:00
var imageCreationOptions = new ImageCreationService.Options
2026-02-04 23:16:06 +01:00
{
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
{
var runResult = await _imageProcessingCoordinator.RunAsync(
new ImageProcessingRunRequest { Options = imageCreationOptions },
token,
update =>
{
ProcessedImagesCount = update.Processed;
TotalImagesCount = update.Total;
ProgressBarMaximum = update.Total;
ProgressBarValue = update.Processed;
ProcessingStatus = update.Status;
},
speed => { SpeedCounter = speed; }).ConfigureAwait(false);
// AI integration stub: if ExtractNumbers is enabled, simulate or invoke OCR processing
if (ExtractNumbers)
{
try
{
await RunAiExtractionCoreAsync(token);
}
catch (OperationCanceledException)
{
_logger.LogInformation("AI extraction canceled");
}
catch (Exception ex)
{
_logger.LogError(ex, "AI extraction failed");
}
}
SpeedCounter = runResult.FinalSpeedCounter;
2026-02-04 23:16:06 +01:00
}
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;
}
ProcessingStatus = "Finito";
UiEnabled = true;
}
2025-07-29 11:07:49 +02:00
private async Task CancelOperation()
{
try
{
var tokenSource = MainToken;
if (tokenSource is not null)
{
// Cancel synchronously and return to caller. Some CTSource implementations
// may provide async helpers but cancelling is immediate.
try
{
tokenSource.Cancel();
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Exception while cancelling token");
}
}
2025-07-29 11:07:49 +02:00
UiEnabled = true;
}
catch (Exception e)
{
_logger.LogError(e, "Error canceling the token");
2025-07-29 11:07:49 +02:00
_logger.LogInformation("Ignora questo errore");
}
}
2026-02-04 19:48:03 +01:00
// 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? SelectModelsFolderRequested;
public event EventHandler? SelectCsvOutputRequested;
public event EventHandler<string?>? SaveSettingsRequested;
public event EventHandler<string?>? LoadSettingsRequested;
public event EventHandler? SelectColorRequested;
// Request that the View shows a message to the user (message, caption, icon)
#if WINDOWS
public event EventHandler<Tuple<string, string, MessageBoxIcon>>? ShowMessageRequested;
#else
public event EventHandler<Tuple<string, string, int>>? ShowMessageRequested;
#endif
public event EventHandler? SelectTransparentColorRequested;
2026-02-04 19:48:03 +01:00
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 SelectModelsFolder(object parameter)
{
SelectModelsFolderRequested?.Invoke(this, EventArgs.Empty);
}
private void SelectCsvOutput(object parameter)
{
SelectCsvOutputRequested?.Invoke(this, EventArgs.Empty);
}
2026-02-04 19:48:03 +01:00
private void SaveSettings(object parameter)
{
SaveSettingsRequested?.Invoke(this, string.Empty);
2026-02-04 19:48:03 +01:00
}
private void LoadSettings(object parameter)
{
LoadSettingsRequested?.Invoke(this, string.Empty);
2026-02-04 19:48:03 +01:00
}
private void SelectColor(object parameter)
{
SelectColorRequested?.Invoke(this, EventArgs.Empty);
}
private void SelectTransparentColor(object parameter)
{
SelectTransparentColorRequested?.Invoke(this, EventArgs.Empty);
}
2026-02-04 19:48:03 +01:00
public async Task SaveSettingsToFileAsync(string filePath)
{
await _settingsService.SaveSettingsAsync(filePath, this);
}
public async Task LoadSettingsFromFileAsync(string filePath)
{
await _settingsService.LoadSettingsAsync(filePath, this);
}
private string _appVersion = string.Empty;
public string AppVersion
{
get => _appVersion;
set
{
_appVersion = value;
NotifyPropertyChanged();
}
}
2024-10-14 22:55:52 +02:00
}
}