using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using ImageCatalog_2;
using ImageCatalog_2.Commands;
using ImageCatalog_2.Services;
using MaddoShared;
using Microsoft.Extensions.Logging;

namespace ImageCatalog;

public partial class MainForm
{
    private readonly DataModel Model;

    private readonly ILogger<MainForm> _logger;

    private readonly ParametriSetup _parametriSetup;
    private readonly PicSettings _picSettings;
    // Prevent re-entrant updates between UI events and model PropertyChanged handling
    private bool _suppressRadioUpdates = false;
    private bool _transparentDialogOpen = false;

    public MainForm(DataModel model, ImageCreationStuff imageCreationStuff, PicSettings picSettings,
        ParametriSetup parametriSetup, ILogger<MainForm> logger)
    {
        Model = model;
        _parametriSetup = parametriSetup;
        _picSettings = picSettings;
        _logger = logger;

        _logger.LogDebug("Start");

        InitializeComponent();
            // Set this form as the control for thread marshalling in the DataModel
            Model.SetControl(this);

            // Ensure the designer data bindings have a concrete DataSource immediately so
            // that UI controls (radio buttons) reflect the ViewModel state and propagate
            // user changes back to the ViewModel.
            bindingSource1.DataSource = Model;

            BindControls();

            // The designer originally bound the radio buttons to boolean helpers on the ViewModel.
            // Those two separate bindings can fight with each other. Clear designer bindings and
            // wire explicit Click handlers that update the single authoritative property
            // `Model.ImageLibrary`. Also keep a PropertyChanged listener to reflect external
            // changes back into the radio buttons.
            rdbLibrary1.DataBindings.Clear();
            rdbLibrary2.DataBindings.Clear();

            // Initialize radio state from model
            rdbLibrary1.Checked = Model.UseSystemGraphics;
            rdbLibrary2.Checked = Model.UseImageSharp;

            // Use Click handlers (not CheckedChanged) to avoid competing binding updates
            rdbLibrary1.Click += (_, _) =>
            {
                if (_suppressRadioUpdates) return;
                if (Model.ImageLibrary != "System.Graphics")
                    Model.ImageLibrary = "System.Graphics";
            };
            rdbLibrary2.Click += (_, _) =>
            {
                if (_suppressRadioUpdates) return;
                if (Model.ImageLibrary != "ImageSharp")
                    Model.ImageLibrary = "ImageSharp";
            };

            // Watch for model changes so we can reflect external updates
            Model.PropertyChanged += Model_PropertyChanged;

            // Thumbnail options moved to ComboBox to avoid conflicting bindings with multiple radio buttons

            // Initialize ComboBox with Italian descriptions
            comboThumbnailOption.Items.Clear();
            comboThumbnailOption.Items.AddRange(new object[] {
                "Nessuna",
                "Aggiungi scritta",
                "Nome file",
                "Aggiungi orario",
                "Nome+Orario",
                "Tempo gara"
            });

            // Bind to model via helper index property ThumbnailOptionIndex
            comboThumbnailOption.DataBindings.Add(new Binding("SelectedIndex", bindingSource1, "ThumbnailOptionIndex", true, DataSourceUpdateMode.OnPropertyChanged));

        // Save user preferences on form close instead of immediately when dialogs are used
        this.FormClosing += MainForm_FormClosing;

        // Wire up 'Open folder in Explorer' buttons
        btnOpenSourceFolder.Click += BtnOpenSourceFolder_Click;
        btnOpenDestFolder.Click += BtnOpenDestFolder_Click;

        // Show currently selected color in small PictureBox3
        PictureBox3.BackColor = ColorTranslator.FromHtml(Model.TransparentColor);

        // Version label is data-bound to DataModel.AppVersion; DataModel is populated with the version via DI
    }

    

    private void RdbLibrary_CheckedChanged(object? sender, EventArgs e)
    {
        // Keep behavior simple: when a radio button becomes checked, update the ViewModel
        // so that the designer binding and PicSettings stay in sync.
        if (sender is RadioButton rb && rb.Checked)
        {
            _logger?.LogDebug("Radio library changed: {RadioName}", rb.Name);
            if (rb == rdbLibrary2)
            {
                Model.ImageLibrary = "ImageSharp";
            }
            else if (rb == rdbLibrary1)
            {
                Model.ImageLibrary = "System.Graphics";
            }
        }
    }

