//******************************************************************************//
// SettingsViewModel.cs
// 12/4/2023
//
// RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION
// PROPRIETARY TO RAYTHEON COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS
// AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. DISCLOSURE TO
// UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO
// RAYTHEON COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS
// CONTENTS SHALL BE FURNISHED OR DISCLOSED TO OR COPIED OR USED BY PERSONS
// OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF RAYTHEON
// COMPANY.
//
// THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S. GOVERNMENT
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// WARNING: THIS DOCUMENT CONTAINS TECHNICAL DATA AND / OR TECHNOLOGY WHOSE
// EXPORT OR DISCLOSURE TO NON-U.S. PERSONS, WHEREVER LOCATED, IS RESTRICTED
// BY THE INTERNATIONAL TRAFFIC IN ARMS REGULATIONS (ITAR) (22 C.F.R. SECTION
// 120-130) OR THE EXPORT ADMINISTRATION REGULATIONS (EAR) (15 C.F.R. SECTION
// 730-774). THIS DOCUMENT CANNOT BE EXPORTED (E.G., PROVIDED TO A SUPPLIER
// OUTSIDE OF THE UNITED STATES) OR DISCLOSED TO A NON-U.S. PERSON, WHEREVER
// LOCATED, UNTIL A FINAL JURISDICTION AND CLASSIFICATION DETERMINATION HAS
// BEEN COMPLETED AND APPROVED BY RAYTHEON, AND ANY REQUIRED U.S. GOVERNMENT
// APPROVALS HAVE BEEN OBTAINED. VIOLATIONS ARE SUBJECT TO SEVERE CRIMINAL
// PENALTIES.
//
// DOD 5220.22-M, INDUSTRIAL SECURITY MANUAL, CHAPTER 5, SECTION 1 THROUGH 9 :
// FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN OR DOD 5200.1-R,
// INFORMATION SECURITY PROGRAM, CHAPTER 6. FOR UNCLASSIFIED, LIMITED DOCUMENTS
// DESTROY BY ANY  METHOD THAT WILL PREVENT DISCLOSURE OF CONTENTS OR
// RECONSTRUCTION OF THE DOCUMENT.
// POC: Alex Kravchenko (1118268)
//******************************************************************************//

// Ignore Spelling: Taskbar

using NLog;
using Raytheon.LogDashboard.Helpers;
using Raytheon.LogDashboard.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Web.UI.WebControls;
using System.Windows.Media;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using Raytheon.LogDashboard.View;
using Settings = Raytheon.LogDashboard.Model.Settings;
using GalaSoft.MvvmLight.Messaging;
using System.Threading.Tasks;
using System.IO;
using System.Collections.Concurrent;
using System.Threading;
using System.Text.RegularExpressions;
using Raytheon.Common;
using Microsoft.WindowsAPICodePack.Dialogs;

namespace Raytheon.LogDashboard.ViewModel
{
    public class SettingsViewModel : ViewModelBase
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();

        private ObservableCollection<Receiver> receivers = new ObservableCollection<Receiver>();
        private ObservableCollection<IgnoredIPAddr> ignoredIpAdresses = new ObservableCollection<IgnoredIPAddr>();
                                                                                                                
        private const string ErrorLogger32DefaultLocation = @"%AppData%\Raytheon\ErrorLogger\ErrorLogger.ini";
        private const string ErrorLogger64DefaultLocation = @"%AppData%\Raytheon\ErrorLogger64\ErrorLogger64.ini";

        protected static LogDashViewModelLocator LocatorInstance => (Application.Current.Resources["LogDashLocator"] as LogDashViewModelLocator);

        private CancellationTokenSource _ctsPersistingLogMessages;
        private readonly ConcurrentQueue<LogMessage> _persistingLogQueue = new ConcurrentQueue<LogMessage>();
        private FileSystemWatcher _errorLoggerWatcher;

        private string currentThemeName = string.Empty;

        #region Properties

