// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MainViewModel.cs" company="HandBrake Project (https://handbrake.fr)">
//   This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
// </copyright>
// <summary>
//   HandBrakes Main Window
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace HandBrakeWPF.ViewModels
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text.Json;
    using System.Threading;
    using System.Windows;
    using System.Windows.Input;

    using HandBrake.App.Core.Model;
    using HandBrake.App.Core.Utilities;
    using HandBrake.Interop.Interop;
    using HandBrake.Interop.Utilities;

    using HandBrakeWPF.Commands;
    using HandBrakeWPF.Commands.DebugTools;
    using HandBrakeWPF.Commands.Menu;
    using HandBrakeWPF.Commands.Presets;
    using HandBrakeWPF.EventArgs;
    using HandBrakeWPF.Helpers;
    using HandBrakeWPF.Model;
    using HandBrakeWPF.Model.Audio;
    using HandBrakeWPF.Model.Options;
    using HandBrakeWPF.Model.Queue;
    using HandBrakeWPF.Model.Subtitles;
    using HandBrakeWPF.Properties;
    using HandBrakeWPF.Services.Encode.Model;
    using HandBrakeWPF.Services.Encode.Model.Models;
    using HandBrakeWPF.Services.Interfaces;
    using HandBrakeWPF.Services.Presets.Interfaces;
    using HandBrakeWPF.Services.Presets.Model;
    using HandBrakeWPF.Services.Queue.Interfaces;
    using HandBrakeWPF.Services.Queue.Model;
    using HandBrakeWPF.Services.Scan.EventArgs;
    using HandBrakeWPF.Services.Scan.Interfaces;
    using HandBrakeWPF.Services.Scan.Model;
    using HandBrakeWPF.Startup;
    using HandBrakeWPF.Utilities;
    using HandBrakeWPF.Utilities.FileDialogs;
    using HandBrakeWPF.ViewModels.Interfaces;
    using HandBrakeWPF.Views;

    using Action = System.Action;
    using Application = System.Windows.Application;
    using DataFormats = System.Windows.DataFormats;
    using DragEventArgs = System.Windows.DragEventArgs;
    using ILog = Services.Logging.Interfaces.ILog;

    public class MainViewModel : ViewModelBase, IMainViewModel
    {
        private readonly IQueueService queueProcessor;
        private readonly IPresetService presetService;
        private readonly IErrorService errorService;
        private readonly IUpdateService updateService;
        private readonly IWindowManager windowManager;
        private readonly INotifyIconService notifyIconService;
        private readonly ILog logService;
        private readonly INotificationService notificationService;
        private readonly IUserSettingService userSettingService;
        private readonly IScan scanService;
        private readonly WindowsTaskbar windowsTaskbar = new WindowsTaskbar();
        private readonly DelayedActionProcessor delayedPreviewprocessor = new DelayedActionProcessor();

        private string windowName;
        private string sourceLabel;
        private string statusLabel;
        private string programStatusLabel;
        private Source scannedSource;
        private Title selectedTitle;
        private string duration;
        private bool showStatusWindow;
        private Preset selectedPreset;
        private QueueTask queueEditTask;
        private int lastEncodePercentage;
        private bool showSourceSelection;
        private BindingList<SourceMenuItem> drives;
        private bool showAlertWindow;
        private string alertWindowHeader;
        private string alertWindowText;
        private bool hasSource;
        private bool isSettingPreset;
        private bool isModifiedPreset;
        private bool updateAvailable;
        private bool isNavigationEnabled;
        private double progressAmount;

        public MainViewModel(
            IUserSettingService userSettingService,
            IScan scanService,
            IPresetService presetService,
            IErrorService errorService,
            IUpdateService updateService,
            IPrePostActionService whenDoneService,
            IWindowManager windowManager,
            IPictureSettingsViewModel pictureSettingsViewModel,
            IVideoViewModel videoViewModel,
            ISummaryViewModel summaryViewModel,
            IFiltersViewModel filtersViewModel,
            IAudioViewModel audioViewModel,
            ISubtitlesViewModel subtitlesViewModel,
            IChaptersViewModel chaptersViewModel,
            IStaticPreviewViewModel staticPreviewViewModel,
            IQueueViewModel queueViewModel,
            IMetaDataViewModel metaDataViewModel,
            INotifyIconService notifyIconService,
            ISystemService systemService,
            ILog logService,
            INotificationService notificationService)
            : base(userSettingService)
        {
            this.scanService = scanService;
            this.presetService = presetService;
            this.errorService = errorService;
            this.updateService = updateService;
            this.windowManager = windowManager;
            this.notifyIconService = notifyIconService;
            this.logService = logService;
            this.notificationService = notificationService;
            this.QueueViewModel = queueViewModel;
            this.userSettingService = userSettingService;
            this.queueProcessor = IoCHelper.Get<IQueueService>();

            this.SummaryViewModel = summaryViewModel;
            this.PictureSettingsViewModel = pictureSettingsViewModel;
            this.VideoViewModel = videoViewModel;
            this.MetaDataViewModel = metaDataViewModel;
            this.FiltersViewModel = filtersViewModel;
            this.AudioViewModel = audioViewModel;
            this.SubtitleViewModel = subtitlesViewModel;
            this.ChaptersViewModel = chaptersViewModel;
            this.StaticPreviewViewModel = staticPreviewViewModel;

            // Setup Properties
            this.WindowTitle = Resources.HandBrake_Title;
            this.CurrentTask = new EncodeTask();
            this.ScannedSource = new Source();
            this.HasSource = false;
            this.IsNavigationEnabled = true;

            // Setup Events
            this.scanService.ScanStarted += this.ScanStared;
            this.scanService.ScanCompleted += this.ScanCompleted;
            this.scanService.ScanStatusChanged += this.ScanStatusChanged;
            this.queueProcessor.JobProcessingStarted += this.QueueProcessorJobProcessingStarted;
            this.queueProcessor.QueueCompleted += this.QueueCompleted;
            this.queueProcessor.QueueChanged += this.QueueChanged;
            this.queueProcessor.QueuePaused += this.QueueProcessor_QueuePaused;
            this.queueProcessor.QueueJobStatusChanged += this.QueueProcessor_QueueJobStatusChanged;
            this.userSettingService.SettingChanged += this.UserSettingServiceSettingChanged;

            this.PresetsCategories = new BindingList<IPresetObject>();
            this.Drives = new BindingList<SourceMenuItem>();

            // Set Process Priority. Only when using in-process encoding. 
            // When process isolation is enabled, we'll stick to "Normal".
            if (!this.userSettingService.GetUserSetting<bool>(UserSettingConstants.ProcessIsolationEnabled))
            {
                switch ((ProcessPriority)this.userSettingService.GetUserSetting<int>(UserSettingConstants.ProcessPriorityInt))
                {
                    case ProcessPriority.High:
                        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
                        break;
                    case ProcessPriority.AboveNormal:
                        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.AboveNormal;
                        break;
                    case ProcessPriority.Normal:
                        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Normal;
                        break;
                    case ProcessPriority.Low:
                        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Idle;
                        break;
                    default:
                        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.BelowNormal;
                        break;
                }
            }


            // Setup Commands
            this.QueueCommand = new QueueCommands(this.QueueViewModel);
            this.ProcessDriveCommand = new SimpleRelayCommand<object>(this.ProcessDrive);
            this.WhenDoneCommand = new SimpleRelayCommand<int>(this.WhenDone);
            this.PresetClone = new PresetClone(presetService, errorService, windowManager);

            // Monitor the system.
            systemService.Start();
            
            this.Load();
        }

        public SimpleRelayCommand<int> WhenDoneCommand { get; set; }

        public ICommand PresetClone { get; set; }

        public ICommand PresetMove => new PresetMoveCommand(this, this.presetService);

        /* View Model Properties */

        public IPictureSettingsViewModel PictureSettingsViewModel { get; set; }

        public IAudioViewModel AudioViewModel { get; set; }

        public ISubtitlesViewModel SubtitleViewModel { get; set; }

        public IChaptersViewModel ChaptersViewModel { get; set; }

        public IVideoViewModel VideoViewModel { get; set; }

        public IFiltersViewModel FiltersViewModel { get; set; }

        public IQueueViewModel QueueViewModel { get; set; }

        public IStaticPreviewViewModel StaticPreviewViewModel { get; set; }

        public IMetaDataViewModel MetaDataViewModel { get; set; }

        public ISummaryViewModel SummaryViewModel { get; set; }

        public int SelectedTab { get; set; }

        /* Commands */
        public ICommand AddToQueueQualitySweepCommand => new AddToQueueQualitySweepCommand(this, this.VideoViewModel, this.userSettingService, this.errorService);
        public SimpleRelayCommand<object> ProcessDriveCommand { get; set; }

        /* Properties */

        public string WindowTitle
        {
            get => this.windowName;

            set
            {
                if (!Equals(this.windowName, value))
                {
                    this.windowName = value;
                    this.NotifyOfPropertyChange(() => this.WindowTitle);
                }
            }
        }

        public string ProgramStatusLabel
        {
            get => string.IsNullOrEmpty(this.programStatusLabel) ? Resources.State_Ready : this.programStatusLabel;

            set
            {
                if (!Equals(this.programStatusLabel, value))
                {
                    this.programStatusLabel = value;
                    this.NotifyOfPropertyChange(() => this.ProgramStatusLabel);
                }
            }
        }

        public string StatusLabel
        {
            get => string.IsNullOrEmpty(this.statusLabel) ? Resources.State_Ready : this.statusLabel;

            set
            {
                if (!Equals(this.statusLabel, value))
                {
                    this.statusLabel = value;
                    this.NotifyOfPropertyChange(() => this.StatusLabel);
                }
            }
        }

        public bool QueueRecoveryArchivesExist { get; set; }

        public IEnumerable<IPresetObject> PresetsCategories { get; set; }

        public Preset SelectedPreset
        {
            get => this.selectedPreset;

            set
            {
                if (!object.Equals(this.selectedPreset, value))
                {
                    if (value == null)
                    {
                        this.errorService.ShowError("Null Preset", null, Environment.StackTrace.ToString());
                    }

                    if (value != null)
                    {
                        bool result = this.PresetSelect(value);
                        if (result)
                        {
                            this.selectedPreset = value;
                            this.NotifyOfPropertyChange(() => this.SelectedPreset);
                        }
                    }
                }
            }
        }

        public bool IsModifiedPreset
        {
            get => this.isModifiedPreset;

            set
            {
                if (value == this.isModifiedPreset)
                {
                    return;
                }

                this.isModifiedPreset = value;
                this.NotifyOfPropertyChange(() => this.IsModifiedPreset);
            }
        }

        public EncodeTask CurrentTask { get; set; }

        public Source ScannedSource
        {
            get => this.scannedSource;

            set
            {
                this.scannedSource = value;
                this.NotifyOfPropertyChange(() => this.ScannedSource);
                this.NotifyOfPropertyChange(() => this.ScannedSource.Titles);
            }
        }

        public int TitleSpecificScan { get; set; }

        public string SourceLabel
        {
            get => string.IsNullOrEmpty(this.sourceLabel) ? Resources.Main_SelectSource : this.sourceLabel;

            set
            {
                if (!Equals(this.sourceLabel, value))
                {
                    this.sourceLabel = value;
                    this.NotifyOfPropertyChange(() => SourceLabel);
                }
            }
        }

        public double ProgressAmount
        {
            get => this.progressAmount;
            set
            {
                if (value.Equals(this.progressAmount))
                {
                    return;
                }

                this.progressAmount = value;
                this.NotifyOfPropertyChange(() => this.ProgressAmount);
            }
        }

        public BindingList<PointToPointMode> RangeMode { get; } = new BindingList<PointToPointMode> { PointToPointMode.Chapters, PointToPointMode.Seconds, PointToPointMode.Frames };

        public bool ShowTextEntryForPointToPointMode => this.SelectedPointToPoint != PointToPointMode.Chapters;

        public IEnumerable<int> StartEndRangeItems
        {
            get => this.SelectedTitle?.Chapters.Select(item => item.ChapterNumber).Select(dummy => dummy).ToList();
        }

        public IEnumerable<int> Angles
        {
            get
            {
                if (this.SelectedTitle == null)
                {
                    return null;
                }

                List<int> items = new List<int>();
                for (int i = 1; i <= this.selectedTitle.AngleCount; i++)
                {
                    items.Add(i);
                }

                return items;
            }
        }

        public string Duration
        {
            get => string.IsNullOrEmpty(duration) ? "--:--:--" : duration;
            set
            {
                duration = value;
                this.NotifyOfPropertyChange(() => Duration);
            }
        }

        public bool IsEncoding => this.queueProcessor.IsEncoding;

        public bool ShowStatusWindow
        {
            get => this.showStatusWindow;

            set
            {
                this.showStatusWindow = value;
                this.IsNavigationEnabled = !this.showStatusWindow;
                this.NotifyOfPropertyChange(() => this.ShowStatusWindow);
            }
        }

        public IEnumerable<OutputFormat> OutputFormats => new List<OutputFormat> { OutputFormat.Mp4, OutputFormat.Mov, OutputFormat.Mkv, OutputFormat.WebM };

        public string Destination
        {
            get => this.CurrentTask.Destination;
            set
            {
                value = value?.Replace("\"", string.Empty);

                if (!Equals(this.CurrentTask.Destination, value))
                {
                    if (!string.IsNullOrEmpty(value))
                    {
                        string ext = string.Empty;
                        try
                        {
                            if (FileHelper.FilePathHasInvalidChars(value))
                            {
                                this.errorService.ShowMessageBox(Resources.Main_InvalidDestination, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
                                return;
                            }

                            if (value == this.SelectedTitle.SourcePath)
                            {
                                this.errorService.ShowMessageBox(Resources.Main_MatchingFileOverwriteWarning, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
                                return;
                            }

                            ext = Path.GetExtension(value);
                        }
                        catch (Exception exc)
                        {
                            this.errorService.ShowError(Resources.Main_InvalidDestination, string.Empty, value + Environment.NewLine + exc);
                            return;
                        }

                        this.CurrentTask.Destination = value;
                        this.NotifyOfPropertyChange(() => this.Destination);

                        switch (ext)
                        {
                            case ".mkv":
                                this.SummaryViewModel.SetContainer(OutputFormat.Mkv);
                                break;
                            case ".mp4":
                            case ".m4v":
                                this.SummaryViewModel.SetContainer(OutputFormat.Mp4);
                                break;
                            case ".mov":
                                this.SummaryViewModel.SetContainer(OutputFormat.Mov);
                                break;
                            case ".webm":
                                this.SummaryViewModel.SetContainer(OutputFormat.WebM);
                                break;
                        }
                    }
                    else
                    {
                        this.CurrentTask.Destination = string.Empty;
                        this.NotifyOfPropertyChange(() => this.Destination);
                    }
                }
            }
        }

        public Title SelectedTitle
        {
            get => this.selectedTitle;

            set
            {
                if (!Equals(this.selectedTitle, value))
                {
                    this.selectedTitle = value;

                    if (this.selectedTitle == null)
                    {
                        return;
                    }

                    // Use the Path on the Title, or the Source Scan path if one doesn't exist.
                    this.SourceLabel = this.SelectedTitle?.DisplaySourceName;
                    this.CurrentTask.Source = this.selectedTitle.SourcePath;
                    this.CurrentTask.Title = value.TitleNumber;
                    this.NotifyOfPropertyChange(() => this.StartEndRangeItems);
                    this.NotifyOfPropertyChange(() => this.SelectedTitle);
                    this.NotifyOfPropertyChange(() => this.Angles);
                    this.NotifyOfPropertyChange(() => this.SourceInfo);

                    // Default the Start and End Point dropdowns
                    this.SelectedStartPoint = 1;
                    this.SelectedEndPoint = this.selectedTitle.Chapters != null &&
                                            this.selectedTitle.Chapters.Count != 0
                                                ? this.selectedTitle.Chapters.Last().ChapterNumber
                                                : 1;

                    DefaultRangeMode defaultRangeMode = this.userSettingService.GetUserSetting<DefaultRangeMode>(UserSettingConstants.DefaultRangeMode);
                    switch (defaultRangeMode)
                    {
                        case DefaultRangeMode.Chapters:
                            this.SelectedPointToPoint = PointToPointMode.Chapters;
                            break;
                        case DefaultRangeMode.Frames:
                            this.SelectedPointToPoint = PointToPointMode.Frames;
                            break;
                        case DefaultRangeMode.Seconds:
                            this.SelectedPointToPoint = PointToPointMode.Seconds;
                            break;
                    }

                    this.SelectedAngle = 1;

                    this.TriggerAutonameChange(ChangedOption.Source);

                    this.NotifyOfPropertyChange(() => this.CurrentTask);

                    this.Duration = this.DurationCalculation();

                    // Setup the tab controls
                    this.SetupTabs();
                }
            }
        }

        public int SelectedAngle
        {
            get => this.CurrentTask.Angle;

            set
            {
                this.CurrentTask.Angle = value;
                this.NotifyOfPropertyChange(() => this.SelectedAngle);

                this.TriggerAutonameChange(ChangedOption.Angle);
            }
        }

        public bool IsTimespanRange { get; set; }

        public long SelectedStartPoint
        {
            get => this.CurrentTask.StartPoint;

            set
            {
                this.CurrentTask.StartPoint = value;
                this.NotifyOfPropertyChange(() => this.SelectedStartPoint);
                this.Duration = this.DurationCalculation();

                TriggerAutonameChange(ChangedOption.Chapters);

                if (this.SelectedStartPoint > this.SelectedEndPoint)
                {
                    this.SelectedEndPoint = this.SelectedStartPoint;
                }
            }
        }

        public long SelectedEndPoint
        {
            get => this.CurrentTask.EndPoint;

            set
            {
                this.CurrentTask.EndPoint = value;
                this.NotifyOfPropertyChange(() => this.SelectedEndPoint);
                this.Duration = this.DurationCalculation();

                TriggerAutonameChange(ChangedOption.Chapters);

                if (this.SelectedStartPoint > this.SelectedEndPoint && this.SelectedPointToPoint == PointToPointMode.Chapters)
                {
                    this.SelectedStartPoint = this.SelectedEndPoint;
                }
            }
        }

        public PointToPointMode SelectedPointToPoint
        {
            get => this.CurrentTask.PointToPointMode;
            set
            {
                this.CurrentTask.PointToPointMode = value;
                this.NotifyOfPropertyChange(() => SelectedPointToPoint);
                this.NotifyOfPropertyChange(() => ShowTextEntryForPointToPointMode);

                if (value == PointToPointMode.Chapters && this.SelectedTitle != null)
                {
                    if (this.selectedTitle == null)
                    {
                        return;
                    }

                    this.SelectedStartPoint = 1;
                    this.SelectedEndPoint = selectedTitle.Chapters != null && selectedTitle.Chapters.Count > 0 ? selectedTitle.Chapters.Last().ChapterNumber : 1;
                }
                else if (value == PointToPointMode.Seconds)
                {
                    if (this.selectedTitle == null)
                    {
                        return;
                    }

                    this.SelectedStartPoint = 0;

                    int timeInSeconds;
                    if (int.TryParse(Math.Round(selectedTitle.Duration.TotalSeconds, 0).ToString(CultureInfo.InvariantCulture), out timeInSeconds))
                    {
                        this.SelectedEndPoint = timeInSeconds;
                    }

                    this.IsTimespanRange = true;
                    this.NotifyOfPropertyChange(() => this.IsTimespanRange);
                }
                else
                {
                    if (this.selectedTitle == null)
                    {
                        return;
                    }

                    // Note this does not account for VFR. It's only a guesstimate. 
                    double estimatedTotalFrames = selectedTitle.Fps * selectedTitle.Duration.TotalSeconds;

                    this.SelectedStartPoint = 0;
                    int totalFrames;
                    if (int.TryParse(Math.Round(estimatedTotalFrames, 0).ToString(CultureInfo.InvariantCulture), out totalFrames))
                    {
                        this.SelectedEndPoint = totalFrames;
                    }

                    this.IsTimespanRange = false;
                    this.NotifyOfPropertyChange(() => this.IsTimespanRange);
                }
            }
        }

        public int ProgressPercentage { get; set; }

        public bool ShowSourceSelection
        {
            get => this.showSourceSelection;
            set
            {
                if (value.Equals(this.showSourceSelection))
                {
                    return;
                }
                this.showSourceSelection = value;
                this.NotifyOfPropertyChange(() => this.ShowSourceSelection);

                this.IsNavigationEnabled = !showSourceSelection;

                // Refresh the drives.
                if (this.showSourceSelection)
                {
                    this.Drives.Clear();
                    foreach (SourceMenuItem menuItem in from item in DriveUtilities.GetDrives()
                                                        let driveInformation = item
                                                        select new SourceMenuItem
                                                        {
                                                            Text = string.Format("{0} ({1})", item.RootDirectory, item.VolumeLabel), 
                                                            Command = new SourceMenuCommand(() => this.ProcessDrive(driveInformation)), 
                                                            Tag = item, 
                                                            IsDrive = true
                                                        })
                    {
                        this.Drives.Add(menuItem);
                    }

                    this.TitleSpecificScan = 0;
                    this.NotifyOfPropertyChange(() => this.TitleSpecificScan);
                }
            }
        }

        public BindingList<SourceMenuItem> Drives
        {
            get => this.drives;
            set
            {
                if (Equals(value, this.drives))
                {
                    return;
                }

                this.drives = value;
                this.NotifyOfPropertyChange(() => this.Drives);
            }
        }

        public Action CancelAction => this.CancelScan;

        public Action OpenLogWindowAction => this.OpenLogWindow;

        public bool ShowAlertWindow
        {
            get => this.showAlertWindow;
            set
            {
                if (value.Equals(this.showAlertWindow))
                {
                    return;
                }
                this.showAlertWindow = value;
                this.IsNavigationEnabled = !this.showAlertWindow;
                this.NotifyOfPropertyChange(() => this.ShowAlertWindow);
            }
        }

        public string AlertWindowHeader
        {
            get => this.alertWindowHeader;
            set
            {
                if (value == this.alertWindowHeader)
                {
                    return;
                }

                this.alertWindowHeader = value;
                this.NotifyOfPropertyChange(() => this.AlertWindowHeader);
            }
        }

        public string AlertWindowText
        {
            get => this.alertWindowText;
            set
            {
                if (value == this.alertWindowText)
                {
                    return;
                }

                this.alertWindowText = value;
                this.NotifyOfPropertyChange(() => this.AlertWindowText);
            }
        }

        public Action AlertWindowClose => this.CloseAlertWindow;

        public int QueueCount => this.queueProcessor.Count;
        
        public bool IsQueueCountVisible => this.queueProcessor.Count > 0;

        public string QueueLabel => string.Format(Resources.Main_QueueLabel, string.Empty);

        public string StartLabel
        {
            get
            {
                if (this.queueProcessor.IsPaused && this.queueProcessor.ActiveJobCount > 0)
                {
                    return Resources.Main_ResumeEncode;
                }

                return this.queueProcessor.Count > 0 ? Resources.Main_StartQueue : Resources.Main_Start;
            } 
        }

        public bool IsNavigationEnabled
        {
            get => this.isNavigationEnabled;
            set
            {
                if (value == this.isNavigationEnabled) return;
                this.isNavigationEnabled = value;
                this.NotifyOfPropertyChange(() => this.IsNavigationEnabled);
                this.NotifyOfPropertyChange(() => this.HasSource);
            }
        }

        public bool HasSource
        {
            get => this.hasSource && this.IsNavigationEnabled;

            set
            {
                if (value.Equals(this.hasSource))
                {
                    return;
                }

                this.hasSource = value;
                this.NotifyOfPropertyChange(() => this.HasSource);
            }
        }

        public string SourceInfo => SourceInfoHelper.GenerateSourceInfo(this.SelectedTitle);

        public bool ShowAddAllToQueue => this.userSettingService.GetUserSetting<bool>(UserSettingConstants.ShowAddAllToQueue);

        public bool ShowAddSelectionToQueue => this.userSettingService.GetUserSetting<bool>(UserSettingConstants.ShowAddSelectionToQueue);

        public string ShowAddAllMenuName =>
            string.Format("{0} {1}", (!this.ShowAddAllToQueue ? Resources.MainView_Show : Resources.MainView_Hide), Resources.MainView_ShowAddAllToQueue);

        public string ShowAddSelectionMenuName =>
            string.Format("{0} {1}", (!this.ShowAddSelectionToQueue ? Resources.MainView_Show : Resources.MainView_Hide), Resources.MainView_ShowAddSelectionToQueue);

        public bool UpdateAvailable
        {
            get => this.updateAvailable;
            set
            {
                if (value == this.updateAvailable)
                {
                    return;
                }

                this.updateAvailable = value;
                this.NotifyOfPropertyChange(() => this.UpdateAvailable);
            }
        }

        public bool IsOldNightly { get; set; }

        public bool IsMultiProcess { get; set; }

        public bool IsNightly => HandBrakeVersionHelper.IsNightly();

        public bool IsPresetPaneDisplayed { get; set; }

        public bool IsPresetDescriptionVisible { get; set; }

        public bool IsMenuStylePresetDisplayed { get; set; }

        public bool IsLegacyMenuShown { get; set; }

        public string ShowHideMenuText => this.IsLegacyMenuShown ? Resources.MainView_HideClassicMenu : Resources.MainView_ShowClassicMenu;

        /* Commands */

        public ICommand QueueCommand { get; set; }

        /* Load and Shutdown Handling */
        
        public override void OnLoad()
        {
            // Perform an update check if required
            this.updateService.PerformStartupUpdateCheck(this.HandleUpdateCheckResults);

            // Setup the presets.
            this.presetService.Load();
            this.PresetsCategories = this.presetService.Presets;
            this.NotifyOfPropertyChange(() => this.PresetsCategories);
            this.presetService.LoadCategoryStates();

            this.SummaryViewModel.OutputFormatChanged += this.SummaryViewModel_OutputFormatChanged;

            // Queue Recovery
            bool queueRecovered = QueueRecoveryHelper.RecoverQueue(this.queueProcessor, this.errorService, StartupOptions.AutoRestartQueue, StartupOptions.QueueRecoveryIds);
            this.QueueRecoveryArchivesExist = QueueRecoveryHelper.ArchivesExist();
            this.NotifyOfPropertyChange(() => this.QueueRecoveryArchivesExist);

            // If the queue is not recovered, show the source selection window by default.
            if (!queueRecovered)
            {
                this.ShowSourceSelection = true;
            }
            else
            {
                this.HasSource = true; // Enable the GUI. Needed for in-line queue.
            }

            // If the user has enabled --auto-start-queue, start the queue.
            if (StartupOptions.AutoRestartQueue && !this.queueProcessor.IsProcessing && this.queueProcessor.Count > 0)
            {
                this.queueProcessor.Start();
            }

            // Preset Selection
            this.SelectDefaultPreset();

            // Reset WhenDone if necessary.
            if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.ResetWhenDoneAction))
            {
                this.WhenDone(0);
            }

            // Log Cleaning
            if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.ClearOldLogs))
            {
                Thread clearLog = new Thread(() => GeneralUtilities.ClearLogFiles(7));
                clearLog.Start();
            }

            // Preset Panel
            if (userSettingService.GetUserSetting<bool>(UserSettingConstants.ShowPresetDesc))
            {
                IsPresetDescriptionVisible = true;
                this.NotifyOfPropertyChange(() => this.IsPresetDescriptionVisible);
            }

            this.PictureSettingsViewModel.TabStatusChanged += this.TabStatusChanged;
            this.VideoViewModel.TabStatusChanged += this.TabStatusChanged;
            this.FiltersViewModel.TabStatusChanged += this.TabStatusChanged;
            this.AudioViewModel.TabStatusChanged += this.TabStatusChanged;
            this.SubtitleViewModel.TabStatusChanged += this.TabStatusChanged;
            this.ChaptersViewModel.TabStatusChanged += this.TabStatusChanged;
            this.MetaDataViewModel.TabStatusChanged += this.TabStatusChanged;
            this.SummaryViewModel.TabStatusChanged += this.TabStatusChanged;

            // Menu State
            this.IsLegacyMenuShown = this.userSettingService.GetUserSetting<bool>(UserSettingConstants.IsLegacyMenuShown);
            this.NotifyOfPropertyChange(() => this.IsLegacyMenuShown);
            this.NotifyOfPropertyChange(() => this.ShowHideMenuText);

            // Preset UI
            PresetUiType uiType = this.userSettingService.GetUserSetting<PresetUiType>(UserSettingConstants.PresetUiType);
            this.IsMenuStylePresetDisplayed = uiType == PresetUiType.Menu;
            this.NotifyOfPropertyChange(() => IsMenuStylePresetDisplayed);
        }

        public void Shutdown()
        {
            // Notification Service
            this.notificationService.Shutdown();

            // Shutdown Service
            this.queueProcessor.Stop(true);
            this.presetService.SaveCategoryStates();

            // Unsubscribe from Events.
            this.scanService.ScanStarted -= this.ScanStared;
            this.scanService.ScanCompleted -= this.ScanCompleted;
            this.scanService.ScanStatusChanged -= this.ScanStatusChanged;
            this.queueProcessor.QueuePaused -= this.QueueProcessor_QueuePaused;
            this.queueProcessor.QueueCompleted -= this.QueueCompleted;
            this.queueProcessor.QueueChanged -= this.QueueChanged;
          
            this.queueProcessor.JobProcessingStarted -= this.QueueProcessorJobProcessingStarted;
            this.userSettingService.SettingChanged -= this.UserSettingServiceSettingChanged;

            this.SummaryViewModel.OutputFormatChanged -= this.SummaryViewModel_OutputFormatChanged;

            // Tab status events
            this.PictureSettingsViewModel.TabStatusChanged -= this.TabStatusChanged;
            this.VideoViewModel.TabStatusChanged -= this.TabStatusChanged;
            this.FiltersViewModel.TabStatusChanged -= this.TabStatusChanged;
            this.AudioViewModel.TabStatusChanged -= this.TabStatusChanged;
            this.SubtitleViewModel.TabStatusChanged -= this.TabStatusChanged;
            this.ChaptersViewModel.TabStatusChanged -= this.TabStatusChanged;
            this.MetaDataViewModel.TabStatusChanged -= this.TabStatusChanged;
            this.SummaryViewModel.TabStatusChanged -= this.TabStatusChanged;
        }

        /* Menu and Toolbar */

        public void OpenAutonamePreferences()
        {
            OpenOptionsScreenCommand command = new OpenOptionsScreenCommand();
            command.Execute(OptionsTab.OutputFiles);
        }

        public void OpenAboutApplication()
        {
            OpenOptionsScreenCommand command = new OpenOptionsScreenCommand();
            command.Execute(OptionsTab.About);
        }

        public void OpenOptionsWindow()
        {
            OpenOptionsScreenCommand command = new OpenOptionsScreenCommand();
            command.Execute(null);
        }
         
        public void OpenLogWindow()
        {
            WindowHelper.ShowWindow<ILogViewModel, LogView>(this.windowManager);
        }

        public void OpenQueueWindow()
        {
            WindowHelper.ShowWindow<IQueueViewModel, QueueView>(this.windowManager);
        }

        public void OpenPreviewWindow()
        {
            if (!string.IsNullOrEmpty(this.CurrentTask.Source) && !this.StaticPreviewViewModel.IsOpen)
            {
                this.StaticPreviewViewModel.IsOpen = true;
                this.StaticPreviewViewModel.UpdatePreviewFrame(this.SelectedTitle, this.CurrentTask, this.ScannedSource);
                this.windowManager.ShowWindow<StaticPreviewView>(this.StaticPreviewViewModel);
            }
            else if (this.StaticPreviewViewModel.IsOpen)
            {
                WindowHelper.ShowWindow<IStaticPreviewViewModel, StaticPreviewView>(this.windowManager);
            }
        }

        private void HandleManagePresetChanges(Preset preset)
        {
            this.PresetsCategories = null;
            this.NotifyOfPropertyChange(() => this.PresetsCategories);

            this.PresetsCategories = this.presetService.Presets;
            this.NotifyOfPropertyChange(() => this.PresetsCategories);

            this.selectedPreset = preset; // Reselect the preset      
            this.NotifyOfPropertyChange(() => this.SelectedPreset);
        }
        
        public void LaunchHelp()
        {
            try
            {
                Process.Start("explorer.exe", "https://handbrake.fr/docs");
            }
            catch (Exception exc)
            {
                this.errorService.ShowError(Resources.Main_UnableToLoadHelpMessage, Resources.Main_UnableToLoadHelpSolution, exc);
            }
        }

        public void CheckForUpdates()
        {
            OpenOptionsScreenCommand command = new OpenOptionsScreenCommand();
            command.Execute(OptionsTab.Updates);
        }
        
        public void NightlyUpdate()
        {
            try
            {
                Process.Start("explorer.exe", "https://github.com/HandBrake/HandBrake-snapshots");
            }
            catch (Exception exc)
            {
                this.errorService.ShowError(Resources.Main_UnableToLoadHelpMessage, Resources.Main_UnableToLoadHelpSolution, exc);
            }
        }

        public AddQueueError AddToQueue(bool batch)
        {
            if (this.ScannedSource == null || string.IsNullOrEmpty(this.SelectedTitle?.SourcePath) || this.ScannedSource.Titles.Count == 0)
            {
                return new AddQueueError(Resources.Main_ScanSource, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
            }

            if (string.IsNullOrEmpty(this.CurrentTask.Destination))
            {
                return new AddQueueError(Resources.Main_SetDestination, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
            }

            if (this.Destination.ToLower() == this.SelectedTitle.SourcePath.ToLower())
            {
                return new AddQueueError(Resources.Main_MatchingFileOverwriteWarning, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
            }

            if (File.Exists(this.CurrentTask.Destination))
            {
                FileOverwriteBehaviour behaviour = (FileOverwriteBehaviour)this.userSettingService.GetUserSetting<int>(UserSettingConstants.FileOverwriteBehaviour);
                if (behaviour == FileOverwriteBehaviour.Ask)
                {
                    MessageBoxResult result = this.errorService.ShowMessageBox(string.Format(Resources.Main_QueueOverwritePrompt, Path.GetFileName(this.CurrentTask.Destination)), Resources.Question, MessageBoxButton.YesNo, MessageBoxImage.Warning);
                    if (result == MessageBoxResult.No)
                    {
                        return null; // Handled by the above action.
                    }
                }
            }

            if (!DirectoryUtilities.IsWritable(Path.GetDirectoryName(this.CurrentTask.Destination), true))
            {
                return new AddQueueError(Resources.Main_NoPermissionsOrMissingDirectory, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
            }

            if (!batch && !DriveUtilities.HasMinimumDiskSpace(
                this.Destination,
                this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel)))
            {
                MessageBoxResult result = this.errorService.ShowMessageBox(Resources.Main_LowDiskspace, Resources.Warning, MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result == MessageBoxResult.No)
                {
                    return null; // Handled by the above action.
                }
            }

            // Sanity check the filename
            if (FileHelper.FilePathHasInvalidChars(this.Destination))
            {
                this.NotifyOfPropertyChange(() => this.Destination);
                return new AddQueueError(Resources.Main_InvalidDestination, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
            }

            // defer to subtitle's validation messages
            if (!this.SubtitleViewModel.ValidateSubtitles())
            {
                return new AddQueueError(Resources.Subtitles_WebmSubtitleIncompatibilityHeader, Resources.Main_PleaseFixSubtitleSettings, MessageBoxButton.OK, MessageBoxImage.Error);
            }

            QueueTask task = new QueueTask(new EncodeTask(this.CurrentTask), this.SelectedTitle.SourcePath, this.SelectedPreset, this.IsModifiedPreset, this.selectedTitle);

            if (!this.queueProcessor.CheckForDestinationPathDuplicates(task.Task.Destination))
            {
                this.queueProcessor.Add(task);
            }
            else
            {
                return new AddQueueError(Resources.Main_DuplicateDestinationOnQueue, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Warning);
            }

            if (!this.IsEncoding)
            {
                this.ProgramStatusLabel = string.Format(Resources.Main_XEncodesPending, this.queueProcessor.Count);
            }

            return null;
        }

        public void AddToQueueWithErrorHandling()
        {
            var addError = this.AddToQueue(false);
            if (addError != null)
            {
                this.errorService.ShowMessageBox(addError.Message, addError.Header, addError.Buttons, addError.ErrorType);
            }
        }

        public void AddAllToQueue()
        {
            if (this.ScannedSource == null || this.ScannedSource.Titles == null || this.ScannedSource.Titles.Count == 0)
            {
                this.errorService.ShowMessageBox(Resources.Main_ScanSource, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (!AutoNameHelper.IsAutonamingEnabled())
            {
                this.errorService.ShowMessageBox(Resources.Main_TurnOnAutoFileNaming, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (!DriveUtilities.HasMinimumDiskSpace(this.Destination, this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel)))
            {
                MessageBoxResult result = this.errorService.ShowMessageBox(Resources.Main_LowDiskspace, Resources.Warning, MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result == MessageBoxResult.No)
                {
                    return; // Handled by the above action.
                }
            }

            foreach (Title title in this.ScannedSource.Titles)
            {
                this.SelectedTitle = title;
                var addError = this.AddToQueue(true);
                if (addError != null)
                {
                    MessageBoxResult result = this.errorService.ShowMessageBox(addError.Message + Environment.NewLine + Environment.NewLine + Resources.Main_ContinueAddingToQueue, addError.Header, MessageBoxButton.YesNo, addError.ErrorType);

                    if (result == MessageBoxResult.No)
                    {
                        break;
                    }
                }
            }
        }

        public void AddSelectionToQueue()
        {
            if (this.ScannedSource == null || this.ScannedSource.Titles == null || this.ScannedSource.Titles.Count == 0)
            {
                this.errorService.ShowMessageBox(Resources.Main_ScanSource, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (!AutoNameHelper.IsAutonamingEnabled())
            {
                this.errorService.ShowMessageBox(Resources.Main_TurnOnAutoFileNaming, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (!DriveUtilities.HasMinimumDiskSpace(this.Destination, this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel)))
            {
                MessageBoxResult result = this.errorService.ShowMessageBox(Resources.Main_LowDiskspace, Resources.Warning, MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result == MessageBoxResult.No)
                {
                    return; // Handled by the above action.
                }
            }

            // Always use the current settings when adding to the queue as best as possible.
            Preset temporaryPreset = this.selectedPreset;
            if (this.IsModifiedPreset)
            {
                temporaryPreset = new Preset(this.SelectedPreset);
                temporaryPreset.Name = string.Format(
                    "{0} {1}",
                    temporaryPreset.Name,
                    Resources.MainView_ModifiedPreset);
                temporaryPreset.Task = new EncodeTask(this.CurrentTask);
                temporaryPreset.AudioTrackBehaviours = new AudioBehaviours(this.AudioViewModel.AudioBehaviours);
                temporaryPreset.SubtitleTrackBehaviours = new SubtitleBehaviours(this.SubtitleViewModel.SubtitleBehaviours);
            }

            Window window = Application.Current.Windows.Cast<Window>().FirstOrDefault(x => x.GetType() == typeof(QueueSelectionViewModel));
            IQueueSelectionViewModel viewModel = IoCHelper.Get<IQueueSelectionViewModel>();

            viewModel.Setup(
                this.ScannedSource,
                (tasks, limits) => {  this.BatchAddTitles(tasks, limits); },
                temporaryPreset);

            if (window != null)
            {
                window.Activate();
            }
            else
            {
                this.windowManager.ShowWindow<QueueSelectionView>(viewModel);
            }
        }

        private void BatchAddTitles(IEnumerable<SelectionTitle> tasks, QueueAddRangeLimit limits)
        {
            bool foundOutOfBound = false;

            foreach (SelectionTitle title in tasks)
            {
                bool outOfBounds = false;
                this.SelectedTitle = title.Title;
                if (limits != null && limits.IsEnabled)
                {
                    this.SelectedPointToPoint = limits.SelectedPointToPoint;
                    if (limits.SelectedPointToPoint == PointToPointMode.Seconds)
                    {
                        long totalSeconds = (long)this.SelectedTitle.Duration.TotalSeconds;
                        if (limits.SelectedStartPoint > totalSeconds)
                        {
                            outOfBounds = true;
                            foundOutOfBound = true;
                        }
                        else
                        {
                            this.SelectedStartPoint = Math.Max(0, limits.SelectedStartPoint);
                            this.SelectedEndPoint = Math.Min(limits.SelectedEndPoint, totalSeconds);
                        }
                    }
                    else if (limits.SelectedPointToPoint == PointToPointMode.Frames)
                    {
                        this.SelectedStartPoint = Math.Max(0, limits.SelectedStartPoint);
                        this.SelectedEndPoint = limits.SelectedEndPoint;
                    }
                    else
                    {
                        if (limits.SelectedStartPoint > this.selectedTitle.Chapters.Count)
                        {
                            outOfBounds = true;
                            foundOutOfBound = true;
                        }
                        else
                        {
                            // Last chapter only, if the range is out of limits.
                            this.SelectedStartPoint = Math.Max(0, limits.SelectedStartPoint);
                            this.SelectedEndPoint = Math.Min(limits.SelectedEndPoint, this.StartEndRangeItems.Last());
                        }
                    }
                }

                if (!outOfBounds)
                {
                    var addError = this.AddToQueue(true);
                    if (addError != null)
                    {
                        MessageBoxResult result = this.errorService.ShowMessageBox(addError.Message + Environment.NewLine + Environment.NewLine + Resources.Main_ContinueAddingToQueue, addError.Header, MessageBoxButton.YesNo, addError.ErrorType);

                        if (result == MessageBoxResult.No)
                        {
                            break;
                        }
                    }
                }
            }

            if (foundOutOfBound)
            {
                this.errorService.ShowMessageBox(Resources.AddToQueue_RangeLimitError, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        public void FolderScan()
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog { Description = Resources.Main_PleaseSelectFolder };
            bool? dialogResult = dialog.ShowDialog();

            if (dialogResult.HasValue && dialogResult.Value)
            {
                if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.RecursiveFolderScan) &&  !FileHelper.IsDvdOrBluray(dialog.SelectedPath))
                {
                    this.StartScan(FileHelper.FileList(dialog.SelectedPath, true, this.userSettingService.GetUserSetting<List<string>>(UserSettingConstants.ExcludedExtensions)), this.TitleSpecificScan);
                }
                else
                {
                    this.StartScan(new List<string> { dialog.SelectedPath }, this.TitleSpecificScan);
                }
            }
        }

        public void FileScan()
        {
            OpenFileDialog dialog = new OpenFileDialog { Filter = "All files (*.*)|*.*", Multiselect = true };

            string mruDir = this.GetMru(Constants.FileScanMru);
            if (!string.IsNullOrEmpty(mruDir) && Directory.Exists(mruDir))
            {
                dialog.InitialDirectory = mruDir;
            }

            bool? dialogResult = null;
            try
            {
                dialogResult = dialog.ShowDialog();
            }
            catch (Exception e)
            {
                this.SetMru(Constants.FileScanMru, string.Empty); // RESET MRU in case it's the fault.
                this.errorService.ShowMessageBox(
                    Resources.MainViewModel_FilePathSelectError,
                    Resources.Error,
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
                this.logService.LogMessage("Attempted to recover from an error fro the File Scan FileDialog: " + e);
                return;
            }
            
            if (dialogResult.HasValue && dialogResult.Value)
            {
                if (!string.IsNullOrEmpty(dialog.FileName))
                {
                    this.SetMru(Constants.FileScanMru, Path.GetDirectoryName(dialog.FileName));
                }

                this.StartScan(dialog.FileNames.ToList(), this.TitleSpecificScan);
            }
        }

        public void CancelScan()
        {
            this.ShowStatusWindow = false;
            this.scanService.Cancel();
        }

        public void StartEncode()
        {
            if (this.queueProcessor.IsProcessing)
            {
                this.NotifyOfPropertyChange(() => this.IsEncoding);
                return;
            }

            // Check if we already have jobs, and if we do, just start the queue.
            if (this.queueProcessor.Count != 0 || this.queueProcessor.IsPaused)
            {
                this.NotifyOfPropertyChange(() => this.IsEncoding);
                this.QueueViewModel.StartQueue(); // Provides user checks.
                return;
            }

            // Otherwise, perform Sanity Checking then add to the queue and start if everything is ok.
            if (this.SelectedTitle == null)
            {
                this.errorService.ShowMessageBox(Resources.Main_ScanSource, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            // Create the Queue Task and Start Processing
            var addError = this.AddToQueue(false);
            if (addError == null)
            {
                if (this.queueProcessor.Count == 0)
                {
                    return;
                }

                this.NotifyOfPropertyChange(() => this.IsEncoding);

                this.QueueViewModel.StartQueue(); // Provides user checks.
            }
            else
            {
                this.errorService.ShowMessageBox(
                    addError.Message,
                    addError.Header,
                    addError.Buttons,
                    addError.ErrorType);
            }
        }

        public void EditQueueJob(QueueTask queueTask)
        {
            // Rescan the source to make sure it's still valid
            EncodeTask task = queueTask.Task;

            this.queueEditTask = queueTask;
            this.scanService.Scan(new List<string> { task.Source }, task.Title, QueueEditAction);
        }

        public void PauseEncode()
        {
            if (!this.queueProcessor.IsEncoding || this.queueProcessor.IsPaused)
            {
                return;
            }

            this.queueProcessor.Pause(true);
            this.NotifyOfPropertyChange(() => this.IsEncoding);
        }

        public void StopEncode()
        {
            MessageBoxResult result = this.errorService.ShowMessageBox(
                Resources.MainView_StopEncodeConfirm,
                Resources.MainView_StopEncode,
                MessageBoxButton.YesNo,
                MessageBoxImage.Question);

            if (result == MessageBoxResult.Yes)
            {
                this.queueProcessor.Stop(true);
            }
        }

        public void ExitApplication()
        {
            Application.Current.Shutdown();
        }

        public void SelectSourceWindow()
        {
            ShowSourceSelection = !ShowSourceSelection;
        }

        public void CloseSourceSelection()
        {
            this.ShowSourceSelection = false;
        }

        public void CloseAlertWindow()
        {
            this.ShowAlertWindow = false;
            this.AlertWindowText = string.Empty;
            this.AlertWindowHeader = string.Empty;
        }

        public void WhenDone(int action)
        {
            this.QueueViewModel?.WhenDone(action, true);
        }

        public void ExportSourceData()
        {
            if (this.ScannedSource == null)
            {
                return; 
            }

            string json = JsonSerializer.Serialize(this.ScannedSource, JsonSettings.Options);

            SaveFileDialog savefiledialog = new SaveFileDialog
                                            {
                                                Filter = "json|*.json",
                                                CheckPathExists = true,
                                                AddExtension = true,
                                                DefaultExt = ".json",
                                                OverwritePrompt = true,
                                                FilterIndex = 0,
                                                FileName = "debug.scan_output.json"
            };

            savefiledialog.ShowDialog();

            if (!string.IsNullOrEmpty(savefiledialog.FileName))
            {
                using (StreamWriter writer = new StreamWriter(savefiledialog.FileName))
                {
                    writer.Write(json);
                }
            }
        }

        public void ImportSourceData()
        {
            OpenFileDialog dialog = new OpenFileDialog { Filter = "Debug Files|*.json", CheckFileExists = true };
            bool? dialogResult = dialog.ShowDialog();
            if (dialogResult.HasValue && dialogResult.Value)
            {
                using (StreamReader reader = new StreamReader(dialog.FileName))
                {
                    string json = reader.ReadToEnd();
                    if (!string.IsNullOrEmpty(json))
                    {
                       Source source = JsonSerializer.Deserialize<Source>(json, JsonSettings.Options);
                       this.ScannedSource = source;
                       this.HasSource = true;
                       this.SelectedTitle = this.ScannedSource.Titles.FirstOrDefault(t => t.MainTitle) ?? this.ScannedSource.Titles.FirstOrDefault();
                    }
                }
            }
        }

        public void TogglePresetPane()
        {
            this.IsPresetPaneDisplayed = !this.IsPresetPaneDisplayed;
            if (!this.IsPresetPaneDisplayed)
            {
                this.presetService.Save(); // Save any updates on close. I.e moves
            }

            this.NotifyOfPropertyChange(() => IsPresetPaneDisplayed);
        }

        public void NextTitle()
        {
            if (this.ScannedSource == null || this.SelectedTitle == null)
            {
                return;
            }

            int index = this.ScannedSource.Titles.IndexOf(this.selectedTitle);
            if (this.ScannedSource.Titles.Count >= (index + 2))
            {
                this.SelectedTitle = this.ScannedSource.Titles[index + 1];
            }
        }

        public void PreviousTitle()
        {
            if (this.ScannedSource == null || this.SelectedTitle == null)
            {
                return;
            }

            int index = this.ScannedSource.Titles.IndexOf(this.selectedTitle);
            if (index >= 1)
            {
                this.SelectedTitle = this.ScannedSource.Titles[index -1];
            }
        }

        public void ShowHideMenu()
        {
            this.IsLegacyMenuShown = !this.IsLegacyMenuShown;
            this.NotifyOfPropertyChange(() => this.IsLegacyMenuShown);
            this.NotifyOfPropertyChange(() => this.ShowHideMenuText);

            this.userSettingService.SetUserSetting(UserSettingConstants.IsLegacyMenuShown, this.IsLegacyMenuShown);
        }

        /* Main Window Public Methods*/

        public void FilesDroppedOnWindow(DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] fileNames = e.Data.GetData(DataFormats.FileDrop, true) as string[];
                if (fileNames != null && fileNames.Any() && (File.Exists(fileNames[0]) || Directory.Exists(fileNames[0])))
                {
                    List<string> videoContent = fileNames.Where(f => Path.GetExtension(f)?.ToLower() != ".srt" && Path.GetExtension(f)?.ToLower() != ".ssa" && Path.GetExtension(f)?.ToLower() != ".ass").ToList();

                    if (videoContent.Count == 1 && Directory.Exists(videoContent[0]))
                    {
                        // Is a directory.
                        if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.RecursiveFolderScan) && !FileHelper.IsDvdOrBluray(videoContent[0]))
                        {
                            this.StartScan(FileHelper.FileList(videoContent[0], true, this.userSettingService.GetUserSetting<List<string>>(UserSettingConstants.ExcludedExtensions)), this.TitleSpecificScan);
                        }
                        else
                        {
                            this.StartScan(videoContent, 0);
                        }
                    } 
                    else if (videoContent.Count >= 1)
                    {
                        this.StartScan(videoContent, 0);
                        return;
                    }

                    // StartScan is not synchronous, so for now we don't support adding both srt and video file at the same time. 
                    string[] subtitleFiles = fileNames.Where(f => Path.GetExtension(f)?.ToLower() == ".srt" || Path.GetExtension(f)?.ToLower() == ".ssa" || Path.GetExtension(f)?.ToLower() == ".ass").ToArray();
                    if (subtitleFiles.Any())
                    {
                        this.SwitchTab(5);
                        this.SubtitleViewModel.Import(subtitleFiles);
                    }
                }
            }

            e.Handled = true;
        }

        public void SwitchTab(int i)
        {
            this.SelectedTab = i;
            this.NotifyOfPropertyChange(() => this.SelectedTab);
        }

        public void BrowseDestination()
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog
            {
                Filter = "mp4|*.mp4;*.m4v|mov|*.mov|mkv|*.mkv|webm|*.webm", 
                CheckPathExists = true, 
                AddExtension = true, 
                DefaultExt = ".mp4",
                RestoreDirectory = true
            };

            saveFileDialog.OverwritePrompt =
                (FileOverwriteBehaviour)this.userSettingService.GetUserSetting<int>(UserSettingConstants.FileOverwriteBehaviour) == FileOverwriteBehaviour.Ask;

            string extension = Path.GetExtension(this.CurrentTask.Destination);

            saveFileDialog.FilterIndex = !string.IsNullOrEmpty(this.CurrentTask.Destination)
                                         && !string.IsNullOrEmpty(extension)
                                             ? (extension == ".mp4" || extension == ".m4v" ? 1 : 2)
                                             : (this.CurrentTask.OutputFormat == OutputFormat.Mkv 
                                                 ? 2 
                                                 : (this.CurrentTask.OutputFormat == OutputFormat.WebM ? 3 : 0));

            string mruDir = this.GetMru(Constants.FileSaveMru);
            if (!string.IsNullOrEmpty(mruDir) && Directory.Exists(mruDir))
            {
                saveFileDialog.InitialDirectory = mruDir;
            }

            // If we have a current directory, override the MRU.
            if (this.CurrentTask != null && !string.IsNullOrEmpty(this.CurrentTask.Destination))
            {
                if (Directory.Exists(Path.GetDirectoryName(this.CurrentTask.Destination)))
                {
                    saveFileDialog.InitialDirectory = Path.GetDirectoryName(this.CurrentTask.Destination);
                }

                saveFileDialog.FileName = Path.GetFileName(this.CurrentTask.Destination);
            }

            bool? result = saveFileDialog.ShowDialog();
            if (result.HasValue && result.Value)
            {
                this.SetMru(Constants.FileSaveMru, Path.GetDirectoryName(saveFileDialog.FileName));

                this.Destination = saveFileDialog.FileName;

                // Set the Extension Dropdown. This will also set Mp4/m4v correctly.
                if (!string.IsNullOrEmpty(saveFileDialog.FileName))
                {
                    switch (Path.GetExtension(saveFileDialog.FileName))
                    {
                        case ".mkv":
                            this.SummaryViewModel.SetContainer(OutputFormat.Mkv);
                            break;
                        case ".mp4":
                        case ".m4v":
                            this.SummaryViewModel.SetContainer(OutputFormat.Mp4);
                            break;
                        case ".mov":
                            this.SummaryViewModel.SetContainer(OutputFormat.Mov);
                            break;
                        case ".webm":
                            this.SummaryViewModel.SetContainer(OutputFormat.WebM);
                            break;
                    }

                    this.NotifyOfPropertyChange(() => this.CurrentTask);
                }
            }
        }

        public void OpenDestinationDirectory()
        {
            if (!string.IsNullOrEmpty(this.Destination))
            {
                try
                {
                    string directory = Path.GetDirectoryName(this.Destination);
                    if (!string.IsNullOrEmpty(directory) && Directory.Exists(directory))
                    {
                        Process.Start("explorer.exe", directory);
                    }
                    else
                    {
                        MessageBoxResult result =
                            errorService.ShowMessageBox(
                                string.Format(Resources.DirectoryUtils_CreateFolderMsg, directory),
                                Resources.DirectoryUtils_CreateFolder,
                                MessageBoxButton.YesNo,
                                MessageBoxImage.Question);
                        if (result == MessageBoxResult.Yes)
                        {
                            Directory.CreateDirectory(directory);
                            Process.Start("explorer.exe", directory);
                        }
                    }
                }
                catch (Exception exc)
                {
                    this.errorService.ShowError(Resources.MainViewModel_UnableToLaunchDestDir, Resources.MainViewModel_UnableToLaunchDestDirSolution, exc);
                }
            }
        }

        public void PresetAdd()
        {
            IAddPresetViewModel presetViewModel = IoCHelper.Get<IAddPresetViewModel>();
            presetViewModel.Setup(this.CurrentTask, this.AudioViewModel.AudioBehaviours, this.SubtitleViewModel.SubtitleBehaviours, null);
            bool? result = this.windowManager.ShowDialog<AddPresetView>(presetViewModel);

            if (result.HasValue && result.Value)
            {
                this.NotifyOfPropertyChange(() => this.PresetsCategories);
                this.SelectedPreset = this.presetService.GetPreset(presetViewModel.PresetName);

                this.IsModifiedPreset = false;
            }
        }

        public void PresetUpdate()
        {
            if (this.SelectedPreset == null)
            {
                this.errorService.ShowMessageBox(
                    Resources.Main_SelectPresetForUpdate, Resources.Main_NoPresetSelected, MessageBoxButton.OK, MessageBoxImage.Warning);

                return;
            }

            if (this.selectedPreset.IsBuildIn)
            {
                this.errorService.ShowMessageBox(
                    Resources.Main_NoUpdateOfBuiltInPresets, Resources.Main_NoPresetSelected, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (this.errorService.ShowMessageBox(Resources.Main_PresetUpdateConfirmation, Resources.AreYouSure, MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
            {
                this.selectedPreset.Update(new EncodeTask(this.CurrentTask), new AudioBehaviours(this.AudioViewModel.AudioBehaviours), new SubtitleBehaviours(this.SubtitleViewModel.SubtitleBehaviours));
                this.presetService.Update(this.selectedPreset.Name, this.selectedPreset);
                this.IsModifiedPreset = false;

                this.errorService.ShowMessageBox(
                        Resources.Main_PresetUpdated, Resources.Updated, MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        public void PresetManage()
        {
            if (this.SelectedPreset == null)
            {
                this.errorService.ShowMessageBox(
                    Resources.Main_SelectPresetForUpdate, Resources.Main_NoPresetSelected, MessageBoxButton.OK, MessageBoxImage.Warning);

                return;
            }

            IManagePresetViewModel presetViewModel = IoCHelper.Get<IManagePresetViewModel>();
            presetViewModel.Setup(this.selectedPreset);
            this.windowManager.ShowDialog<ManagePresetView>(presetViewModel);
            Preset preset = presetViewModel.Preset;

            this.NotifyOfPropertyChange(() => this.PresetsCategories);
            this.selectedPreset = preset; // Reselect the preset      
            this.NotifyOfPropertyChange(() => this.SelectedPreset);
        }

        public void PresetRemoveSelected()
        {
            this.PresetRemove(this.SelectedPreset);
        }

        public void PresetRemove(object presetObj)
        {
            Preset preset = presetObj as Preset;
            if (preset != null)
            {
                if (preset.IsDefault)
                {
                    this.errorService.ShowMessageBox(
                      Resources.MainViewModel_CanNotDeleteDefaultPreset, 
                      Resources.Warning, 
                      MessageBoxButton.OK, 
                      MessageBoxImage.Information);

                    return;
                }

                MessageBoxResult result =
                this.errorService.ShowMessageBox(
                   Resources.MainViewModel_PresetRemove_AreYouSure + preset.Name + " ?", 
                   Resources.Question, 
                   MessageBoxButton.YesNo, 
                   MessageBoxImage.Question);

                if (result == MessageBoxResult.No)
                {
                    return;
                }

                this.presetService.Remove(preset.Name);
                this.NotifyOfPropertyChange(() => this.PresetsCategories);
                this.SelectedPreset = this.presetService.GetDefaultPreset();
            }
            else
            {
                this.errorService.ShowMessageBox(Resources.Main_SelectPreset, Resources.Warning, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        public void PresetSetDefault()
        {
            if (this.selectedPreset != null)
            {
                this.presetService.SetDefault(this.selectedPreset.Name);
                this.errorService.ShowMessageBox(string.Format(Resources.Main_NewDefaultPreset, this.selectedPreset.Name), Resources.Main_Presets, MessageBoxButton.OK, MessageBoxImage.Information);
            }
            else
            {
                this.errorService.ShowMessageBox(Resources.Main_SelectPreset, Resources.Warning, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        public void PresetImport()
        {
            OpenFileDialog dialog = new OpenFileDialog { Filter = "Preset Files|*.json", CheckFileExists = true };
            bool? dialogResult = dialog.ShowDialog();
            if (dialogResult.HasValue && dialogResult.Value)
            {
                this.presetService.Import(dialog.FileName);
                this.NotifyOfPropertyChange(() => this.PresetsCategories);
            }
        }

        public void PresetExport()
        {
            if (this.selectedPreset != null && !this.selectedPreset.IsBuildIn)
            {
                SaveFileDialog savefiledialog = new SaveFileDialog
                                                {
                                                    Filter = "json|*.json",
                                                    CheckPathExists = true,
                                                    AddExtension = true,
                                                    DefaultExt = ".json",
                                                    OverwritePrompt = true,
                                                    FilterIndex = 0
                                                };

                savefiledialog.ShowDialog();
                string filename = savefiledialog.FileName;

                if (!string.IsNullOrEmpty(filename))
                {
                    this.presetService.Export(savefiledialog.FileName, this.selectedPreset.Name);
                }
            }
            else
            {
                this.errorService.ShowMessageBox(Resources.Main_SelectPreset, Resources.Warning, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        public void PresetReset()
        {
            this.presetService.UpdateBuiltInPresets();
            this.NotifyOfPropertyChange(() => this.PresetsCategories);
            this.SelectDefaultPreset();
            this.errorService.ShowMessageBox(Resources.Presets_ResetComplete, Resources.Presets_ResetHeader, MessageBoxButton.OK, MessageBoxImage.Information);
        }

        public void PresetDeleteBuildIn()
        {
            this.presetService.DeleteBuiltInPresets();
            this.NotifyOfPropertyChange(() => this.PresetsCategories);

            if (this.presetService.GetDefaultPreset() != null)
            {
                this.SelectedPreset = this.presetService.GetDefaultPreset();
            }
        }

        public void ExportUserPresets()
        {
            SaveFileDialog savefiledialog = new SaveFileDialog
                                            {
                                                Filter = "json|*.json",
                                                CheckPathExists = true,
                                                AddExtension = true,
                                                DefaultExt = ".json",
                                                OverwritePrompt = true,
                                                FilterIndex = 0
                                            };

            savefiledialog.ShowDialog();
            string filename = savefiledialog.FileName;

            if (!string.IsNullOrEmpty(filename))
            {
                IList<PresetDisplayCategory> userPresets = this.presetService.GetPresetCategories(true);
                this.presetService.ExportCategories(savefiledialog.FileName, userPresets);
            }
        }

        public void PresetReSelect()
        {
            this.PresetSelect(this.SelectedPreset);
        }

        public bool PresetSelect(object tag)
        {
            Preset preset = tag as Preset;
            if (preset != null)
            {
                if (preset.IsPresetDisabled)
                {
                    return false;
                }

                this.selectedPreset = preset;
                this.NotifyOfPropertyChange(() => this.SelectedPreset);

                this.presetService.SetSelected(this.selectedPreset.Name);

                if (this.selectedPreset != null)
                {
                    // Tab Settings
                    this.isSettingPreset = true;
                    this.IsModifiedPreset = false;
                    this.SummaryViewModel.SetPreset(this.selectedPreset, this.CurrentTask);
                    this.PictureSettingsViewModel.SetPreset(this.selectedPreset, this.CurrentTask);
                    this.VideoViewModel.SetPreset(this.selectedPreset, this.CurrentTask);
                    this.FiltersViewModel.SetPreset(this.selectedPreset, this.CurrentTask);
                    this.AudioViewModel.SetPreset(this.selectedPreset, this.CurrentTask);
                    this.SubtitleViewModel.SetPreset(this.selectedPreset, this.CurrentTask);
                    this.ChaptersViewModel.SetPreset(this.selectedPreset, this.CurrentTask);
                    this.MetaDataViewModel.SetPreset(this.selectedPreset, this.CurrentTask);
                    this.SummaryViewModel.UpdateDisplayedInfo();

                    this.isSettingPreset = false;

                    if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.AutoNaming))
                    {
                        if (this.userSettingService.GetUserSetting<string>(UserSettingConstants.AutoNameFormat) != null)
                        {
                            this.TriggerAutonameChange(ChangedOption.Preset);
                        }
                    }

                    return true;
                }
            }

            return false;
        }

        public void ShowHidePresetDesc()
        {
            this.IsPresetDescriptionVisible = !this.IsPresetDescriptionVisible;
            this.userSettingService.SetUserSetting(UserSettingConstants.ShowPresetDesc, this.IsPresetDescriptionVisible);
            this.NotifyOfPropertyChange(() => this.IsPresetDescriptionVisible);
        }

        public void StartScan(List<string> filePaths, int title)
        {
            if (filePaths != null && filePaths.Count > 0)
            {
                ShowSourceSelection = false;
                this.scanService.Scan(filePaths, title, null);
            }
        }

        public void ProcessDrive(object item)
        {
            if (item != null)
            {
                if (item.GetType() == typeof(DriveInformation))
                {
                    string path = ((DriveInformation)item).RootDirectory;
                    string videoDir = Path.Combine(path, "VIDEO_TS");

                    List<string> scanPath = new List<string> { Directory.Exists(videoDir) ? videoDir : path };

                    this.StartScan(scanPath, this.TitleSpecificScan);
                }
                else if (item.GetType() == typeof(SourceMenuItem))
                {
                    DriveInformation driveInfo = ((SourceMenuItem)item).Tag as DriveInformation;
                    if (driveInfo != null)
                    {
                        string path = driveInfo.RootDirectory;
                        string videoDir = Path.Combine(driveInfo.RootDirectory, "VIDEO_TS");

                        List<string> scanPath = new List<string> { Directory.Exists(videoDir) ? videoDir : path };

                        this.StartScan(scanPath, this.TitleSpecificScan);
                    }

                    this.ShowSourceSelection = false;
                }
            }
        }

        public bool CanRecoverQueue()
        {
            return this.QueueRecoveryArchivesExist;
        }

        public void RecoverQueue()
        {
            QueueRecoveryHelper.ResetArchives();
            bool result = QueueRecoveryHelper.RecoverQueue(this.queueProcessor, this.errorService, StartupOptions.AutoRestartQueue, StartupOptions.QueueRecoveryIds);
            this.QueueRecoveryArchivesExist = !result && QueueRecoveryHelper.ArchivesExist();
            this.NotifyOfPropertyChange(() => this.QueueRecoveryArchivesExist);
        }

        public void FlipAddAllToQueue()
        {
            bool value = !this.ShowAddAllToQueue;
            this.userSettingService.SetUserSetting(UserSettingConstants.ShowAddAllToQueue, value);

            var optionsViewModel = IoCHelper.Get<IOptionsViewModel>();
            optionsViewModel.UpdateSettings();
        }

        public void FlipAddSelectionToQueue()
        {
            bool value = !this.ShowAddSelectionToQueue;
            this.userSettingService.SetUserSetting(UserSettingConstants.ShowAddSelectionToQueue, value);

            var optionsViewModel = IoCHelper.Get<IOptionsViewModel>();
            optionsViewModel.UpdateSettings();
        }

        public void ReGenerateAutoName()
        {
            if (this.ScannedSource != null)
            {
                this.Destination = AutoNameHelper.AutoName(this.CurrentTask, this.SelectedTitle?.DisplaySourceName, this.SelectedTitle?.DisplaySourceName, this.selectedPreset);
            }
        }

        /* Private Methods*/

        private void QueueEditAction(bool successful, Source scannedSource)
        {
            /* TODO Fix this. */
            ThreadHelper.OnUIThread(() =>
            {
                if (this.queueEditTask != null && !string.IsNullOrEmpty(this.queueEditTask.SelectedPresetKey) && this.selectedPreset.Name != this.queueEditTask.SelectedPresetKey)
                {
                    Preset foundPreset = this.presetService.GetPreset(this.queueEditTask.SelectedPresetKey);
                    if (foundPreset != null)
                    {
                        this.selectedPreset = foundPreset;
                        this.NotifyOfPropertyChange(() => this.SelectedPreset);
                    }
                }

                // Copy all the Scan data into the UI
                this.ScannedSource = new Source(scannedSource);

                // Select the Users Title
                this.SelectedTitle = this.ScannedSource.Titles.FirstOrDefault();
                this.CurrentTask = new EncodeTask(this.queueEditTask.Task);
                this.NotifyOfPropertyChange(() => this.CurrentTask);
                this.HasSource = true;
             
                // Update the Main Window
                this.NotifyOfPropertyChange(() => this.Destination);
                this.SelectedAngle = this.CurrentTask.Angle;
                long start = this.CurrentTask.StartPoint;
                long end = this.CurrentTask.EndPoint;
                this.SelectedPointToPoint = this.CurrentTask.PointToPointMode; // Force reset.
                this.SelectedStartPoint = start;
                this.SelectedEndPoint = end;

                // Update the Tab Controls
                this.SummaryViewModel.UpdateTask(this.CurrentTask);
                this.PictureSettingsViewModel.UpdateTask(this.CurrentTask);
                this.VideoViewModel.UpdateTask(this.CurrentTask);
                this.FiltersViewModel.UpdateTask(this.CurrentTask);
                this.AudioViewModel.UpdateTask(this.CurrentTask);
                this.SubtitleViewModel.UpdateTask(this.CurrentTask);
                this.ChaptersViewModel.UpdateTask(this.CurrentTask);
                this.MetaDataViewModel.UpdateTask(this.CurrentTask);
              
                // Cleanup
                this.ShowStatusWindow = false;
                this.SourceLabel = this.SelectedTitle?.DisplaySourceName ?? this.SelectedTitle?.DisplaySourceName;
                this.StatusLabel = Resources.Main_ScanCompleted;
            });
        }

        private void SetupTabs()
        {
            // Setup the Tabs
            if (this.selectedTitle != null)
            {
                this.isSettingPreset = true;
                this.PictureSettingsViewModel.SetSource(this.ScannedSource, this.SelectedTitle, this.selectedPreset, this.CurrentTask);
                this.VideoViewModel.SetSource(this.ScannedSource, this.SelectedTitle, this.selectedPreset, this.CurrentTask);
                this.FiltersViewModel.SetSource(this.ScannedSource, this.SelectedTitle, this.selectedPreset, this.CurrentTask);
                this.AudioViewModel.SetSource(this.ScannedSource, this.SelectedTitle, this.selectedPreset, this.CurrentTask);
                this.SubtitleViewModel.SetSource(this.ScannedSource, this.SelectedTitle, this.selectedPreset, this.CurrentTask);
                this.ChaptersViewModel.SetSource(this.ScannedSource, this.SelectedTitle, this.selectedPreset, this.CurrentTask);
                this.MetaDataViewModel.SetSource(this.ScannedSource, this.SelectedTitle, this.selectedPreset, this.CurrentTask);
                this.SummaryViewModel.SetSource(this.ScannedSource, this.SelectedTitle, this.selectedPreset, this.CurrentTask);
                this.isSettingPreset = false;

                TriggerAutonameChange(ChangedOption.Dimensions);
            }
        }

        private void TabStatusChanged(object sender, TabStatusEventArgs e)
        {
            if (this.isSettingPreset)
            {
                return; // Don't process this when we are setting up.
            }

            // Update Preview if needed
            if (e != null && e.TabKey != null && e.TabKey.Equals(TabStatusEventType.FilterType) && this.StaticPreviewViewModel.IsOpen)
            {
                delayedPreviewprocessor.PerformTask(() => this.StaticPreviewViewModel.UpdatePreviewFrame(this.SelectedTitle, this.CurrentTask, this.ScannedSource), 1000);
            }

            // Preset Check
            bool matchesPreset = this.PictureSettingsViewModel.MatchesPreset(this.selectedPreset);

            if (!this.SummaryViewModel.MatchesPreset(this.selectedPreset))
            {
                matchesPreset = false;
            }

            if (!this.PictureSettingsViewModel.MatchesPreset(this.selectedPreset))
            {
                matchesPreset = false;
            }

            if (!this.VideoViewModel.MatchesPreset(this.selectedPreset))
            {
                matchesPreset = false;
            }

            if (!this.FiltersViewModel.MatchesPreset(this.selectedPreset))
            {
                matchesPreset = false;
            }

            if (!this.AudioViewModel.MatchesPreset(this.selectedPreset))
            {
                matchesPreset = false;
            }

            if (!this.SubtitleViewModel.MatchesPreset(this.selectedPreset))
            {
                matchesPreset = false;
            }

            if (!this.ChaptersViewModel.MatchesPreset(this.selectedPreset))
            {
                matchesPreset = false;
            }

            if (!this.MetaDataViewModel.MatchesPreset(this.selectedPreset))
            {
                matchesPreset = false;
            }

            this.IsModifiedPreset = !matchesPreset;

            if (e != null)
            {
                this.TriggerAutonameChange(e.ChangedOption);
            }
        }

        private void TriggerAutonameChange(ChangedOption option)
        {
            if (!this.userSettingService.GetUserSetting<bool>(UserSettingConstants.AutoNaming))
            {
                return;
            }

            string autonameFormat = this.userSettingService.GetUserSetting<string>(UserSettingConstants.AutoNameFormat);
            if (autonameFormat == null)
            {
                return;
            }

            //AutoNameFormat Empty will generate chapter name
            if (option != ChangedOption.None && option != ChangedOption.Chapters) 
            {
                if (string.IsNullOrEmpty(autonameFormat))
                {
                    return;
                }
            }

            bool is_execute = false;
            
            if ((autonameFormat.Contains(Constants.Source) || autonameFormat.Contains(Constants.SourcePath) || autonameFormat.Contains(Constants.SourceFolderName) || autonameFormat.Contains(Constants.Title)) && option == ChangedOption.Source)
            {
                is_execute = true;
            }

            if (option == ChangedOption.Chapters 
                && this.SelectedPointToPoint == PointToPointMode.Chapters
                && this.SelectedTitle.SourcePath != null
                && autonameFormat.Contains(Constants.Chapters)
               )
            {
                is_execute = true;
            }

            if ((autonameFormat.Contains(Constants.Encoder) || autonameFormat.Contains(Constants.Codec) || autonameFormat.Contains(Constants.EncoderBitDepth)  || autonameFormat.Contains(Constants.EncoderDisplay) ) 
                && option == ChangedOption.Encoder)
            {
                is_execute = true;
            }

            if ((autonameFormat.Contains(Constants.QualityBitrate) || autonameFormat.Contains(Constants.QualityType)) && (option == ChangedOption.Bitrate || option == ChangedOption.Quality))
            {
                is_execute = true;
            }

            if (autonameFormat.Contains(Constants.EncoderBitDepth) && option == ChangedOption.Encoder)
            {
                is_execute = true;
            }

            if (autonameFormat.Contains(Constants.Preset) && option == ChangedOption.Preset)
            {
                is_execute = true;
            }

            if ((autonameFormat.Contains(Constants.StorageWidth) || autonameFormat.Contains(Constants.StorageHeight)) && option == ChangedOption.Dimensions)
            {
                is_execute = true;
            }

            if (autonameFormat.Contains(Constants.Angle))
            {
                is_execute = true;
            }

            if (!is_execute && option == ChangedOption.Preset)
            {
                is_execute = true;
            }

            if (is_execute)
            {
                this.Destination = AutoNameHelper.AutoName(this.CurrentTask, this.SelectedTitle?.DisplaySourceName, this.SelectedTitle?.DisplaySourceName, this.selectedPreset);
            }
        }

        private string DurationCalculation()
        {
            if (this.selectedTitle == null)
            {
                return "--:--:--";
            }

            double startEndDuration = this.SelectedEndPoint - this.SelectedStartPoint;
            TimeSpan output;

            switch (this.SelectedPointToPoint)
            {
                case PointToPointMode.Chapters:
                    output = this.SelectedTitle.CalculateDuration(this.SelectedStartPoint, this.SelectedEndPoint);
                    return string.Format("{0:00}:{1:00}:{2:00}", output.Hours, output.Minutes, output.Seconds);
                case PointToPointMode.Seconds:
                    output = TimeSpan.FromSeconds(startEndDuration);
                    return string.Format("{0:00}:{1:00}:{2:00}", output.Hours, output.Minutes, output.Seconds);
                case PointToPointMode.Frames:
                    startEndDuration = startEndDuration / selectedTitle.Fps;
                    output = TimeSpan.FromSeconds(Math.Round(startEndDuration, 2));
                    return string.Format("{0:00}:{1:00}:{2:00}", output.Hours, output.Minutes, output.Seconds);
            }

            return "--:--:--";
        }

        private void HandleUpdateCheckResults(UpdateCheckInformation information)
        {
            if (information.NewVersionAvailable)
            {
                this.UpdateAvailable = true;
                this.ProgramStatusLabel = Resources.Main_NewUpdate;
            }
            else if (HandBrakeVersionHelper.IsNightly())
            { 
                int ageLimit = this.userSettingService.GetUserSetting<int>(UserSettingConstants.DaysBetweenUpdateCheck);
                if (HandBrakeVersionHelper.NightlyBuildAge() > ageLimit)
                {
                    // Any nightly build older than 30 days is considered old. Encourage users to update.
                    this.UpdateAvailable = false;
                    this.IsOldNightly = true;
                    this.NotifyOfPropertyChange(() => this.IsOldNightly);
                }
            }
        }

        private void OpenAlertWindow(string header, string message)
        {
            this.ShowAlertWindow = true;
            this.AlertWindowHeader = header;
            this.AlertWindowText = message;
        }

        private void SelectDefaultPreset()
        {
            if (this.presetService.GetDefaultPreset() != null)
            {
                this.SelectedPreset = this.presetService.GetDefaultPreset();
            }
        }

        /* Event Handlers */

        private void ScanStatusChanged(object sender, ScanProgressEventArgs e)
        {
            this.SourceLabel = string.Format(Resources.Main_ScanningTitleXOfY, e.CurrentTitle, e.Titles, e.Percentage);
            this.StatusLabel = string.Format(Resources.Main_ScanningTitleXOfY, e.CurrentTitle, e.Titles, e.Percentage);
            this.ProgressAmount = (double)e.Percentage;
        }

        private void ScanCompleted(object sender, ScanCompletedEventArgs e)
        {
            this.ShowStatusWindow = false;

            if (e.ScannedSource != null && !e.Cancelled)
            {
                this.ScannedSource = new Source(e.ScannedSource);
            }
            else
            {
                this.ScannedSource = null;
            }

            ThreadHelper.OnUIThread(() =>
            {
                if (e.Successful && this.ScannedSource != null)
                {
                    this.NotifyOfPropertyChange(() => this.ScannedSource);
                    this.NotifyOfPropertyChange(() => this.ScannedSource.Titles);
                    this.HasSource = true;
                    this.SelectedTitle = this.ScannedSource.Titles.FirstOrDefault(t => t.MainTitle) ?? this.ScannedSource.Titles.FirstOrDefault();
                }
                else if (!e.Cancelled)
                {
                    this.OpenAlertWindow(Resources.Main_ScanNoTitlesFound, Resources.Main_ScanNoTitlesFoundMessage);
                }

                if (e.Successful)
                {
                    this.SourceLabel = this.SelectedTitle?.DisplaySourceName;
                    this.StatusLabel = Resources.Main_ScanCompleted;
                }
                else if (e.Cancelled)
                {
                    this.SourceLabel = Resources.Main_ScanCancelled;
                    this.StatusLabel = Resources.Main_ScanCancelled;
                }
                else
                {
                    this.SourceLabel = Resources.Main_ScanFailed_CheckLog;
                    this.StatusLabel = Resources.Main_ScanFailed_CheckLog;
                }
            });
        }

        private void ScanStared(object sender, EventArgs e)
        {
            ThreadHelper.OnUIThread(
                () =>
                {
                    this.StatusLabel = Resources.Main_ScanningPleaseWait;
                    this.ShowStatusWindow = true;
                });
        }

        private void QueueProcessorJobProcessingStarted(object sender, QueueProgressEventArgs e)
        {
            ThreadHelper.OnUIThread(
               () =>
               {
                   this.ProgramStatusLabel = Resources.Main_PreparingToEncode;
                   this.NotifyOfPropertyChange(() => this.IsEncoding);
               });
        }

        private void QueueCompleted(object sender, EventArgs e)
        {
            this.NotifyOfPropertyChange(() => this.IsEncoding);
            this.NotifyOfPropertyChange(() => this.StartLabel);

            ThreadHelper.OnUIThread(
                () =>
                {
                    string errorDesc = string.Empty;
                    if (this.queueProcessor.ErrorCount > 0)
                    {
                        errorDesc += string.Format(Resources.Main_QueueFinishedErrors, this.queueProcessor.ErrorCount);
                    }

                    this.ProgramStatusLabel = Resources.Main_QueueFinished + errorDesc;
                    this.WindowTitle = Resources.HandBrake_Title;
                    this.notifyIconService.SetTooltip(this.WindowTitle);
                    this.windowsTaskbar.SetNoProgress();
                });
        }

        private void QueueChanged(object sender, EventArgs e)
        {
            ThreadHelper.OnUIThread(
              () =>
              {
                  if (!this.queueProcessor.IsEncoding && !this.queueProcessor.IsProcessing)
                  {
                      this.ProgramStatusLabel = string.Format(Resources.Main_XEncodesPending, this.queueProcessor.Count);
                  }

                  this.NotifyOfPropertyChange(() => this.IsQueueCountVisible);
                  this.NotifyOfPropertyChange(() => this.QueueCount);
                  this.NotifyOfPropertyChange(() => this.StartLabel);
                  this.NotifyOfPropertyChange(() => this.IsEncoding);

                  if (!this.queueProcessor.IsEncoding && this.IsMultiProcess)
                  {
                      this.IsMultiProcess = false;
                      this.NotifyOfPropertyChange(() => this.IsMultiProcess);
                  }
              });
        }

        private void QueueProcessor_QueuePaused(object sender, EventArgs e)
        {
            ThreadHelper.OnUIThread(
                () =>
                {
                    this.ProgramStatusLabel = Resources.Main_QueuePaused;
                    this.NotifyOfPropertyChange(() => this.IsQueueCountVisible);
                    this.NotifyOfPropertyChange(() => this.QueueCount);
                    this.NotifyOfPropertyChange(() => this.StartLabel);
                    this.NotifyOfPropertyChange(() => this.IsEncoding);

                    this.windowsTaskbar.SetPaused();
                });
        }
        
        private void QueueProcessor_QueueJobStatusChanged(object sender, EventArgs e)
        {
            List<QueueProgressStatus> queueJobStatuses = this.queueProcessor.GetQueueProgressStatus();
            string jobsPending = "   " + string.Format(Resources.Main_JobsPending_addon, this.queueProcessor.Count);


            ThreadHelper.OnUIThread(() =>
            {
                if (queueJobStatuses.Count == 0)
                {
                    this.ProgramStatusLabel = Resources.Main_QueueFinished;
                    this.NotifyOfPropertyChange(() => this.IsEncoding);
                    this.WindowTitle = Resources.HandBrake_Title;
                    this.notifyIconService.SetTooltip(this.WindowTitle);

                    this.IsMultiProcess = false;
                    this.NotifyOfPropertyChange(() => this.IsMultiProcess);
                    this.windowsTaskbar.SetNoProgress();
                }
            });

            if (this.queueProcessor.IsPaused)
            {
                return;
            }

            ThreadHelper.OnUIThread(
                () =>
                {
                    if (queueJobStatuses.Count == 1)
                    {
                        QueueProgressStatus status = queueJobStatuses.First();
                        this.ProgramStatusLabel = status.JobStatus.Replace(Environment.NewLine, " ") + jobsPending;

                        int percent;
                        int.TryParse(Math.Round(status.ProgressValue).ToString(CultureInfo.InvariantCulture), out percent);

                        if (this.lastEncodePercentage != percent)
                        {
                            this.windowsTaskbar.SetTaskBarProgress(percent);
                        }

                        this.lastEncodePercentage = percent;
                        this.ProgressPercentage = percent;
                        this.NotifyOfPropertyChange(() => this.ProgressPercentage);

                        if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.ShowStatusInTitleBar))
                        {
                            this.WindowTitle = string.Format(Resources.WindowTitleStatus, Resources.HandBrake_Title, String.Format("{0:##0.0}", Math.Round(status.ProgressValue, 1)), status.Task, status.TaskCount);
                            this.notifyIconService.SetTooltip(string.Format(Resources.TaskTrayStatusTitle, Resources.HandBrake_Title));
                        }

                        this.IsMultiProcess = false;
                        this.NotifyOfPropertyChange(() => this.IsMultiProcess);
                    }
                    else if (queueJobStatuses.Count > 1)
                    {
                        this.windowsTaskbar.SetNoProgress();
                        this.ProgramStatusLabel = string.Format(Resources.Main_QueueMultiJobStatus, this.queueProcessor.CompletedCount, Environment.NewLine, queueJobStatuses.Count, this.queueProcessor.Count);

                        this.WindowTitle = Resources.HandBrake_Title; // Percentage is not available when multiple jobs are running. 
                        this.windowsTaskbar.SetTaskBarProgress(0);

                        this.notifyIconService.SetTooltip(string.Format(Resources.TaskTrayStatusManyTitle, Resources.HandBrake_Title, queueJobStatuses.Count));
                        this.IsMultiProcess = true;
                        this.NotifyOfPropertyChange(() => this.IsMultiProcess);
                    }
                }
                );
        }

        private void UserSettingServiceSettingChanged(object sender, SettingChangedEventArgs e)
        {
            switch (e.Key)
            {
                case UserSettingConstants.WhenCompleteAction:
                    this.QueueViewModel.WhenDone(this.userSettingService.GetUserSetting<int>(UserSettingConstants.WhenCompleteAction), false);
                    break;

                case UserSettingConstants.ShowAddAllToQueue:
                case UserSettingConstants.ShowAddSelectionToQueue:
                    this.NotifyOfPropertyChange(() => this.ShowAddAllToQueue);
                    this.NotifyOfPropertyChange(() => this.ShowAddSelectionToQueue);
                    this.NotifyOfPropertyChange(() => this.ShowAddAllMenuName);
                    this.NotifyOfPropertyChange(() => this.ShowAddSelectionMenuName);
                    break;

                case UserSettingConstants.PresetUiType:
                    PresetUiType uiType = this.userSettingService.GetUserSetting<PresetUiType>(UserSettingConstants.PresetUiType);
                    this.IsMenuStylePresetDisplayed = uiType == PresetUiType.Menu;
                    this.NotifyOfPropertyChange(() => IsMenuStylePresetDisplayed);
                    break;
            }
        }

        private void SummaryViewModel_OutputFormatChanged(object sender, OutputFormatChangedEventArgs e)
        {
            if (!string.IsNullOrEmpty(e.Extension))
            {
                this.Destination = Path.ChangeExtension(this.Destination, e.Extension);
            }

            this.VideoViewModel.RefreshTask();
            this.AudioViewModel.RefreshTask(this.CurrentTask.OutputFormat);
            this.SubtitleViewModel.RefreshTask();
        }
    }
}