    private void Model_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName is null) return;
        if (e.PropertyName == nameof(Model.ImageLibrary) || e.PropertyName == nameof(Model.UseImageSharp) || e.PropertyName == nameof(Model.UseSystemGraphics))
        {
            _logger?.LogDebug("Model property changed: {Property} => ImageLibrary={ImageLibrary}, PicSettings.Provider={Provider}", e.PropertyName, Model.ImageLibrary, _picSettings.ImageCreatorProvider);

            // Reflect authoritative model value into the radio buttons in a thread-safe, re-entrancy-safe way
            try
            {
                _suppressRadioUpdates = true;
                rdbLibrary1.Checked = Model.UseSystemGraphics;
                rdbLibrary2.Checked = Model.UseImageSharp;
            }
            finally
            {
                _suppressRadioUpdates = false;
            }
        }

        // Thumbnail mode changes - reflect back to combo box index
        if (e.PropertyName == nameof(Model.ThumbnailMode) ||
            e.PropertyName == nameof(Model.ThumbnailOption) ||
            e.PropertyName == nameof(Model.ThumbnailOptionIndex))
        {
            try
            {
                _suppressRadioUpdates = true;
                comboThumbnailOption.SelectedIndex = Model.ThumbnailOptionIndex;
            }
            finally
            {
                _suppressRadioUpdates = false;
            }
        }
    }

        protected void BindControls()
        {
            // Bind buttons to ViewModel commands using command binding
            _btnCreaCatalogoAsync.BindCommand(Model.ProcessImagesCommand);
            // Note: `button1` control does not exist in the designer. Use the primary create button only.
            _Button2.BindCommand(Model.SelectSourceFolderCommand);
            _Button3.BindCommand(Model.SelectDestinationFolderCommand);
            _Button4.BindCommand(Model.SelectLogoFileCommand);
            _Button5.BindCommand(Model.SaveSettingsCommand);
            _Button6.BindCommand(Model.LoadSettingsCommand);
            _Button8.BindCommand(Model.SelectColorCommand);
            // Bind the transparency chooser button/command
            btnSetTransparency.BindCommand(Model.SelectTransparentColorCommand);

        // Subscribe to ViewModel events for UI dialogs (these need UI context)
        Model.SelectSourceFolderRequested += OnSelectSourceFolderRequested;
        Model.SelectDestinationFolderRequested += OnSelectDestinationFolderRequested;
        Model.SelectLogoFileRequested += OnSelectLogoFileRequested;
        Model.SaveSettingsRequested += OnSaveSettingsRequested;
        Model.LoadSettingsRequested += OnLoadSettingsRequested;
        Model.SelectColorRequested += OnSelectColorRequested;
        Model.SelectTransparentColorRequested += OnSelectTransparentColorRequested;
        // Show message requests (from ViewModel validation)
        Model.ShowMessageRequested += OnShowMessageRequested;
    }

    private void OnSelectTransparentColorRequested(object? sender, EventArgs e)
    {
        // Ensure UI thread
        if (InvokeRequired)
        {
            Invoke(new Action<object, EventArgs>(OnSelectTransparentColorRequested), sender, e as EventArgs ?? EventArgs.Empty);
            return;
        }
        // Prevent re-entrancy: if the dialog is already open, ignore subsequent requests
        if (_transparentDialogOpen) return;

        _transparentDialogOpen = true;
        var dlg = new ColorDialog { AllowFullOpen = true };
        try
        {
            dlg.Color = ColorTranslator.FromHtml(Model.TransparentColor);
        }
        catch { }
        try
        {
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                Model.TransparentColor = ColorTranslator.ToHtml(dlg.Color);
                PictureBox3.BackColor = dlg.Color;

                // Update preview if logo exists
                if (!string.IsNullOrWhiteSpace(Model.LogoFile) && File.Exists(Model.LogoFile))
                {
                    UpdateLogoPictureBox(Model.LogoFile);
                }
            }
        }
        finally
        {
            _transparentDialogOpen = false;
        }
    }

    private void BtnSetTransparency_Click(object? sender, EventArgs e)
    {
        Model.SelectTransparentColorCommand.Execute(null);
    }

    private void OnShowMessageRequested(object? sender, Tuple<string, string, MessageBoxIcon> args)
    {
        if (args is null) return;
        // Ensure call on UI thread
        if (InvokeRequired)
        {
            Invoke(new Action(() => OnShowMessageRequested(sender, args)));
            return;
        }

        MessageBox.Show(this, args.Item1, args.Item2, MessageBoxButtons.OK, args.Item3);
    }

    private void SetDefaults()
    {
        // Bind ComboBoxes to Model using proper data binding
        ComboBox1.DataSource = new List<string>(Model.VerticalPositions);
        ComboBox1.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.VerticalPosition), 
            false, DataSourceUpdateMode.OnPropertyChanged));
        
        ComboBox2.DataSource = new List<string>(Model.HorizontalAlignments);
        ComboBox2.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.HorizontalAlignment), 
            false, DataSourceUpdateMode.OnPropertyChanged));

        ComboBox3.DataSource = new List<string>(Model.AvailableFonts);
        ComboBox3.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.FontName), 
            false, DataSourceUpdateMode.OnPropertyChanged));

        ComboBox4.DataSource = new List<string>(Model.HorizontalAlignments);
        ComboBox4.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.LogoHorizontalPosition), 
            false, DataSourceUpdateMode.OnPropertyChanged));
        
        ComboBox5.DataSource = new List<string> { "Alto", "Centro", "Basso" };
        ComboBox5.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.LogoVerticalPosition), 
            false, DataSourceUpdateMode.OnPropertyChanged));
        
        // Bind progress bar and status labels
        ProgressBar1.DataBindings.Add(new Binding("Maximum", bindingSource1, nameof(Model.ProgressBarMaximum),
            false, DataSourceUpdateMode.OnPropertyChanged));
        ProgressBar1.DataBindings.Add(new Binding("Value", bindingSource1, nameof(Model.ProgressBarValue),
            false, DataSourceUpdateMode.OnPropertyChanged));
        
        Label18.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.ProcessedImagesCount),
            false, DataSourceUpdateMode.OnPropertyChanged));
        lblFotoTotaliNum.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.TotalImagesCount),
            false, DataSourceUpdateMode.OnPropertyChanged));
        Label10.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.ProcessingStatus),
            false, DataSourceUpdateMode.OnPropertyChanged));

        // Bind transparency model properties to UI
        chkUseTransparentColor.DataBindings.Add(new Binding("Checked", bindingSource1, nameof(Model.UseTransparentColor), false, DataSourceUpdateMode.OnPropertyChanged));
        // Show currently selected color in PictureBox3
        PictureBox3.Visible = false;
        if (!string.IsNullOrWhiteSpace(Model.TransparentColor))
        {
            try
            {
                PictureBox3.BackColor = ColorTranslator.FromHtml(Model.TransparentColor);
                PictureBox3.Visible = true;
            }
            catch
            {
                PictureBox3.Visible = false;
            }
        }

        // When logo file changes, update preview
        Model.PropertyChanged += (s, e) =>
        {
            if (e.PropertyName == nameof(Model.LogoFile) || e.PropertyName == nameof(Model.UseTransparentColor) || e.PropertyName == nameof(Model.TransparentColor))
            {
                if (!string.IsNullOrWhiteSpace(Model.LogoFile) && System.IO.File.Exists(Model.LogoFile))
                {
                    UpdateLogoPictureBox(Model.LogoFile);
                }
            }
        };

        // Bind transparent color hex and show color in PictureBox3
        // Bind UseTransparentColor checkbox (designer control named CheckBox5 used for AddLogo earlier, add new binding control exists in designer)
        // Use PictureBox3 to display color value
        var colorBinding = new Binding("BackColor", bindingSource1, nameof(Model.TransparentColor), true, DataSourceUpdateMode.OnPropertyChanged);
        colorBinding.Format += (s, e) =>
        {
            try
            {
                e.Value = ColorTranslator.FromHtml(e.Value?.ToString() ?? "#FFFFFF");
            }
            catch
            {
                e.Value = Color.White;
            }
        };
        PictureBox3.DataBindings.Add(colorBinding);

        // Bind checkbox for using color key transparency if such control exists (CheckBox5 was repurposed as AddLogo); create binding if available
        try
        {
            // The designer has CheckBox5 for 'AddLogo'. We'll add a separate binding to a new control named CheckBoxUseTransparentColor if present.
            var chk = this.Controls.Find("chkUseTransparentColor", true).FirstOrDefault() as CheckBox;
            if (chk != null)
            {
                chk.DataBindings.Add(new Binding("Checked", bindingSource1, nameof(Model.UseTransparentColor), false, DataSourceUpdateMode.OnPropertyChanged));
            }
        }
        catch { }
    }


    private void Form1_Load(object sender, EventArgs e)
    {
        bindingSource1.DataSource = Model;
        Application.EnableVisualStyles();
        SetDefaults();

        _logger.LogInformation("Programma Avviato");
        // If settings were loaded before the form was shown, ensure the logo preview is updated
        try
        {
            if (!string.IsNullOrWhiteSpace(Model.LogoFile) && File.Exists(Model.LogoFile))
            {
                UpdateLogoPictureBox(Model.LogoFile);
            }
        }
        catch (Exception ex)
        {
            _logger.LogDebug(ex, "Failed to load logo during form load");
        }
    }

    private string CalcTime(DateTime timeStart, DateTime timeStop, int numFoto)
    {
        long timediffH, timediffS;
        long timediffM;

        TimeSpan timeDiff = timeStop - timeStart;

        timediffM = (int)timeDiff.TotalMinutes;
        timediffS = (int)timeDiff.TotalSeconds;
        timediffH = (int)timeDiff.TotalHours;

        // timediffM = DateAndTime.DateDiff(DateInterval.Minute, timeStart, timeStop);
        // timediffS = DateAndTime.DateDiff(DateInterval.Second, timeStart, timeStop);
        // timediffH = DateAndTime.DateDiff(DateInterval.Hour, timeStart, timeStop);

        double fotoSec = numFoto / (double)timediffS;
        double fotoMin = numFoto / (double)timediffM;
        double fotoOra = numFoto / (double)timediffH;
        string s = "S: " + timediffS.ToString() + "; F/s: " +
                   fotoSec.ToString(
                       "0.000"); // + " F/m: " + fotoMin.ToString("0.00") + " F/h: " + fotoOra.ToString("0.00")
        return s;
    }

    private string SelectFolder(string startingFolder)
    {
        var dialog = new FolderBrowserDialog();
        dialog.InitialDirectory = startingFolder;
        if (dialog.ShowDialog() != DialogResult.OK) return string.Empty;

        return FixPath(dialog.SelectedPath);
    }

    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('/', Path.DirectorySeparatorChar)
            .Replace('\\', Path.DirectorySeparatorChar);

        // Remove trailing separators then add one back
        path = path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;

        return path;
    }

    private void OnSelectSourceFolderRequested(object sender, EventArgs e)
    {
        // Prefer model value; if empty fall back to last-used value stored in user prefs
        var starting = !string.IsNullOrWhiteSpace(Model.SourcePath)
            ? Model.SourcePath
            : _parametriSetup.LeggiParametroString("LastSourceFolder");

        var dialogResult = SelectFolder(starting);
        if (!string.IsNullOrWhiteSpace(dialogResult))
        {
            Model.SourcePath = dialogResult;
            _parametriSetup.AggiornaParametro("LastSourceFolder", dialogResult);
            _parametriSetup.SalvaParametriSetup();
        }
    }

    private void BtnOpenSourceFolder_Click(object? sender, EventArgs e)
    {
        // Prefer the model value but fall back to the textbox if needed
        var path = string.IsNullOrWhiteSpace(Model.SourcePath) ? txtSorgente.Text : Model.SourcePath;
        OpenFolder(path);
    }

    private void BtnOpenDestFolder_Click(object? sender, EventArgs e)
    {
        var path = string.IsNullOrWhiteSpace(Model.DestinationPath) ? txtDestinazione.Text : Model.DestinationPath;
        OpenFolder(path);
    }

    private void OpenFolder(string? path)
    {
        if (string.IsNullOrWhiteSpace(path))
        {
            MessageBox.Show(this, "Folder path is empty.", "Open Folder", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }

        path = path.Trim().Trim('"');

        try
        {
            if (File.Exists(path))
            {
                // If a file was provided, open its folder and select it
                Process.Start("explorer.exe", $"/select,\"{path}\"");
                return;
            }

            if (Directory.Exists(path))
            {
                Process.Start(new ProcessStartInfo
                {
                    FileName = path,
                    UseShellExecute = true
                });
                return;
            }

            MessageBox.Show(this, $"Folder does not exist: {path}", "Open Folder", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
        catch (Exception ex)
        {
            _logger?.LogError(ex, "Failed to open folder {Path}", path);
            MessageBox.Show(this, $"Failed to open folder: {ex.Message}", "Open Folder", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void OnSelectDestinationFolderRequested(object sender, EventArgs e)
    {
        var starting = !string.IsNullOrWhiteSpace(Model.DestinationPath)
            ? Model.DestinationPath
            : _parametriSetup.LeggiParametroString("LastDestinationFolder");

        var dialogResult = SelectFolder(starting);
        if (!string.IsNullOrWhiteSpace(dialogResult))
        {
            Model.DestinationPath = dialogResult;
            _parametriSetup.AggiornaParametro("LastDestinationFolder", dialogResult);
            _parametriSetup.SalvaParametriSetup();
        }
    }

    private void OnSelectLogoFileRequested(object sender, EventArgs e)
    {
        var dialog = new OpenFileDialog();
        dialog.Filter = "Image Files|*.jpg;*.jpeg;*.png;*.bmp;*.gif";
        if (!string.IsNullOrWhiteSpace(Model.LogoFile))
        {
            dialog.FileName = Model.LogoFile;
        }
        else
        {
            var lastLogoFolder = _parametriSetup.LeggiParametroString("LastLogoFolder");
            if (!string.IsNullOrWhiteSpace(lastLogoFolder) && Directory.Exists(lastLogoFolder))
            {
                dialog.InitialDirectory = lastLogoFolder;
            }
        }
        
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            Model.LogoFile = dialog.FileName;
            UpdateLogoPictureBox(Model.LogoFile);
            try
            {
                var folder = Path.GetDirectoryName(dialog.FileName) ?? string.Empty;
                if (!string.IsNullOrWhiteSpace(folder))
                {
                    _parametriSetup.AggiornaParametro("LastLogoFolder", folder);
                    _parametriSetup.SalvaParametriSetup();
                }
            }
            catch
            {
                // ignore preferences save failures
            }
        }
    }

    private async void OnSaveSettingsRequested(object sender, string e)
    {
        var saveDialog = new SaveFileDialog
        {
            Filter = "Setup (*.xml)|*.xml|All valid files (*.*)|*.*",
            FilterIndex = 0,
            RestoreDirectory = true
        };
        var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder");
        if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings))
            saveDialog.InitialDirectory = lastSettings;

        if (saveDialog.ShowDialog() != DialogResult.OK) return;

        await Model.SaveSettingsToFileAsync(saveDialog.FileName);
        Text = "Image Catalog - " + Path.GetFileName(saveDialog.FileName);

        try
        {
            var folder = Path.GetDirectoryName(saveDialog.FileName) ?? string.Empty;
            if (!string.IsNullOrWhiteSpace(folder))
            {
                _parametriSetup.AggiornaParametro("LastSettingsFolder", folder);
                _parametriSetup.SalvaParametriSetup();
            }
        }
        catch
        {
            // ignore
        }
    }

    private async void OnLoadSettingsRequested(object sender, string e)
    {
        var openDialog = new OpenFileDialog
        {
            Filter = "Setup (*.xml)|*.xml|All valid files (*.*)|*.*",
            FilterIndex = 0,
            RestoreDirectory = true
        };

        var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder");
        if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings))
            openDialog.InitialDirectory = lastSettings;

        if (openDialog.ShowDialog() != DialogResult.OK) return;

        try
        {
            await Model.LoadSettingsFromFileAsync(openDialog.FileName);

            // Explicitly ensure UI is enabled after loading
            Model.UiEnabled = true;

            // If a logo path was stored in the settings, try to resolve and display it.
            // The stored path may be absolute or relative to the settings file location.
            try
            {
                var storedLogo = Model.LogoFile;
                // Trim whitespace and surrounding quotes which may be present in saved settings
                if (!string.IsNullOrWhiteSpace(storedLogo))
                {
                    storedLogo = storedLogo.Trim();
                    storedLogo = storedLogo.Trim('"');
                }
                if (!string.IsNullOrWhiteSpace(storedLogo))
                {
                    string resolved = storedLogo;
                    // If not rooted, try to resolve relative to the settings file folder
                    var settingsFolder = Path.GetDirectoryName(openDialog.FileName) ?? string.Empty;
                    if (!Path.IsPathRooted(resolved) && !string.IsNullOrWhiteSpace(settingsFolder))
                    {
                        var candidate = Path.Combine(settingsFolder, resolved);
                        if (File.Exists(candidate)) resolved = candidate;
                    }

                    // If rooted but file doesn't exist, try filename near settings file
                    if (!File.Exists(resolved) && !string.IsNullOrWhiteSpace(settingsFolder))
                    {
                        var candidate2 = Path.Combine(settingsFolder, Path.GetFileName(resolved));
                        if (File.Exists(candidate2)) resolved = candidate2;
                    }

                    if (File.Exists(resolved))
                    {
                        // Update the model so data-bound controls reflect the resolved absolute path
                        Model.LogoFile = resolved;
                        UpdateLogoPictureBox(resolved);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogDebug(ex, "Error resolving logo path after loading settings");
            }

            Text = "Image Catalog - " + Path.GetFileName(openDialog.FileName);
            
            try
            {
                var folder = Path.GetDirectoryName(openDialog.FileName) ?? string.Empty;
                if (!string.IsNullOrWhiteSpace(folder))
                {
                    _parametriSetup.AggiornaParametro("LastSettingsFolder", folder);
                    _parametriSetup.SalvaParametriSetup();
                }
            }
            catch
            {
                // ignore preferences save failures
            }

            _logger.LogInformation($"Settings loaded successfully from {openDialog.FileName}");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error loading settings");
            MessageBox.Show($"Error loading settings: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void MainForm_FormClosing(object? sender, FormClosingEventArgs e)
    {
        try
        {
            // Persist last-used dialogs paths (user preferences)
            // These keys are managed independently from settings files
            // and must be saved when the form closes.
            _parametriSetup.AggiornaParametro("LastSourceFolder", Model.SourcePath ?? string.Empty);
            _parametriSetup.AggiornaParametro("LastDestinationFolder", Model.DestinationPath ?? string.Empty);
            _parametriSetup.AggiornaParametro("LastLogoFolder", Path.GetDirectoryName(Model.LogoFile ?? string.Empty) ?? string.Empty);
            _parametriSetup.SalvaParametriSetup();
        }
        catch (Exception ex)
        {
            _logger?.LogWarning(ex, "Failed to save user preferences on exit");
        }
    }

    private void OnSelectColorRequested(object sender, EventArgs e)
    {
        var colorDialog = new ColorDialog
        {
            AllowFullOpen = true
        };

        if (!string.IsNullOrWhiteSpace(Model.TextColorRGB))
        {
            try
            {
                colorDialog.Color = ColorTranslator.FromHtml(Model.TextColorRGB);
            }
            catch
            {
                // Invalid color, use default
            }
        }

        if (colorDialog.ShowDialog() == DialogResult.OK)
        {
            Model.TextColorRGB = ColorTranslator.ToHtml(colorDialog.Color);
            TextBox34.BackColor = colorDialog.Color;
        }
    }

    private void UpdateLogoPictureBox(string logoPath)
    {
        try
        {
            // Load image via System.Drawing for preview so we can use MakeTransparent when requested
            using var img = System.Drawing.Image.FromFile(logoPath);

            System.Drawing.Bitmap previewBmp;
            // If using color-key transparency and a color is selected, apply MakeTransparent for preview
            if (Model.UseTransparentColor && !string.IsNullOrWhiteSpace(Model.TransparentColor))
            {
                try
                {
                    var key = ColorTranslator.FromHtml(Model.TransparentColor);
                    previewBmp = new System.Drawing.Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    using (var g = System.Drawing.Graphics.FromImage(previewBmp))
                    {
                        g.Clear(System.Drawing.Color.Transparent);
                        g.DrawImage(img, 0, 0, img.Width, img.Height);
                    }
                    // Apply exact color-key transparency
                    previewBmp.MakeTransparent(key);
                    PictureBox3.BackColor = key;
                    PictureBox3.Visible = true;
                }
                catch
                {
                    previewBmp = new System.Drawing.Bitmap(img);
                }
            }
            else
            {
                previewBmp = new System.Drawing.Bitmap(img);
            }

            // Resize preview to fit into PictureBox1 while preserving aspect ratio
            // Resize preview to fit into PictureBox1 while preserving aspect ratio
            var boxW = PictureBox1.ClientSize.Width > 0 ? PictureBox1.ClientSize.Width : 449;
            var boxH = PictureBox1.ClientSize.Height > 0 ? PictureBox1.ClientSize.Height : 369;
            var ratio = Math.Min((double)boxW / previewBmp.Width, (double)boxH / previewBmp.Height);
            var destW = Math.Max(1, (int)(previewBmp.Width * ratio));
            var destH = Math.Max(1, (int)(previewBmp.Height * ratio));
            var scaled = new System.Drawing.Bitmap(destW, destH, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            using (var g = System.Drawing.Graphics.FromImage(scaled))
            {
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.Clear(System.Drawing.Color.Transparent);
                // Center the image in the PictureBox
                var offsetX = Math.Max(0, (boxW - destW) / 2);
                var offsetY = Math.Max(0, (boxH - destH) / 2);
                using var canvas = new System.Drawing.Bitmap(boxW, boxH, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                using (var cg = System.Drawing.Graphics.FromImage(canvas))
                {
                    cg.Clear(System.Drawing.Color.Transparent);
                    cg.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    cg.DrawImage(previewBmp, offsetX, offsetY, destW, destH);
                }
                g.DrawImage(canvas, 0, 0);
            }

            // Set PictureBox1 image (dispose previous)
            var old = PictureBox1.Image;
            PictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
            PictureBox1.Image = scaled;
            old?.Dispose();
            previewBmp.Dispose();
        }
        catch
        {
            // Image loading failed, ignore
        }
    }

    private void UpdateLogoPreviewWithColorKey(string logoPath, Color keyColor)
    {
        try
        {
            using var img = (Bitmap)Image.FromFile(logoPath);
            // Create ARGB copy
            var bmp = new Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            using (var g = Graphics.FromImage(bmp))
            {
                g.DrawImage(img, 0, 0, img.Width, img.Height);
            }

            bmp.MakeTransparent(keyColor);

            // Resize to PictureBox1 size similar to previous logic
            Bitmap finalBmp;
            if (bmp.Height >= bmp.Width)
            {
                finalBmp = new Bitmap(bmp, new Size((int)(160 * bmp.Width / (double)bmp.Height), 160));
            }
            else
            {
                finalBmp = new Bitmap(bmp, new Size(160, (int)(160 * bmp.Height / (double)bmp.Width)));
            }

            PictureBox1.Image = finalBmp;
        }
        catch
        {
            // ignore preview failures
        }
    }

    private void setLabel18Text(string text)
    {
        if (Label18.InvokeRequired)
        {
            Label18.Invoke(new Action<string>(setLabel18Text), text);
        }
        else
        {
            Label18.Text = text;
        }
    }
}

public class PicInfo
{
    public DirectoryInfo DirSource, DirDest, DirDestStart;
    public string NomeImmagine;

    public PicInfo(DirectoryInfo Dir_Source, DirectoryInfo Dir_Dest, DirectoryInfo Dir_DestStart,
        string Nome_Immagine)
    {
        DirSource = Dir_Source;
        DirDest = Dir_Dest;
        DirDestStart = Dir_DestStart;
        NomeImmagine = Nome_Immagine;
    }
}