        public ObservableCollection<Receiver> Receivers
        {
            get => new ObservableCollection<Receiver>(receivers.Where(r => !r.IsHidden));
            set => Set(() => Receivers, ref receivers, value);
        }
        public ObservableCollection<IgnoredIPAddr> IgnoredIPAddresses
        {
            get => ignoredIpAdresses;
            set
            {
                if(value != null)
                {
                    ignoredIpAdresses = new ObservableCollection<IgnoredIPAddr>(value.OrderBy(x => x.IP));
                    RaisePropertyChanged();
                }
            }
        }

        private bool isConfigurationVisible = false;
        public bool IsConfigurationVisible
        {
            get => isConfigurationVisible;
            set => Set(() => IsConfigurationVisible, ref isConfigurationVisible, value);
        }

        private bool isAutoStartReadAtStartup = true;
        public bool IsAutoStartReadAtStartup
        {
            get => isAutoStartReadAtStartup;
            set => Set(() => IsAutoStartReadAtStartup, ref isAutoStartReadAtStartup, value);
        }

        private bool minimizeToTray = false;
        public bool MinimizeToTray
        {
            get => minimizeToTray;
            set => Set(() => MinimizeToTray, ref minimizeToTray, value);
        }

        private bool isEnableMaxMessageBufferSize = false;
        public bool IsEnableMaxMessageBufferSize
        {
            get => isEnableMaxMessageBufferSize;
            set => Set(() => IsEnableMaxMessageBufferSize, ref isEnableMaxMessageBufferSize, value);
        }

        private int maxMessageBufferSize;
        public int MaxMessageBufferSize
        {
            get => maxMessageBufferSize;
            set => Set(() => MaxMessageBufferSize, ref maxMessageBufferSize, value);
        }

        private int deletedMessagesCount;
        public int DeletedMessagesCount
        {
            get => deletedMessagesCount;
            set
            {
                if(value >= MaxMessageBufferSize)
                {
                    deletedMessagesCount = MaxMessageBufferSize;
                }

                if(value <= 0)
                {
                    deletedMessagesCount = 1;
                }

                if(value > 0 && value < MaxMessageBufferSize)
                {
                    deletedMessagesCount = value;
                }

                RaisePropertyChanged();
            }
        }

        private bool onlyOneAppInstance = true;
        public bool OnlyOneAppInstance
        {
            get => onlyOneAppInstance;
            set => Set(() => OnlyOneAppInstance, ref onlyOneAppInstance, value);
        }

        private string displayedDataFormat = "MM/dd/yyyy HH:mm:ss.fff";
        public string DisplayedDataFormat
        {
            get => displayedDataFormat;
            set => Set(() => DisplayedDataFormat, ref displayedDataFormat, value);
        }

        private string typedIP;
        public string TypedIP
        {
            get => typedIP;
            set => Set(() => TypedIP, ref typedIP, value);
        }

        private IgnoredIPAddr selectedIP;
        public IgnoredIPAddr SelectedIP
        {
            get => selectedIP;
            set => Set(() => SelectedIP, ref selectedIP, value);
        }

        private Receiver selectedReceiver;
        public Receiver SelectedReceiver
        {
            get => selectedReceiver;
            set
            {
                selectedReceiver = value;
                IsConfigurationVisible = selectedReceiver != null;
                RaisePropertyChanged();
            }
        }

        public static List<ColorTheme> ColorThemes => Settings.ColorThemes;
        public static List<string> Themes => Settings.Themes;

        public string ExampleDateTime { get; set; }

        public List<string> DataFormats { get; set; } = new List<string>
        {
            "MM/dd/yyyy HH:mm",
            "MM/dd/yyyy HH:mm:ss",
            "MM/dd/yyyy HH:mm:ss.fff",
            "HH:mm",
            "HH:mm:ss",
            "HH:mm:ss.fff",
            "MM/dd/yyyy"
        };

        private string selectedDataFormat;
        public string SelectedDataFormat
        {
            get => selectedDataFormat;
            set
            {
                selectedDataFormat = value;
                ExampleDateTime = DateTime.Now.ToString(selectedDataFormat);
                RaisePropertyChanged(nameof(ExampleDateTime));
                RaisePropertyChanged();
            }
        }

        public string SelectedTheme
        {
            get => LocatorInstance.SelectedTheme;
            set => LocatorInstance.SelectedTheme = value;
        }

        public int SelectedColorIndex
        {
            get => ColorThemes.FindIndex(c => c.Name == SelectedColorTheme.Name);
            set => SelectedColorTheme = ColorThemes[value];
        }

        public ColorTheme SelectedColorTheme
        {
            get => LocatorInstance.SelectedColorTheme;
            set
            {
                LocatorInstance.SelectedColorTheme = value;
                RaisePropertyChanged(() => IconColor);
            }
        }
        public SolidColorBrush IconColor
        {
            get => SelectedColorTheme.Color;
        }

        private bool isShowSourceColumn;
        public bool IsShowSourceColumn
        {
            get => isShowSourceColumn;
            set => Set(() => IsShowSourceColumn, ref isShowSourceColumn, value);
        }

        private bool isShowThreadColumn;
        public bool IsShowThreadColumn
        {
            get => isShowThreadColumn;
            set => Set(() => IsShowThreadColumn, ref isShowThreadColumn, value);
        }

        private bool isShowTaskbarProgress;
        public bool IsShowTaskbarProgress
        {
            get => isShowTaskbarProgress;
            set => Set(() => IsShowTaskbarProgress, ref isShowTaskbarProgress, value);
        }

        private bool showMessageHighlightByReceiverColor;
        public bool ShowMessageHighlightByReceiverColor
        {
            get => showMessageHighlightByReceiverColor;
            set => Set(() => ShowMessageHighlightByReceiverColor, ref showMessageHighlightByReceiverColor, value);
        }

        private bool isSeparateIPLoggersByPort;
        public bool IsSeparateIPLoggersByPort
        {
            get => isSeparateIPLoggersByPort;
            set => Set(() => IsSeparateIPLoggersByPort, ref isSeparateIPLoggersByPort, value);
        }

        private bool isErrorLogger;
        public bool IsErrorLogger
        {
            get => isErrorLogger;
            set 
            { 
                Set(() => IsErrorLogger, ref isErrorLogger, value);
                RaisePropertyChanged(nameof(IsAutoSaveEnabled));
            }
        }

        public string Version { get; set; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();

        public bool EmbeddedMode { get => !Settings.Instance.EmbeddedMode; }

        private bool isAutoSave;
        public bool IsAutoSave
        {
            get => isAutoSave;
            set 
            { 
                Set(() => IsAutoSave, ref isAutoSave, value);
                RaisePropertyChanged(nameof(IsAutoSaveEnabled));
            }
        }

        public bool IsAutoSaveEnabled
        {
            get => isAutoSave && !IsErrorLogger;
        }

        private string autoSaveLocation;
        public string AutoSaveLocation
        {
            get => autoSaveLocation;
            set => Set(() => AutoSaveLocation, ref autoSaveLocation, value);
        }

        #endregion

        public SettingsViewModel()
        {
            //ParseTheme();

            Messenger.Default.Register<LogMessage>(this, "PersistLogMessage", PersistLogMessage);

            try
            {
                Receivers = new ObservableCollection<Receiver>(Settings.Instance.Receivers);
                IgnoredIPAddresses = new ObservableCollection<IgnoredIPAddr>(Settings.Instance.IgnoredIPs);
                IsAutoStartReadAtStartup = Settings.Instance.AutoStartInStartup;
                MinimizeToTray = Settings.Instance.MinimizeToTray;
                OnlyOneAppInstance = Settings.Instance.OnlyOneAppInstance;
                SelectedDataFormat = Settings.Instance.DataFormat;
                DisplayedDataFormat = Settings.Instance.DataFormat;
                IsEnableMaxMessageBufferSize = Settings.Instance.IsEnabledMaxMessageBufferSize;
                MaxMessageBufferSize = Settings.Instance.MaxMessageBufferSize;
                DeletedMessagesCount = Settings.Instance.DeletedMessagesCount;
                ExampleDateTime = DateTime.Now.ToString(DisplayedDataFormat);
                IsShowSourceColumn = Settings.Instance.IsShowSourceColumn;
                IsShowThreadColumn = Settings.Instance.IsShowThreadColumn;
                IsShowTaskbarProgress = Settings.Instance.IsShowTaskbarProgress;
                ShowMessageHighlightByReceiverColor = Settings.Instance.ShowMessageHighlightByReceiverColor;
                IsSeparateIPLoggersByPort = Settings.Instance.IsSeparateIPLoggersByPort;
                IsAutoSave = Settings.Instance.IsAutoSave;
                AutoSaveLocation = Settings.Instance.AutoSaveLocation;
                IsErrorLogger = Settings.Instance.IsErrorLogger;

                var themeColor = Settings.ColorThemes.FirstOrDefault(x => x.Name == Settings.Instance.CurrentColorTheme.Name);
                if(themeColor != null)
                {
                    SelectedColorTheme = themeColor;
                }

                if(IsAutoSave)
                {
                    StartPersistingLogMessages();
                }

                SelectedTheme = Settings.Instance.CurrentTheme;
            }
            catch(Exception e)
            {
                logger.Warn(e, "SettingsViewModel: An error occurred while get receivers settings.");
            }
        }

        #region Commands

        private RelayCommand browseLocationCommand;
        public RelayCommand BrowseLocationCommand => browseLocationCommand ?? (browseLocationCommand = new RelayCommand(BrowseLocation));

        private RelayCommand addReceiverCommand;
        public RelayCommand AddReceiverCommand => addReceiverCommand ?? (addReceiverCommand = new RelayCommand(AddReceiver));

        private RelayCommand removeReceiverCommand;
        public RelayCommand RemoveReceiverCommand => removeReceiverCommand ?? (removeReceiverCommand = new RelayCommand(RemoveReceiver));

        private RelayCommand removeIgnoreIPCommand;
        public RelayCommand RemoveIgnoreIPCommand => removeIgnoreIPCommand ?? (removeIgnoreIPCommand = new RelayCommand(RemoveIgnoreIP));

        private RelayCommand addIgnoreIPCommand;
        public RelayCommand AddIgnoreIPCommand => addIgnoreIPCommand ?? (addIgnoreIPCommand = new RelayCommand(AddIgnoreIP));

        private RelayCommand saveCommand;
        public RelayCommand SaveCommand => saveCommand ?? (saveCommand = new RelayCommand(Save));

        private RelayCommand сancelCommand;
        public RelayCommand CancelCommand => сancelCommand ?? (сancelCommand = new RelayCommand(Cancel));

        private RelayCommand showReleaseNotesCommand;
        public RelayCommand ShowReleaseNotesCommand => showReleaseNotesCommand ?? (showReleaseNotesCommand = new RelayCommand(ShowReleaseNotes));

        #endregion

        private void BrowseLocation()
        {
            string loc = SelectErrorLoggerLocation();

            if(string.IsNullOrEmpty(loc))
            {
                if(receivers.Any(r => r.ReceiverType == ReceiverType.ErrorLogger && r.ErrorLoggerType == ErrorLoggerType.BIT_64))
                {
                    loc = ErrorLogger64DefaultLocation;
                }
                else
                {
                    loc = ErrorLogger32DefaultLocation;
                }
            }

            AutoSaveLocation = loc;
        }

        private string SelectErrorLoggerLocation()
        {
            using(CommonOpenFileDialog folderDialog = new CommonOpenFileDialog())
            {
                folderDialog.IsFolderPicker = true;
                folderDialog.Title = "Select folder for Logging or Navigate to the ErrorLogger.ini file";

                if(folderDialog.ShowDialog() == CommonFileDialogResult.Ok)
                {
                    return folderDialog.FileName;
                }
                else
                {
                    // User canceled the dialog
                    return null;
                }
            }
        }

        private void AddReceiver()
        {
            receivers.Add(new Receiver("UDP", "", ReceiverType.UDP, receivers.Select(r => r.Name)));
            RaisePropertyChanged(() => Receivers);
        }

        private void RemoveReceiver()
        {
            if(!receivers.Any())
            {
                IsConfigurationVisible = false;
            }
            else
            {
                receivers.Remove(SelectedReceiver);
            }
            RaisePropertyChanged(() => Receivers);
        }

        private void AddIgnoreIP()
        {
            if(IgnoredIPAddresses.All(x => !x.IP.Contains(TypedIP)))
            {
                IgnoredIPAddresses.Add(new IgnoredIPAddr { IP = TypedIP });
            }
        }

        private void RemoveIgnoreIP()
        {
            IgnoredIPAddresses.Remove(SelectedIP);
        }

        private void Save()
        {
            if(receivers.Where(r => r.ReceiverType == ReceiverType.UDP).DistinctBy(x => x.Port).Count() !=
                receivers.Count(r => r.ReceiverType == ReceiverType.UDP))
            {
                Messenger.Default.Send("Please Select Unique Port Numbers for UDP Receivers", "DisplayCustomMessage");
                return;
            }

            Settings.Instance.MinimizeToTray = MinimizeToTray;
            Settings.Instance.AutoStartInStartup = IsAutoStartReadAtStartup;
            Settings.Instance.IsShowSourceColumn = IsShowSourceColumn;
            Settings.Instance.IsShowThreadColumn = IsShowThreadColumn;
            Settings.Instance.IsShowTaskbarProgress = IsShowTaskbarProgress;
            Settings.Instance.ShowMessageHighlightByReceiverColor = ShowMessageHighlightByReceiverColor;
            Settings.Instance.CurrentColorTheme = SelectedColorTheme;
            Settings.Instance.DataFormat = SelectedDataFormat;
            Settings.Instance.IgnoredIPs = IgnoredIPAddresses.ToList();
            Settings.Instance.Receivers = receivers.ToList();
            Settings.Instance.FontColor = SelectedColorTheme.Color.ToARGB();
            Settings.Instance.IsEnabledMaxMessageBufferSize = IsEnableMaxMessageBufferSize;
            Settings.Instance.MaxMessageBufferSize = MaxMessageBufferSize;
            Settings.Instance.DeletedMessagesCount = DeletedMessagesCount;
            Settings.Instance.OnlyOneAppInstance = OnlyOneAppInstance;
            Settings.Instance.IsSeparateIPLoggersByPort = IsSeparateIPLoggersByPort;
            Settings.Instance.IsAutoSave = IsAutoSave;
            Settings.Instance.AutoSaveLocation = AutoSaveLocation;
            Settings.Instance.IsErrorLogger = IsErrorLogger;

            if(!string.IsNullOrEmpty(currentThemeName) && SelectedColorTheme.Name != currentThemeName)
            {
                Settings.Instance.ApplyTheme();
            }
            Settings.Instance.CurrentTheme = SelectedTheme;

            Settings.Instance.Save();

            Messenger.Default.Send(true, "SaveSettings");

            if(IsAutoSave)
            {
                StopMonitoringErrorLogger();
                StopPersistingLogMessages();
                Thread.Sleep(200);
                StartPersistingLogMessages();
            }
            else
            {
                StopMonitoringErrorLogger();
                StopPersistingLogMessages();
            }
        }

        private void Cancel()
        {
            Messenger.Default.Send(false, "SaveSettings");
        }

        private void ShowReleaseNotes()
        {
            ReleaseNotesDialog releaseNotesDialog = new ReleaseNotesDialog
            {
                DataContext = new ReleaseNotesViewModel()
            };
            releaseNotesDialog.ShowDialog();
        }

        /// <summary>
        /// identifies the log file location first and then starts writing messages to that file
        /// </summary>
        private void StartPersistingLogMessages()
        {
            if(!IsAutoSave)
            {
                return;
            }

            string baseFileName = string.Empty;

            // if error logger was selected by the user use the location from the ini file
            if(IsErrorLogger)
            {
                string errorLoggerLocation = LogDashViewModelLocator.LogDashboard.GetErrorLoggerLocation();

                FileInfo fileInfo = new FileInfo(errorLoggerLocation);
                
                // special case for error logger.
                if(fileInfo.Exists)
                {
                    IConfigurationFile configurationFile = new ConfigurationFile(errorLoggerLocation);
                    string location = configurationFile.ReadValue("ErrorLogger", "LogPath", "");
                    if(!string.IsNullOrEmpty(location))
                    {
                        baseFileName = location;

                        StopMonitoringErrorLogger();

                        string directory = Path.GetDirectoryName(errorLoggerLocation);
                        string fileName = Path.GetFileName(errorLoggerLocation);

                        _errorLoggerWatcher = new FileSystemWatcher 
                        { 
                            Path = directory,
                            Filter = fileName,
                            NotifyFilter = NotifyFilters.LastWrite
                        };
                        _errorLoggerWatcher.Changed += ErrorLoggerChanged;
                        _errorLoggerWatcher.EnableRaisingEvents = true;
                    }
                }                
            }
            else
            {
                if(string.IsNullOrEmpty(AutoSaveLocation))
                {
                    AutoSaveLocation = PopulateDefaultAutoSaveLocation();
                }

                string autoSaveLocation = ReplaceDatePatterns(AutoSaveLocation);

                // get the file attributes for file or directory
                DirectoryInfo directoryInfo = new DirectoryInfo(autoSaveLocation);
                if(directoryInfo.IsDirectory())
                {
                    if(!Directory.Exists(autoSaveLocation))
                    {
                        // The directory does not exist, so create it
                        Directory.CreateDirectory(autoSaveLocation);
                    }
                    baseFileName = Path.Combine(autoSaveLocation, "LogDashboard.log");
                }
            }

            StartPersistingLogMessagesNow(baseFileName);
        }

        /// <summary>
        /// starts a thread for writing log messages to the designated location
        /// </summary>
        /// <param name="baseFileName"></param>
        private void StartPersistingLogMessagesNow(string baseFileName)
        {
            if(string.IsNullOrEmpty(baseFileName))
            {
                return;
            }

            StopPersistingLogMessages();
            _ctsPersistingLogMessages = new CancellationTokenSource();
            Task.Run(() => MonitorPersistingLogQueue(_persistingLogQueue, _ctsPersistingLogMessages.Token, baseFileName, Settings.Instance.AutoSaveMaxSize));
        }

        private void ErrorLoggerChanged(object sender, FileSystemEventArgs e)
        {
            IConfigurationFile configurationFile = new ConfigurationFile(e.FullPath);
            string location = configurationFile.ReadValue("ErrorLogger", "LogPath", "");
            if(!string.IsNullOrEmpty(location))
            {
                StartPersistingLogMessagesNow(location);
            }
        }

        /// <summary>
        /// if error logger was selected for monitoring changes for the file location
        /// stops monitoring the file.
        /// </summary>
        public void StopMonitoringErrorLogger()
        {
            if(_errorLoggerWatcher == null)
            {
                return;
            }

            _errorLoggerWatcher.EnableRaisingEvents = false;
            _errorLoggerWatcher.Dispose();
            _errorLoggerWatcher = null;
        }

        private string PopulateDefaultAutoSaveLocation()
        {
            string configPath = LocatorInstance.ErrorLogger32DefaultLocation;
            if(File.Exists(configPath))
            {
                IConfigurationFile config = new ConfigurationFile(configPath);
                if(config.ValueExists("ErrorLogger", "LogPath"))
                {
                    return config.ReadValue("ErrorLogger", "LogPath", "");
                }
            }
            string configPath64 = LocatorInstance.ErrorLogger64DefaultLocation;
            if(File.Exists(configPath64))
            {
                IConfigurationFile config = new ConfigurationFile(configPath);
                if(config.ValueExists("ErrorLogger64", "LogPath"))
                {
                    return config.ReadValue("ErrorLogger64", "LogPath", "");
                }
            }
            return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "LogDashboard");
        }

        public static string ReplaceDatePatterns(string input)
        {
            // Define the regex pattern for yyyy-mm-dd or yyyy-mm
            string pattern = @"(yyyy)-((mm)|(MM))(-dd)?";

            // Replace the matched patterns with actual date components
            string result = Regex.Replace(input, pattern, match =>
            {
                bool year = match.Groups[1].Success;
                bool month = match.Groups[3].Success || match.Groups[4].Success;
                bool day = match.Groups[5].Success;

                return year ? DateTime.Now.ToString($"yyyy{(month ? "-MM" : "")}{(day ? "-dd" : "")}") : "";
            });

            return result;
        }

        /// <summary>
        /// generates unique file name
        /// </summary>
        /// <param name="existingFilePath"></param>
        /// <returns></returns>
        private static string GenerateUniqueFileNameDyDateTime(string existingFilePath)
        {
            string directory = Path.GetDirectoryName(existingFilePath);
            string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(existingFilePath);
            string extension = Path.GetExtension(existingFilePath);

            // Append a timestamp to the original filename
            string uniqueFileName = $"{fileNameWithoutExtension}_{DateTime.Now:MMddHHmmss}{extension}";

            // Combine with the directory path
            return Path.Combine(directory, uniqueFileName);
        }

        /// <summary>
        /// Stops recording messages into a rolling file
        /// </summary>
        public void StopPersistingLogMessages()
        {
            _ctsPersistingLogMessages?.Cancel();
            Thread.Sleep(400);
            _ctsPersistingLogMessages?.Dispose();
            _ctsPersistingLogMessages = null;
            ClearQueue(_persistingLogQueue);            
        }

        private void PersistLogMessage(LogMessage logMessage)
        {
            _persistingLogQueue.Enqueue(logMessage);
        }

        /// <summary>
        /// open log file and write LogMessage to it, until reaches the max size limit
        /// then it rolls over to the next file
        /// </summary>
        /// <param name="queue"></param>
        /// <param name="cancellationToken"></param>
        /// <param name="fileName"></param>
        private static void MonitorPersistingLogQueue(ConcurrentQueue<LogMessage> queue, CancellationToken cancellationToken, string baseFileName, long maxFileSize)
        {
            FileStream fileStream = null;
            StreamWriter writer = null;
            bool rollOver = false;

            try
            {
                while(!cancellationToken.IsCancellationRequested)
                {
                    // Open the base file for writing
                    fileStream = new FileStream(baseFileName, FileMode.OpenOrCreate, FileAccess.Write);
                    writer = new StreamWriter(fileStream)
                    {
                        AutoFlush = true
                    };
                    
                    // Move the stream position to the end of the file
                    fileStream.Seek(0, SeekOrigin.End);

                    while(fileStream.Length < maxFileSize && !cancellationToken.IsCancellationRequested)
                    {
                        if(queue.TryDequeue(out var logMessage))
                        {
                            string message = $"{logMessage.Time:MM-dd-yy HH:mm:ss.ffff};{logMessage.Level};{logMessage.ProcessID ?? 0};{logMessage.Thread};{logMessage.Logger};{logMessage.Address};{logMessage.Message}";
                            writer.WriteLine(message);
                        }
                        else
                        {
                            // No message in the queue, wait for a short time
                            Thread.Sleep(100);
                        }
                    }

                    if(fileStream.Length >= maxFileSize)
                    {
                        rollOver = true;
                    }

                    writer?.Close();
                    fileStream?.Close();

                    if(rollOver)
                    {
                        rollOver = false;
                        // Generate a unique file name for the current log file
                        string uniqueFileName = GenerateUniqueFileNameDyDateTime(baseFileName);
                        File.Move(baseFileName, uniqueFileName); // Save a copy of the file

                        // Delete the old base file
                        File.Delete(baseFileName);
                    }
                    Thread.Sleep(100);
                }
            }
            catch(Exception ex)
            {
                Messenger.Default.Send($"An error occurred while trying to log messages to {baseFileName}.\n{ex.Message}", "DisplayCustomMessage");
            }
            finally
            {
                writer?.Close();
                fileStream?.Close();
            }
        }

        private static void ClearQueue<T>(ConcurrentQueue<T> queue)
        {
            while(!queue.IsEmpty)
            {
                queue.TryDequeue(out _);
            }
        }

        private void ParseTheme()
        {
            try
            {
                string source = Application.Current.Resources.MergedDictionaries[2].Source.ToString().Replace(".xaml", string.Empty);
                string themeName = source.Substring(source.LastIndexOf(".") + 1);
                currentThemeName = themeName;
                var theme = Settings.ColorThemes.FirstOrDefault(x => x.Name == themeName);
                if(theme != null)
                {
                    SelectedColorTheme = theme;
                }
            }
            catch(Exception e)
            {
                logger.Warn(e, "An error occurred while ParseTheme");
            }
        }
    }
}
