using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using LiveCharts;
using LiveCharts.Defaults;
using LiveCharts.Wpf;
using log4net;
using MahApps.Metro;
using Microsoft.Win32;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Data.Entity;
using System.Data.SQLite;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Data.SQLite.Linq;
using System.Linq.Expressions;
using Vulnerator.Helper;
using Vulnerator.Model.BusinessLogic;
using Vulnerator.Model.DataAccess;
using Vulnerator.Model.Entity;
using Vulnerator.Model.Object;
using Vulnerator.ViewModel.ViewModelHelper;

namespace Vulnerator.ViewModel
{
    public class VulnerabilityViewModel : ViewModelBase
    {
        public string[] Labels { get; set; }
        public SeriesCollection ColumnSeriesCollection { get; set; }
        public SeriesCollection AreaSerisCollection { get; set; }
        public List<int> Values { get; set; }
        public Func<double, string> Formatter { get; set; }
        public Func<double, string> DateFormatter { get; set; }
        OpenFileDialog openFileDialog;
        public static Stopwatch stopWatch = new Stopwatch();
        public static Stopwatch fileStopWatch = new Stopwatch();
        private DatabaseInterface databaseInterface = new DatabaseInterface();
        private bool keepConnectionAlive = true;

        private ObservableCollection<UniqueFinding> _uniqueFindings;

        public ObservableCollection<UniqueFinding> UniqueFindings
        {
            get => _uniqueFindings;
            set
            {
                if (_uniqueFindings != value)
                {
                    _uniqueFindings = value;
                    RaisePropertyChanged("UniqueFindings");
                }
            }
        }

        private ObservableCollection<GroupMitigationOrConditionVulnerability> _projectMitigations;

        public ObservableCollection<GroupMitigationOrConditionVulnerability> ProjectMitigations
        {
            get => _projectMitigations;
            set
            {
                if (_projectMitigations != value)
                {
                    _projectMitigations = value;
                    RaisePropertyChanged("ProjectMitigations");
                }
            }
        }

        private List<NIST_ControlCCI> _nistControlsCcis;

        public List<NIST_ControlCCI> NistControlsCcis
        {
            get => _nistControlsCcis;
            set
            {
                if (_nistControlsCcis != value)
                {
                    _nistControlsCcis = value;
                    RaisePropertyChanged("NistControlsCcis");
                }
            }
        }

        private List<NIST_ControlCCI> _bulkNistControlsCcis;

        public List<NIST_ControlCCI> BulkNistControlsCcis
        {
            get => _bulkNistControlsCcis;
            set
            {
                if (_bulkNistControlsCcis != value)
                {
                    _bulkNistControlsCcis = value;
                    RaisePropertyChanged("BulkNistControlsCcis");
                }
            }
        }

        private AsyncObservableCollection<string> _supportedFileTypes;

        public AsyncObservableCollection<string> SupportedFileTypes
        {
            get => _supportedFileTypes;
            set
            {
                if (_supportedFileTypes != value)
                {
                    _supportedFileTypes = value;
                    RaisePropertyChanged("SupportedFileTypes");
                }
            }
        }

        private AsyncObservableCollection<Model.Object.File> _ingestedFiles;

        public AsyncObservableCollection<Model.Object.File> IngestedFiles
        {
            get => _ingestedFiles;
            set
            {
                if (_ingestedFiles != value)
                {
                    _ingestedFiles = value;
                    RaisePropertyChanged("IngestedFiles");
                }
            }
        }

        private List<RequiredReport> _reports;

        public List<RequiredReport> Reports
        {
            get => _reports;
            set
            {
                if (_reports != value)
                {
                    _reports = value;
                    RaisePropertyChanged("Reports");
                }
            }
        }

        private object _selectedMitigationsOrCondition { get; set; }

        public object SelectedMitigationsOrCondition
        {
            get => _selectedMitigationsOrCondition;
            set
            {
                if (_selectedMitigationsOrCondition != value)
                {
                    _selectedMitigationsOrCondition = value;
                    RaisePropertyChanged("SelectedMitigationsOrCondition");
                    SetEditableMitigationOrCondition();
                }
            }
        }

        private GroupMitigationOrConditionVulnerability _editableMitigationsOrCondition { get; set; }

        public GroupMitigationOrConditionVulnerability EditableMitigationsOrCondition
        {
            get => _editableMitigationsOrCondition;
            set
            {
                if (_editableMitigationsOrCondition != value)
                {
                    _editableMitigationsOrCondition = value;
                    RaisePropertyChanged("EditableMitigationsOrCondition");
                    SetEditableMitigationOrCondition();
                }
            }
        }

        private object _selectedUniqueFinding { get; set; }

        public object SelectedUniqueFinding
        {
            get => _selectedUniqueFinding;
            set
            {
                if (_selectedUniqueFinding != value)
                {
                    _selectedUniqueFinding = value;
                    RaisePropertyChanged("SelectedUniqueFinding");
                    SetEditableUniqueFinding();
                }
            }
        }

        private UniqueFinding _editableUniqueFinding { get; set; }

        public UniqueFinding EditableUniqueFinding
        {
            get => _editableUniqueFinding;
            set
            {
                if (_editableUniqueFinding != value)
                {
                    _editableUniqueFinding = value;
                    RaisePropertyChanged("EditableUniqueFinding");
                }
            }
        }

        private ObservableCollection<Vulnerability> _vulnerabilities { get; set; }

        public ObservableCollection<Vulnerability> Vulnerabilities
        {
            get => _vulnerabilities;
            set
            {
                if (_vulnerabilities != value)
                {
                    _vulnerabilities = value;
                    RaisePropertyChanged("Vulnerabilities");
                }
            }
        }

        private Vulnerability _selectedVulnerability { get; set; }

        public Vulnerability SelectedVulnerability
        {
            get => _selectedVulnerability;
            set
            {
                if (_selectedVulnerability != value)
                {
                    _selectedVulnerability = value;
                    RaisePropertyChanged("SelectedVulnerability");
                }
            }
        }

        private List<string> _mitigationStatuses { get; set; }

        public List<string> MitigationStatuses
        {
            get => _mitigationStatuses;
            set
            {
                if (_mitigationStatuses != value)
                {
                    _mitigationStatuses = value;
                    RaisePropertyChanged("MitigationStatuses");
                }
            }
        }

        private List<string> _rmfValues { get; set; }

        public List<string> RmfValues
        {
            get => _rmfValues;
            set
            {
                if (_rmfValues != value)
                {
                    _rmfValues = value;
                    RaisePropertyChanged("RmfValues");
                }
            }
        }

        private List<Group> _groups { get; set; }

        public List<Group> Groups
        {
            get => _groups;
            set
            {
                if (_groups != value)
                {
                    _groups = value;
                    RaisePropertyChanged("Groups");
                }
            }
        }

        private Group _mitigationGroup { get; set; }

        public Group MitigationGroup
        {
            get => _mitigationGroup;
            set
            {
                if (_mitigationGroup != value)
                {
                    _mitigationGroup = value;
                    RaisePropertyChanged("MitigationGroup");
                }
            }
        }

        private Group _ingestionGroup { get; set; }

        public Group IngestionGroup
        {
            get => _ingestionGroup;
            set
            {
                if (_ingestionGroup != value)
                {
                    _ingestionGroup = value;
                    RaisePropertyChanged("IngestionGroup");
                }
            }
        }


        private string _impactDescription { get; set; }

        public string ImpactDescription
        {
            get => _impactDescription;
            set
            {
                if (_impactDescription != value)
                {
                    _impactDescription = value;
                    RaisePropertyChanged("ImpactDescription");
                }
            }
        }

        private string _predisposingConditions { get; set; }

        public string PredisposingConditions
        {
            get => _predisposingConditions;
            set
            {
                if (_predisposingConditions != value)
                {
                    _predisposingConditions = value;
                    RaisePropertyChanged("PredisposingConditions");
                }
            }
        }

        private string _technicalMitigations { get; set; }

        public string TechnicalMitigations
        {
            get => _technicalMitigations;
            set
            {
                if (_technicalMitigations != value)
                {
                    _technicalMitigations = value;
                    RaisePropertyChanged("TechnicalMitigations");
                }
            }
        }

        private string _proposedMitigations { get; set; }

        public string ProposedMitigations
        {
            get => _proposedMitigations;
            set
            {
                if (_proposedMitigations != value)
                {
                    _proposedMitigations = value;
                    RaisePropertyChanged("ProposedMitigations");
                }
            }
        }

        private string _mitigatedStatus { get; set; }

        public string MitigatedStatus
        {
            get => _mitigatedStatus;
            set
            {
                if (_mitigatedStatus != value)
                {
                    _mitigatedStatus = value;
                    RaisePropertyChanged("MitigatedStatus");
                }
            }
        }

        private string _threatDescription { get; set; }

        public string ThreatDescription
        {
            get => _threatDescription;
            set
            {
                if (_threatDescription != value)
                {
                    _threatDescription = value;
                    RaisePropertyChanged("ThreatDescription");
                }
            }
        }

        private string _threatRelevance { get; set; }

        public string ThreatRelevance
        {
            get => _threatRelevance;
            set
            {
                if (_threatRelevance != value)
                {
                    _threatRelevance = value;
                    RaisePropertyChanged("ThreatRelevance");
                }
            }
        }

        private string _severityPervasiveness { get; set; }

        public string SeverityPervasiveness
        {
            get => _severityPervasiveness;
            set
            {
                if (_severityPervasiveness != value)
                {
                    _severityPervasiveness = value;
                    RaisePropertyChanged("SeverityPervasiveness");
                }
            }
        }

        private string _likelihood { get; set; }

        public string Likelihood
        {
            get => _likelihood;
            set
            {
                if (_likelihood != value)
                {
                    _likelihood = value;
                    RaisePropertyChanged("Likelihood");
                }
            }
        }

        private string _impact { get; set; }

        public string Impact
        {
            get => _impact;
            set
            {
                if (_impact != value)
                {
                    _impact = value;
                    RaisePropertyChanged("Impact");
                }
            }
        }

        private string _risk { get; set; }

        public string Risk
        {
            get => _risk;
            set
            {
                if (_risk != value)
                {
                    _risk = value;
                    RaisePropertyChanged("Risk");
                }
            }
        }

        private string _residualRisk { get; set; }

        public string ResidualRisk
        {
            get => _residualRisk;
            set
            {
                if (_residualRisk != value)
                {
                    _residualRisk = value;
                    RaisePropertyChanged("ResidualRisk");
                }
            }
        }

        private string _residualRiskAfterProposed { get; set; }

        public string ResidualRiskAfterProposed
        {
            get => _residualRiskAfterProposed;
            set
            {
                if (_residualRiskAfterProposed != value)
                {
                    _residualRiskAfterProposed = value;
                    RaisePropertyChanged("ResidualRiskAfterProposed");
                }
            }
        }

        private string _estimatedCompletionDate { get; set; }

        public string EstimatedCompletionDate
        {
            get => _estimatedCompletionDate;
            set
            {
                if (_estimatedCompletionDate != value)
                {
                    _estimatedCompletionDate = value;
                    RaisePropertyChanged("EstimatedCompletionDate");
                }
            }
        }

        private bool _bulkProcessExpanded { get; set; }

        public bool BulkProcessExpanded
        {
            get => _bulkProcessExpanded;
            set
            {
                if (_bulkProcessExpanded != value)
                {
                    _bulkProcessExpanded = value;
                    RaisePropertyChanged("BulkProcessExpanded");
                }
            }
        }

        public VulnerabilityViewModel()
        {
            try
            {
                LogWriter.LogStatusUpdate("Begin instantiation of VulnerabilityViewModel.");
                SupportedFileTypes = new AsyncObservableCollection<string>();
                IngestedFiles = new AsyncObservableCollection<Model.Object.File>();
                PopulateGui();
                if (UniqueFindings != null && UniqueFindings.Count > 0)
                {
                    SetTrendSeries();
                }

                Messenger.Default.Register<NotificationMessage<string>>(this, MessengerToken.ModelUpdated,
                    (msg) => HandleModelUpdate(msg.Notification));
                LogWriter.LogStatusUpdate("VulnerabilityViewModel instantiated successfully.");
            }
            catch (Exception exception)
            {
                string error = "Unable to instantiate VulnerabilityViewModel.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void HandleModelUpdate(string modelUpdated)
        {
            try
            {
                if (modelUpdated.Equals("AllModels") || modelUpdated.Equals("VulnerabilityModel"))
                {
                    PopulateGui();
                }
            }
            catch (Exception exception)
            {
                string error = "Unable to update VulnerabilityViewModel.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void PopulateGui()
        {
            try
            {
                MitigationStatuses = new List<string>() {"Ongoing", "Completed", "Not Reviewed", "Not Applicable"};
                RmfValues = new List<string>() {"Very High", "High", "Moderate", "Low", "Very Low"};
                using (DatabaseContext databaseContext = new DatabaseContext())
                {
                    PopulateVulnerabilityList(databaseContext);
                    PopulateSupportedFileTypes(databaseContext);
                    ProjectMitigations = databaseContext.GroupsMitigationsOrConditionsVulnerabilities
                        .Include(m => m.Group)
                        .Include(m => m.Vulnerability)
                        .Include(m => m.MitigationOrCondition)
                        .AsNoTracking()
                        .ToObservableCollection();
                    Vulnerabilities = databaseContext.Vulnerabilities
                        .Include(v => v.CCIs.Select(c => c.NIST_ControlsCCIs))
                        .Include(v => v.VulnerabilitySources)
                        .AsNoTracking()
                        .ToObservableCollection();
                    NistControlsCcis = databaseContext.NIST_ControlsCCIs
                        .Include(n => n.CCI)
                        .AsNoTracking().ToList();
                    BulkNistControlsCcis = databaseContext.NIST_ControlsCCIs
                        .Include(n => n.CCI)
                        .AsNoTracking().ToList();
                    Groups = databaseContext.Groups.AsNoTracking().ToList();
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogErrorWithDebug("Unable to populate VulnerabilityViewModel GUI.", exception);
            }
        }

        private void PopulateSupportedFileTypes(DatabaseContext databaseContext)
        {
            try
            {
                if (SupportedFileTypes.Count != 0) return;
                foreach (FindingType findingType in databaseContext.FindingTypes.AsNoTracking()
                    .OrderBy(x => x.Finding_Type).ToList())
                {
                    SupportedFileTypes.Add("Import " + findingType.Finding_Type);
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to populate VulnerabilityViewModel supported file types list.");
                throw exception;
            }
        }

        private void PopulateVulnerabilityList(DatabaseContext databaseContext)
        {
            try
            {
                UniqueFindings = databaseContext.UniqueFindings
                    .Include(u => u.Vulnerability)
                    .Include(u => u.Hardware)
                    .Include(u => u.Software)
                    .Include(u => u.FindingType)
                    .Include(u => u.UniqueFindingSourceFile)
                    .Include(u => u.MitigationOrCondition)
                    .AsNoTracking()
                    .ToObservableCollection();
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to populate VulnerabilityViewModel unique findings list.");
                throw exception;
            }
        }

        private void SetTrendSeries()
        {
            ColumnSeriesCollection = new SeriesCollection
            {
                new ColumnSeries
                {
                    Title = "ACAS",
                    Values = new ChartValues<double>
                    {
                        UniqueFindings.Count(x =>
                            x.Vulnerability.PrimaryRawRiskIndicator == "I" && x.FindingType.Finding_Type.Equals("ACAS")),
                        UniqueFindings.Count(x =>
                            x.Vulnerability.PrimaryRawRiskIndicator == "II" && x.FindingType.Finding_Type.Equals("ACAS")),
                        UniqueFindings.Count(x =>
                            x.Vulnerability.PrimaryRawRiskIndicator == "III" && x.FindingType.Finding_Type.Equals("ACAS"))
                    }
                }
            };
            Formatter = value => value.ToString();
        }

        public RelayCommand<object> IngestFilesCommand
        {
            get { return new RelayCommand<object>((p) => IngestFiles(p)); }
        }

        private void IngestFiles(object parameter)
        {
            try
            {
                openFileDialog = null;
                openFileDialog = new OpenFileDialog
                {
                    Multiselect = true,
                    CheckFileExists = true
                };
                switch (parameter.ToString())
                {
                    case "Import ACAS":
                    {
                        IngestAcas();
                        break;
                    }
                    case "Import CKL":
                    {
                        IngestCkl();
                        break;
                    }
                    case "Import Fortify":
                    {
                        IngestFortify();
                        break;
                    }
                    case "Import WASSP":
                    {
                        IngestWassp();
                        break;
                    }
                    case "Import XCCDF":
                    {
                        IngestXccdf();
                        break;
                    }
                }

                openFileDialog = null;
            }
            catch (Exception exception)
            {
                string error = "Unable to ingest files.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void IngestAcas()
        {
            try
            {
                openFileDialog.Filter = "ACAS Files (*.csv;*.nessus)|*.csv;*.nessus";
                openFileDialog.Title = "Please select ACAS file(s)";
                openFileDialog.ShowDialog();
                if (openFileDialog.FileNames.Length > 0)
                {
                    for (int i = 0; i < openFileDialog.FileNames.Length; i++)
                    {
                        string filePath = openFileDialog.FileNames[i];
                        string fileName = Path.GetFileNameWithoutExtension(filePath);
                        if (Path.GetExtension(filePath).ToLower().Equals(".nessus"))
                        {
                            IngestedFiles.Add(new Model.Object.File(fileName, "ACAS - Nessus", "Ready", filePath,
                                "True"));
                        }
                        else
                        {
                            IngestedFiles.Add(new Model.Object.File(fileName, "ACAS - CSV", "Ready", filePath, "True"));
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to retrieve ACAS file(s).");
                throw exception;
            }
        }

        private void IngestCkl()
        {
            try
            {
                openFileDialog.Filter = "CKL Files (*.ckl)|*.ckl";
                openFileDialog.Title = "Please select CKL file(s)";
                openFileDialog.ShowDialog();
                if (openFileDialog.FileNames.Length > 0)
                {
                    for (int i = 0; i < openFileDialog.FileNames.Length; i++)
                    {
                        string filePath = openFileDialog.FileNames[i];
                        string fileName = Path.GetFileNameWithoutExtension(filePath);
                        CklReader cklReader = new CklReader();
                        Model.Object.File file =
                            new Model.Object.File(fileName, "Checklist", "Ready", filePath, "True");
                        file = cklReader.ObtainIdentifiers(file);
                        IngestedFiles.Add(file);
                    }
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to retrieve CKL file(s).");
                throw exception;
            }
        }

        private void IngestWassp()
        {
            try
            {
                openFileDialog.Filter = "WASSP Files (*.html)|*.html;*.xml";
                openFileDialog.Title = "Please select WASSP file(s)";
                openFileDialog.ShowDialog();
                if (openFileDialog.FileNames.Length > 0)
                {
                    for (int i = 0; i < openFileDialog.FileNames.Length; i++)
                    {
                        string filePath = openFileDialog.FileNames[i];
                        string fileName = Path.GetFileNameWithoutExtension(filePath);
                        if (Path.GetExtension(filePath).ToLower().Equals(".html"))
                        {
                            IngestedFiles.Add(
                                new Model.Object.File(fileName, "WASSP - HTML", "Ready", filePath, "True"));
                        }
                        else
                        {
                            IngestedFiles.Add(new Model.Object.File(fileName, "WASSP - XML", "Ready", filePath,
                                "True"));
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to retrieve WASSP file(s).");
                throw exception;
            }
        }

        private void IngestXccdf()
        {
            try
            {
                openFileDialog.Filter = "XCCDF Files (*.xml)|*.xml";
                openFileDialog.Title = "Please select XCCDF file(s)";
                openFileDialog.ShowDialog();
                if (openFileDialog.FileNames.Length > 0)
                {
                    for (int i = 0; i < openFileDialog.FileNames.Length; i++)
                    {
                        string filePath = openFileDialog.FileNames[i];
                        string fileName = Path.GetFileNameWithoutExtension(filePath);
                        IngestedFiles.Add(new Model.Object.File(fileName, "SCAP Benchmark", "Ready", filePath, "True"));
                    }
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to retrieve XCCDF file(s).");
                throw exception;
            }
        }

        private void IngestFortify()
        {
            try
            {
                openFileDialog.Filter = "Fortify FPR Files (*.fpr)|*.fpr";
                openFileDialog.Title = "Please select Fortify file(s)";
                openFileDialog.ShowDialog();
                if (openFileDialog.FileNames.Length > 0)
                {
                    for (int i = 0; i < openFileDialog.FileNames.Length; i++)
                    {
                        string filePath = openFileDialog.FileNames[i];
                        string fileName = Path.GetFileNameWithoutExtension(filePath);
                        IngestedFiles.Add(new Model.Object.File(fileName, "Fortify FPR", "Ready", filePath, "True"));
                    }
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to retrieve FPR file(s).");
                throw exception;
            }
        }

        public RelayCommand ExecuteIngestionCommand => new RelayCommand(ExecuteIngestion);

        private void ExecuteIngestion()
        {
            try
            {
                BackgroundWorker backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += ExecuteIngestionBackgroundWorker_DoWork;
                backgroundWorker.RunWorkerCompleted += ExecuteIngestionBackgroundWorker_RunWorkerCompleted;
                backgroundWorker.RunWorkerAsync();
                backgroundWorker.Dispose();
            }
            catch (Exception exception)
            {
                string error = "Unable to execute file ingestion.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void ExecuteIngestionBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                if (IngestedFiles.Count == 0)
                {
                    e.Result = "No Files";
                    return;
                }
                else
                {
                    ParseFiles();
                }

                stopWatch.Stop();
                LogWriter.LogStatusUpdate("File ingestion complete; Elapsed time: " + stopWatch.Elapsed.ToString());
                stopWatch.Reset();
                Messenger.Default.Send(new NotificationMessage<string>("ModelUpdate", "AllModels"),
                    MessengerToken.ModelUpdated);
                if (IngestedFiles.Count(x => !x.Status.Equals("Processed")) > 0)
                {
                    e.Result = "Error";
                }
                else
                {
                    e.Result = "Success";
                }
            }
            catch (Exception exception)
            {
                e.Result = exception;
            }
        }

        private void ExecuteIngestionBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                Tuple<AppTheme, Accent> appStyle = ThemeManager.DetectAppStyle(Application.Current);
                Notification notification = new Notification();
                notification.Background = appStyle.Item1.Resources["GrayBrush10"].ToString();
                notification.Foreground = appStyle.Item1.Resources["TextBrush"].ToString();
                if (e.Result != null)
                {
                    if (e.Result is Exception)
                    {
                        notification.Accent = "Red";
                        notification.Badge = "Failure";
                        notification.Header = "File Ingestion";
                        notification.Message = "File(s) failed to ingest; see log for details.";
                        Exception exception = e.Result as Exception;
                        string error = "Unable to ingest file(s).";
                        LogWriter.LogErrorWithDebug(error, exception);
                    }
                    else
                    {
                        switch (e.Result.ToString())
                        {
                            case "Success":
                            {
                                notification.Accent = "Green";
                                notification.Badge = "Success";
                                notification.Header = "File Ingestion";
                                notification.Message = "File ingestion completed.";
                                break;
                            }
                            case "No Files":
                            {
                                notification.Accent = "Orange";
                                notification.Badge = "Warning";
                                notification.Header = "File Ingestion";
                                notification.Message = "No files to process.";
                                break;
                            }
                            case "Error":
                            {
                                notification.Accent = "Orange";
                                notification.Badge = "Warning";
                                notification.Header = "File Ingestion";
                                notification.Message = "Errors occurred during file processing; see log for details.";
                                break;
                            }
                        }
                    }
                }

                Messenger.Default.Send(notification);
                GuiFeedback guiFeedback = new GuiFeedback();
                guiFeedback.SetFields("File ingestion complete", "Collapsed", true);
                Messenger.Default.Send(guiFeedback);
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to handle file ingestion background worker completion events.");
                throw exception;
            }
        }

        private void ParseFiles()
        {
            try
            {
                GuiFeedback guiFeedback = new GuiFeedback();
                guiFeedback.SetFields("Processing Files...", "Visible", false);
                string groupName = null;
                if (IngestionGroup != null)
                { groupName = IngestionGroup.GroupName; }
                Messenger.Default.Send(guiFeedback);
                stopWatch.Start();
                databaseInterface.DropIndices();
                foreach (Model.Object.File file in IngestedFiles)
                {
                    fileStopWatch.Start();
                    file.Status = "Processing...";
                    guiFeedback.ProgressLabelText =
                        "Processing File " + (IngestedFiles.IndexOf(file) + 1).ToString() + "...";
                    Messenger.Default.Send(guiFeedback);
                    LogWriter.LogStatusUpdate($"Begin processing of '{file.FileName}'.");
                    switch (file.FileType)
                    {
                        case "ACAS - CSV":
                        {
                            AcasCsvReader acasCsvReader = new AcasCsvReader();
                            file.Status = acasCsvReader.ReadAcasCsvFile(file, groupName);
                            break;
                        }
                        case "ACAS - Nessus":
                        {
                            AcasNessusReader acasNessusReader = new AcasNessusReader();
                            file.Status = acasNessusReader.ReadAcasNessusFile(file, groupName);
                            break;
                        }
                        case "Checklist":
                        {
                            CklReader cklReader = new CklReader();
                            file.Status = cklReader.ReadCklFile(file, groupName);
                            break;
                        }
                        case "WASSP - HTML":
                        {
                            WasspReader wasspReader = new WasspReader();
                            file.Status = wasspReader.ReadWassp(file, groupName);
                            break;
                        }
                        case "WASSP - XML":
                        {
                            XmlWasspReader xmlWasspReader = new XmlWasspReader();
                            file.Status = xmlWasspReader.ReadXmlWassp(file, groupName);
                            break;
                        }
                        case "SCAP Benchmark":
                        {
                            XccdfReader xccdfReader = new XccdfReader();
                            file.Status = xccdfReader.ReadXccdfFile(file, groupName);
                            break;
                        }
                        case "Fortify FPR":
                        {
                            FprReader fprReader = new FprReader();
                            file.Status = fprReader.ReadFpr(file, groupName);
                            break;
                        }
                        default:
                        {
                            LogWriter.LogError(
                                $"The file type for '{file.FileName}' is unrecognized and cannot be parsed.");
                            break;
                        }
                    }

                    fileStopWatch.Stop();
                    if (file.Status.Equals("Processed"))
                    {
                        LogWriter.LogStatusUpdate(
                            $"'{file.FileName}' successfully processed; Elapsed time: {fileStopWatch.Elapsed.ToString()}");
                    }
                    else
                    {
                        LogWriter.LogError(
                            $"'{file.FileName}' processing failed; Elapsed time: {fileStopWatch.Elapsed.ToString()}.");
                    }

                    fileStopWatch.Reset();
                }

                databaseInterface.Reindex();
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to parse ingested files.");
                throw exception;
            }
        }

        public RelayCommand ClearIngestedFilesCommand => new RelayCommand(ClearIngestedFiles);

        private void ClearIngestedFiles()
        {
            try
            {
                IngestedFiles.Clear();
            }
            catch (Exception exception)
            {
                string error = "Unable to clear the ingested files list.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private List<Likelihood> PopulateLikelihoodMatrix()
        {
            try
            {
                return new List<Likelihood>()
                {
                    new Likelihood()
                    {
                        Relevance = "Very High", SeverityOrPervasiveness = "Very Low", CalculatedLikelihood = "Very Low"
                    },
                    new Likelihood()
                        {Relevance = "Very High", SeverityOrPervasiveness = "Low", CalculatedLikelihood = "Low"},
                    new Likelihood()
                    {
                        Relevance = "Very High", SeverityOrPervasiveness = "Moderate", CalculatedLikelihood = "Moderate"
                    },
                    new Likelihood()
                        {Relevance = "Very High", SeverityOrPervasiveness = "High", CalculatedLikelihood = "High"},
                    new Likelihood()
                    {
                        Relevance = "Very High", SeverityOrPervasiveness = "Very High",
                        CalculatedLikelihood = "Very High"
                    },
                    new Likelihood()
                        {Relevance = "High", SeverityOrPervasiveness = "Very Low", CalculatedLikelihood = "Very Low"},
                    new Likelihood()
                        {Relevance = "High", SeverityOrPervasiveness = "Low", CalculatedLikelihood = "Low"},
                    new Likelihood()
                        {Relevance = "High", SeverityOrPervasiveness = "Moderate", CalculatedLikelihood = "Moderate"},
                    new Likelihood()
                        {Relevance = "High", SeverityOrPervasiveness = "High", CalculatedLikelihood = "High"},
                    new Likelihood()
                        {Relevance = "High", SeverityOrPervasiveness = "Very High", CalculatedLikelihood = "Very High"},
                    new Likelihood()
                    {
                        Relevance = "Moderate", SeverityOrPervasiveness = "Very Low", CalculatedLikelihood = "Very Low"
                    },
                    new Likelihood()
                        {Relevance = "Moderate", SeverityOrPervasiveness = "Low", CalculatedLikelihood = "Low"},
                    new Likelihood()
                    {
                        Relevance = "Moderate", SeverityOrPervasiveness = "Moderate", CalculatedLikelihood = "Moderate"
                    },
                    new Likelihood()
                        {Relevance = "Moderate", SeverityOrPervasiveness = "High", CalculatedLikelihood = "Moderate"},
                    new Likelihood()
                        {Relevance = "Moderate", SeverityOrPervasiveness = "Very High", CalculatedLikelihood = "High"},
                    new Likelihood()
                        {Relevance = "Low", SeverityOrPervasiveness = "Very Low", CalculatedLikelihood = "Very Low"},
                    new Likelihood() {Relevance = "Low", SeverityOrPervasiveness = "Low", CalculatedLikelihood = "Low"},
                    new Likelihood()
                        {Relevance = "Low", SeverityOrPervasiveness = "Moderate", CalculatedLikelihood = "Low"},
                    new Likelihood()
                        {Relevance = "Low", SeverityOrPervasiveness = "High", CalculatedLikelihood = "Low"},
                    new Likelihood()
                        {Relevance = "Low", SeverityOrPervasiveness = "Very High", CalculatedLikelihood = "Moderate"},
                    new Likelihood()
                    {
                        Relevance = "Very Low", SeverityOrPervasiveness = "Very Low", CalculatedLikelihood = "Very Low"
                    },
                    new Likelihood()
                        {Relevance = "Very Low", SeverityOrPervasiveness = "Low", CalculatedLikelihood = "Very Low"},
                    new Likelihood()
                        {Relevance = "Very Low", SeverityOrPervasiveness = "Moderate", CalculatedLikelihood = "Low"},
                    new Likelihood()
                        {Relevance = "Very Low", SeverityOrPervasiveness = "High", CalculatedLikelihood = "Low"},
                    new Likelihood()
                        {Relevance = "Very Low", SeverityOrPervasiveness = "Very High", CalculatedLikelihood = "Low"}
                };
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to populate the matrix of possible likelihoods.");
                throw exception;
            }
        }

        private List<Risk> PopulateRiskMatrix()
        {
            try
            {
                return new List<Risk>()
                {
                    new Risk() {Likelihood = "Very High", Impact = "Very Low", CalculatedRisk = "Very Low"},
                    new Risk() {Likelihood = "Very High", Impact = "Low", CalculatedRisk = "Low"},
                    new Risk() {Likelihood = "Very High", Impact = "Moderate", CalculatedRisk = "Moderate"},
                    new Risk() {Likelihood = "Very High", Impact = "High", CalculatedRisk = "High"},
                    new Risk() {Likelihood = "Very High", Impact = "Very High", CalculatedRisk = "Very High"},
                    new Risk() {Likelihood = "High", Impact = "Very Low", CalculatedRisk = "Very Low"},
                    new Risk() {Likelihood = "High", Impact = "Low", CalculatedRisk = "Low"},
                    new Risk() {Likelihood = "High", Impact = "Moderate", CalculatedRisk = "Moderate"},
                    new Risk() {Likelihood = "High", Impact = "High", CalculatedRisk = "High"},
                    new Risk() {Likelihood = "High", Impact = "Very High", CalculatedRisk = "Very High"},
                    new Risk() {Likelihood = "Moderate", Impact = "Very Low", CalculatedRisk = "Very Low"},
                    new Risk() {Likelihood = "Moderate", Impact = "Low", CalculatedRisk = "Low"},
                    new Risk() {Likelihood = "Moderate", Impact = "Moderate", CalculatedRisk = "Moderate"},
                    new Risk() {Likelihood = "Moderate", Impact = "High", CalculatedRisk = "Moderate"},
                    new Risk() {Likelihood = "Moderate", Impact = "Very High", CalculatedRisk = "High"},
                    new Risk() {Likelihood = "Low", Impact = "Very Low", CalculatedRisk = "Very Low"},
                    new Risk() {Likelihood = "Low", Impact = "Low", CalculatedRisk = "Low"},
                    new Risk() {Likelihood = "Low", Impact = "Moderate", CalculatedRisk = "Low"},
                    new Risk() {Likelihood = "Low", Impact = "High", CalculatedRisk = "Low"},
                    new Risk() {Likelihood = "Low", Impact = "Very High", CalculatedRisk = "Moderate"},
                    new Risk() {Likelihood = "Very Low", Impact = "Very Low", CalculatedRisk = "Very Low"},
                    new Risk() {Likelihood = "Very Low", Impact = "Low", CalculatedRisk = "Very Low"},
                    new Risk() {Likelihood = "Very Low", Impact = "Moderate", CalculatedRisk = "Low"},
                    new Risk() {Likelihood = "Very Low", Impact = "High", CalculatedRisk = "Low"},
                    new Risk() {Likelihood = "Very Low", Impact = "Very High", CalculatedRisk = "Low"}
                };
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to populate the matrix of possible risks.");
                throw exception;
            }
        }

        public RelayCommand<object> ShowSelectedControlsCommand
        {
            get { return new RelayCommand<object>((p) => ShowSelectedControls(p)); }
        }

        private void ShowSelectedControls(object parameter)
        {
            try
            {
                // if (parameter == null)
                // { return; }
                // foreach (NIST_ControlCCI nistControlsCci in NistControlsCcis)
                // { nistControlsCci.IsChecked = false; }
                // Vulnerability vulnerability = parameter as Vulnerability;
                // foreach (CCI cci in vulnerability.CCIs)
                // { NistControlsCcis.FirstOrDefault(n => n.CCI_ID == cci.CCI_ID).IsChecked = true; }
            }
            catch (Exception exception)
            {
                string error = "Unable to show selected controls for the selected item.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        public RelayCommand<object> UpdateVulnerabilityCciMappingCommand
        {
            get { return new RelayCommand<object>((p) => UpdateVulnerabilityCciMapping(p)); }
        }

        private void UpdateVulnerabilityCciMapping(object parameter)
        {
            try
            {
                // if (DatabaseBuilder.sqliteConnection.State.ToString().Equals("Closed"))
                // { DatabaseBuilder.sqliteConnection.Open(); }
                // using (SQLiteCommand sqliteCommand = DatabaseBuilder.sqliteConnection.CreateCommand())
                // {
                //     sqliteCommand.Parameters.Add(new SQLiteParameter("UniqueVulnerabilityIdentifier", SelectedVulnerability.UniqueVulnerabilityIdentifier));
                //     sqliteCommand.Parameters.Add(new SQLiteParameter("CCI", SelectedNistControlCci.CCI.CCI_Number));
                //
                //     if (SelectedNistControlCci.IsChecked)
                //     { databaseInterface.MapVulnerabilityToCci(sqliteCommand); }
                //     else
                //     { databaseInterface.DeleteVulnerabilityToCciMapping(sqliteCommand); }
                // }
                // DatabaseBuilder.sqliteConnection.Close();
                // Vulnerabilities.FirstOrDefault(v => v == SelectedVulnerability).CCIs.Add(SelectedNistControlCci.CCI);
            }
            catch (Exception exception)
            {
                string error = "Unable to map vulnerability to CCI";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        public RelayCommand BulkUpdatedVulnerabilityCciMappingCommand =>
            new RelayCommand(BulkUpdateVulnerabilityCciMapping);

        private void BulkUpdateVulnerabilityCciMapping()
        {
            try
            {
                // List<NIST_ControlCCI> checkedNistControlList = BulkNistControlsCcis.Where(n => n.IsChecked).ToList();
                // if (DatabaseBuilder.sqliteConnection.State.ToString().Equals("Closed"))
                // { DatabaseBuilder.sqliteConnection.Open(); }
                // using (SQLiteTransaction sqliteTransaction = DatabaseBuilder.sqliteConnection.BeginTransaction())
                // {
                //     foreach (Vulnerability vulnerability in Vulnerabilities.Where(v => v.IsChecked))
                //     {
                //         using (SQLiteCommand sqliteCommand = DatabaseBuilder.sqliteConnection.CreateCommand())
                //         {
                //             sqliteCommand.Parameters.Add(new SQLiteParameter("CCI"));
                //             sqliteCommand.Parameters.Add(
                //                 new SQLiteParameter("UniqueVulnerabilityIdentifier", vulnerability.UniqueVulnerabilityIdentifier));
                //             foreach (NIST_ControlCCI nistControlCci in checkedNistControlList)
                //             {
                //                 sqliteCommand.Parameters["CCI"].Value = nistControlCci.CCI.CCI_Number;
                //                 databaseInterface.MapVulnerabilityToCci(sqliteCommand);
                //                 vulnerability.CCIs.Add(nistControlCci.CCI);
                //             }
                //         }
                //     }
                //     sqliteTransaction.Commit();
                // }
                // DatabaseBuilder.sqliteConnection.Close();
                // foreach (NIST_ControlCCI nistControlCci in BulkNistControlsCcis.Where(n => n.IsChecked))
                // { nistControlCci.IsChecked = false; }
                // SelectedNistControlCci = null;
                // SelectedVulnerability = null;
            }
            catch (Exception exception)
            {
                string error = "Unable to bulk process vulnerability to CCI mapping.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        public RelayCommand<string> CalculateLikelihoodCommand => new RelayCommand<string>(CalculateLikelihood);

        private void CalculateLikelihood(string senderName)
        {
            try
            {
                switch (senderName)
                {
                    case "findingThreatComboBox":
                    {
                        if (EditableUniqueFinding != null)
                        {
                            CalculateUniqueFindingLikelihood();
                            return;
                        }

                        break;
                    }
                    case "findingSeverityComboBox":
                    {
                        if (EditableUniqueFinding != null)
                        {
                            CalculateUniqueFindingLikelihood();
                            return;
                        }

                        break;
                    }
                    case "mitigationThreatComboBox":
                    {
                            if (EditableMitigationsOrCondition != null)
                            {
                                CalculateMitigationLikelihood();
                                return;
                            }
                            break;
                    }
                    case "mitigationSeverityComboBox":
                    {
                            if (EditableMitigationsOrCondition != null)
                            {
                                CalculateMitigationLikelihood();
                                return;
                            }
                            break;
                    }
                }

                if (string.IsNullOrWhiteSpace(ThreatRelevance) || string.IsNullOrWhiteSpace(SeverityPervasiveness))
                {
                    return;
                }

                List<Likelihood> Likelihoods = PopulateLikelihoodMatrix();
                Likelihood = Likelihoods.FirstOrDefault(x =>
                        x.Relevance.Equals(ThreatRelevance) && x.SeverityOrPervasiveness.Equals(SeverityPervasiveness))
                    ?.CalculatedLikelihood;
                if (!string.IsNullOrWhiteSpace(Impact))
                {
                    CalculateRisk(senderName);
                }
            }
            catch (Exception exception)
            {
                string error = "Unable to calculate the likelihood.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void CalculateMitigationLikelihood()
        {
            try
            {
                if (string.IsNullOrWhiteSpace(EditableMitigationsOrCondition.MitigationOrCondition.ThreatRelevance) || string.IsNullOrWhiteSpace(EditableMitigationsOrCondition.MitigationOrCondition.SeverityPervasiveness))
                { return; }

                List<Likelihood> Likelihoods = PopulateLikelihoodMatrix();
                EditableMitigationsOrCondition.MitigationOrCondition.Likelihood = Likelihoods.FirstOrDefault(x => x.Relevance.Equals(EditableMitigationsOrCondition.MitigationOrCondition.ThreatRelevance) && x.SeverityOrPervasiveness.Equals(EditableMitigationsOrCondition.MitigationOrCondition.SeverityPervasiveness))?.CalculatedLikelihood;
                if (!string.IsNullOrWhiteSpace(EditableMitigationsOrCondition.MitigationOrCondition.Impact))
                { CalculateMitigationRisk(); }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to calculate the likelihood for the provided mitigation.");
                throw exception;
            }
        }

        private void CalculateUniqueFindingLikelihood()
        {
            try
            {
                if (EditableUniqueFinding.MitigationOrCondition == null ||
                    string.IsNullOrWhiteSpace(EditableUniqueFinding.MitigationOrCondition.ThreatRelevance) ||
                    string.IsNullOrWhiteSpace(EditableUniqueFinding.MitigationOrCondition.SeverityPervasiveness))
                {
                    return;
                }

                List<Likelihood> Likelihoods = PopulateLikelihoodMatrix();
                EditableUniqueFinding.MitigationOrCondition.Likelihood = Likelihoods.FirstOrDefault(x =>
                        x.Relevance.Equals(EditableUniqueFinding.MitigationOrCondition.ThreatRelevance) &&
                        x.SeverityOrPervasiveness.Equals(EditableUniqueFinding.MitigationOrCondition
                            .SeverityPervasiveness))
                    ?.CalculatedLikelihood;
                if (!string.IsNullOrWhiteSpace(EditableUniqueFinding.MitigationOrCondition.Impact))
                {
                    CalculateUniqueFindingRisk();
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to calculate likelihood for the provided unique finding.");
                throw exception;
            }
        }

        public RelayCommand<string> CalculateRiskCommand => new RelayCommand<string>(CalculateRisk);

        private void CalculateRisk(string senderName)
        {
            try
            {
                switch (senderName)
                {
                    case "findingImpactComboBox":
                    {
                        if (EditableUniqueFinding != null)
                        {
                            CalculateUniqueFindingRisk();
                        }

                        break;
                    }
                    case "mitigationImpactComboBox":
                    {
                            if (EditableMitigationsOrCondition != null)
                            { CalculateMitigationRisk(); }
                            break;
                    }
                }

                if (string.IsNullOrWhiteSpace(Likelihood) || string.IsNullOrWhiteSpace(Impact))
                {
                    return;
                }

                List<Risk> Risks = PopulateRiskMatrix();
                Risk = Risks.FirstOrDefault(x => x.Likelihood.Equals(Likelihood) && x.Impact.Equals(Impact))
                    ?.CalculatedRisk;
            }
            catch (Exception exception)
            {
                string error = "Unable to calculate the risk.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void CalculateMitigationRisk()
        {
            try
            {
                if (string.IsNullOrWhiteSpace(EditableMitigationsOrCondition.MitigationOrCondition.Likelihood) || string.IsNullOrWhiteSpace(EditableMitigationsOrCondition.MitigationOrCondition.Impact))
                { return; }

                List<Risk> Risks = PopulateRiskMatrix();
                EditableMitigationsOrCondition.MitigationOrCondition.Risk = Risks.FirstOrDefault(x => x.Likelihood.Equals(EditableMitigationsOrCondition.MitigationOrCondition.Likelihood) && x.Impact.Equals(EditableMitigationsOrCondition.MitigationOrCondition.Impact))?.CalculatedRisk;
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to calculate the risk for the provided mitigation.");
                throw exception;
            }
        }

        private void CalculateUniqueFindingRisk()
        {
            try
            {
                if (EditableUniqueFinding.MitigationOrCondition == null ||
                    string.IsNullOrWhiteSpace(EditableUniqueFinding.MitigationOrCondition.Likelihood) ||
                    string.IsNullOrWhiteSpace(EditableUniqueFinding.MitigationOrCondition.Impact))
                {
                    return;
                }

                List<Risk> Risks = PopulateRiskMatrix();
                EditableUniqueFinding.MitigationOrCondition.Risk = Risks.FirstOrDefault(x =>
                    x.Likelihood.Equals(EditableUniqueFinding.MitigationOrCondition.Likelihood) &&
                    x.Impact.Equals(EditableUniqueFinding.MitigationOrCondition.Impact))?.CalculatedRisk;
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to calculate the risk for the provided unique finding.");
                throw exception;
            }
        }

        public RelayCommand<string> ModifyMitigationCommand => new RelayCommand<string>(ModifyMitigation);

        private void ModifyMitigation(string senderName)
        {
            try
            {
                if (SelectedMitigationsOrCondition == null)
                {
                    AddMitigation();
                }
                else
                {
                    UpdateMitigation();
                }
            }
            catch (Exception exception)
            {
                string error = "Unable to modify the provided mitigation.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void AddMitigation()
        {
            try
            {
                BackgroundWorker backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += AddMitigationBackgroundWorker_DoWork;
                backgroundWorker.RunWorkerCompleted += MitigationBackgroundWorker_RunWorkerCompleted;
                backgroundWorker.RunWorkerAsync();
                backgroundWorker.Dispose();
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to add the provided mitigation using a BackgroundWorker.");
                throw exception;
            }
        }

        private void AddMitigationBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                if (Vulnerabilities.Count(v => v.IsChecked) < 1 || MitigationGroup == null)
                {
                    return;
                }

                if (DatabaseBuilder.sqliteConnection.State.ToString().Equals("Closed"))
                {
                    DatabaseBuilder.sqliteConnection.Open();
                }

                using (SQLiteTransaction sqliteTransaction = DatabaseBuilder.sqliteConnection.BeginTransaction())
                {
                    using (SQLiteCommand sqliteCommand = DatabaseBuilder.sqliteConnection.CreateCommand())
                    {
                        databaseInterface.InsertParameterPlaceholders(sqliteCommand);
                        sqliteCommand.Parameters["ImpactDescription"].Value =
                            (object) ImpactDescription ?? DBNull.Value;
                        sqliteCommand.Parameters["PredisposingConditions"].Value =
                            (object) PredisposingConditions ?? DBNull.Value;
                        sqliteCommand.Parameters["TechnicalMitigation"].Value =
                            (object) TechnicalMitigations ?? DBNull.Value;
                        sqliteCommand.Parameters["ProposedMitigation"].Value =
                            (object) ProposedMitigations ?? DBNull.Value;
                        sqliteCommand.Parameters["ThreatDescription"].Value =
                            (object) ThreatDescription ?? DBNull.Value;
                        sqliteCommand.Parameters["ThreatRelevance"].Value = (object) ThreatRelevance ?? DBNull.Value;
                        sqliteCommand.Parameters["SeverityPervasiveness"].Value =
                            (object) SeverityPervasiveness ?? DBNull.Value;
                        sqliteCommand.Parameters["Likelihood"].Value = (object) Likelihood ?? DBNull.Value;
                        sqliteCommand.Parameters["Impact"].Value = (object) Impact ?? DBNull.Value;
                        sqliteCommand.Parameters["Risk"].Value = (object) Risk ?? DBNull.Value;
                        sqliteCommand.Parameters["ResidualRisk"].Value = (object) ResidualRisk ?? DBNull.Value;
                        sqliteCommand.Parameters["ResidualRiskAfterProposed"].Value =
                            (object) ResidualRiskAfterProposed ?? DBNull.Value;
                        sqliteCommand.Parameters["MitigatedStatus"].Value = (object) MitigatedStatus ?? DBNull.Value;
                        sqliteCommand.Parameters["EstimatedCompletionDate"].Value =
                            (object) EstimatedCompletionDate ?? DBNull.Value;
                        sqliteCommand.Parameters["IsApproved"].Value = false.ToString();
                        databaseInterface.InsertMitigationOrCondition(sqliteCommand);

                        sqliteCommand.Parameters["MitigationOrCondition_ID"].Value =
                            databaseInterface.SelectLastInsertRowId(sqliteCommand);
                        sqliteCommand.Parameters["Group_ID"].Value = MitigationGroup.Group_ID;

                        foreach (Vulnerability vuln in Vulnerabilities.Where(v => v.IsChecked))
                        {
                            sqliteCommand.Parameters["Vulnerability_ID"].Value = vuln.Vulnerability_ID;
                            databaseInterface.MapMitigationToGroup(sqliteCommand);
                        }
                    }

                    sqliteTransaction.Commit();
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogErrorWithDebug("Unable to add the provided mitigation.", exception);
            }
            finally
            {
                if (!DatabaseBuilder.sqliteConnection.State.Equals(ConnectionState.Closed))
                {
                    DatabaseBuilder.sqliteConnection.Close();
                }
            }
        }

        private void UpdateMitigation()
        {
            try
            {
                BackgroundWorker backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += UpdateMitigationBackgroundWorker_DoWork;
                backgroundWorker.RunWorkerCompleted += MitigationBackgroundWorker_RunWorkerCompleted;
                backgroundWorker.RunWorkerAsync();
                backgroundWorker.Dispose();
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to update the provided mitigation using a BackgroundWorker.");
                throw exception;
            }
        }

        private void UpdateMitigationBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                GroupMitigationOrConditionVulnerability mitigationOrConditionToModify =
                    EditableMitigationsOrCondition as GroupMitigationOrConditionVulnerability;
                if (DatabaseBuilder.sqliteConnection.State.ToString().Equals("Closed"))
                {
                    DatabaseBuilder.sqliteConnection.Open();
                }

                using (SQLiteTransaction sqliteTransaction = DatabaseBuilder.sqliteConnection.BeginTransaction())
                {
                    using (SQLiteCommand sqliteCommand = DatabaseBuilder.sqliteConnection.CreateCommand())
                    {
                        databaseInterface.InsertParameterPlaceholders(sqliteCommand);
                        sqliteCommand.Parameters["MitigationOrCondition_ID"].Value =
                            mitigationOrConditionToModify.MitigationOrCondition.MitigationOrCondition_ID;
                        sqliteCommand.Parameters["ImpactDescription"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.ImpactDescription ??
                            DBNull.Value;
                        sqliteCommand.Parameters["PredisposingConditions"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.PredisposingConditions ??
                            DBNull.Value;
                        sqliteCommand.Parameters["TechnicalMitigation"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.TechnicalMitigation ??
                            DBNull.Value;
                        sqliteCommand.Parameters["ProposedMitigation"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.ProposedMitigation ??
                            DBNull.Value;
                        sqliteCommand.Parameters["ThreatDescription"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.ThreatDescription ??
                            DBNull.Value;
                        sqliteCommand.Parameters["ThreatRelevance"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.ThreatRelevance ??
                            DBNull.Value;
                        sqliteCommand.Parameters["SeverityPervasiveness"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.SeverityPervasiveness ??
                            DBNull.Value;
                        sqliteCommand.Parameters["Likelihood"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.Likelihood ?? DBNull.Value;
                        sqliteCommand.Parameters["Impact"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.Impact ?? DBNull.Value;
                        sqliteCommand.Parameters["Risk"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.Risk ?? DBNull.Value;
                        sqliteCommand.Parameters["ResidualRisk"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.ResidualRisk ?? DBNull.Value;
                        sqliteCommand.Parameters["ResidualRiskAfterProposed"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.ResidualRiskAfterProposed ??
                            DBNull.Value;
                        sqliteCommand.Parameters["MitigatedStatus"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.MitigatedStatus ??
                            DBNull.Value;
                        sqliteCommand.Parameters["EstimatedCompletionDate"].Value =
                            (object) mitigationOrConditionToModify.MitigationOrCondition.EstimatedCompletionDate ??
                            DBNull.Value;
                        sqliteCommand.Parameters["IsApproved"].Value = mitigationOrConditionToModify.MitigationOrCondition.IsApproved;

                        databaseInterface.UpdateMitigationOrCondition(sqliteCommand);

                        foreach (Group group in Groups.Where(g => g.IsChecked))
                        {
                            sqliteCommand.Parameters["Group_ID"].Value = group.Group_ID;
                            databaseInterface.MapMitigationToGroup(sqliteCommand);
                        }
                    }

                    sqliteTransaction.Commit();
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to update the provided mitigation.");
                throw exception;
            }
        }

        public RelayCommand ClearSelectedMitigationCommand => new RelayCommand(ClearSelectedMitigation);

        private void ClearSelectedMitigation()
        {
            try
            {
                SelectedMitigationsOrCondition = null;
                ClearVulnerabilityGroupsChecked();
            }
            catch (Exception exception)
            {
                string error = "Unable to clear the selected mitigation.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        public RelayCommand DeleteMitigationCommand => new RelayCommand(DeleteMitigation);

        private void DeleteMitigation()
        {
            try
            {
                BackgroundWorker backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += DeleteMitigationBackgroundWorker_DoWork;
                backgroundWorker.RunWorkerCompleted += MitigationBackgroundWorker_RunWorkerCompleted;
                backgroundWorker.RunWorkerAsync();
                backgroundWorker.Dispose();
            }
            catch (Exception exception)
            {
                string error = "Unable to delete the selected mitigation.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void DeleteMitigationBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                if (SelectedMitigationsOrCondition == null)
                {
                    return;
                }

                MitigationOrCondition mitigationToDelete = SelectedMitigationsOrCondition as MitigationOrCondition;

                if (DatabaseBuilder.sqliteConnection.State.ToString().Equals("Closed"))
                {
                    DatabaseBuilder.sqliteConnection.Open();
                }

                using (SQLiteTransaction sqliteTransaction = DatabaseBuilder.sqliteConnection.BeginTransaction())
                {
                    using (SQLiteCommand sqliteCommand = DatabaseBuilder.sqliteConnection.CreateCommand())
                    {
                        sqliteCommand.Parameters.Add(new SQLiteParameter("MitigationOrCondition_ID",
                            mitigationToDelete.MitigationOrCondition_ID));
                        databaseInterface.DeleteMitigationGroupMappingByMitigation(sqliteCommand);
                        databaseInterface.DeleteMitigationOrCondition(sqliteCommand);
                    }

                    sqliteTransaction.Commit();
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to delete the provided mitigation.");
                throw exception;
            }
            finally
            {
                if (!DatabaseBuilder.sqliteConnection.State.Equals(ConnectionState.Closed))
                {
                    DatabaseBuilder.sqliteConnection.Close();
                }
            }
        }

        private void MitigationBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                ClearMitigationValues();
                ClearVulnerabilityGroupsChecked();
                Messenger.Default.Send(new NotificationMessage<string>("ModelUpdate", "VulnerabilityModel"),
                    MessengerToken.ModelUpdated);
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to perform the post mitigation deletion cleanup actions.");
                throw exception;
            }
        }

        private void ClearMitigationValues()
        {
            try
            {
                ImpactDescription = string.Empty;
                PredisposingConditions = string.Empty;
                TechnicalMitigations = string.Empty;
                ProposedMitigations = string.Empty;
                ThreatDescription = string.Empty;
                ThreatRelevance = null;
                SeverityPervasiveness = null;
                Likelihood = string.Empty;
                Impact = null;
                Risk = string.Empty;
                ResidualRisk = null;
                ResidualRiskAfterProposed = null;
                MitigatedStatus = null;
                EstimatedCompletionDate = string.Empty;
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to clear mitigation parameters.");
                throw exception;
            }
        }

        private void SetEditableMitigationOrCondition()
        {
            try
            {
                if (SelectedMitigationsOrCondition == null)
                {
                    EditableMitigationsOrCondition = null;
                    MitigationGroup = null;
                    return;
                }

                EditableMitigationsOrCondition =
                    (GroupMitigationOrConditionVulnerability) SelectedMitigationsOrCondition;
                MitigationGroup = Groups.FirstOrDefault(x => x.Group_ID == EditableMitigationsOrCondition.Group_ID);
                ClearVulnerabilityGroupsChecked();
            }
            catch (Exception exception)
            {
                string error = "Unable to clear set editable mitigation.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void ClearVulnerabilityGroupsChecked()
        {
            try
            {
                foreach (Vulnerability vuln in Vulnerabilities.Where(v => v.IsChecked))
                {
                    vuln.IsChecked = false;
                    foreach (Group group in Groups.Where(g => g.IsChecked))
                    {
                        group.IsChecked = false;
                    }
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to clear checked vulnerabilities and groups.");
                throw exception;
            }
        }

        private void SetEditableUniqueFinding()
        {
            try
            {
                if (SelectedUniqueFinding == null)
                {
                    EditableUniqueFinding = null;
                    return;
                }

                EditableUniqueFinding = (UniqueFinding) SelectedUniqueFinding;
                if (EditableUniqueFinding.MitigationOrCondition == null)
                {
                    EditableUniqueFinding.MitigationOrCondition = new MitigationOrCondition();
                    EditableUniqueFinding.MitigationOrCondition.IsApproved = "False";
                }
            }
            catch (Exception exception)
            {
                string error = "Unable to clear set editable mitigation.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        public RelayCommand UpdateUniqueFindingCommand => new RelayCommand(UpdateUniqueFinding);

        private void UpdateUniqueFinding()
        {
            try
            {
                BackgroundWorker backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += UpdateUniqueFindingBackgroundWorker_DoWork;
                backgroundWorker.RunWorkerCompleted += UpdateUniqueFindingsBackgroundWorker_RunWorkerCompleted;
                backgroundWorker.RunWorkerAsync();
            }
            catch (Exception exception)
            {
                string error = "Unable to update the provided unique finding using a BackgroundWorker.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void UpdateUniqueFindingBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                if (SelectedUniqueFinding == null)
                {
                    return;
                }


                if (DatabaseBuilder.sqliteConnection.State.ToString().Equals("Closed"))
                { DatabaseBuilder.sqliteConnection.Open(); }

                using (SQLiteTransaction sqliteTransaction = DatabaseBuilder.sqliteConnection.BeginTransaction())
                {
                    using (SQLiteCommand sqliteCommand = DatabaseBuilder.sqliteConnection.CreateCommand())
                    {
                        databaseInterface.InsertParameterPlaceholders(sqliteCommand);
                        sqliteCommand.Parameters["ImpactDescription"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.ImpactDescription ?? DBNull.Value;
                        sqliteCommand.Parameters["PredisposingConditions"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.PredisposingConditions ?? DBNull.Value;
                        sqliteCommand.Parameters["TechnicalMitigation"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.TechnicalMitigation ?? DBNull.Value;
                        sqliteCommand.Parameters["ProposedMitigation"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.ProposedMitigation ?? DBNull.Value;
                        sqliteCommand.Parameters["ThreatDescription"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.ThreatDescription ?? DBNull.Value;
                        sqliteCommand.Parameters["ThreatRelevance"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.ThreatRelevance ?? DBNull.Value;
                        sqliteCommand.Parameters["SeverityPervasiveness"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.SeverityPervasiveness ?? DBNull.Value;
                        sqliteCommand.Parameters["Likelihood"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.Likelihood ?? DBNull.Value;
                        sqliteCommand.Parameters["Impact"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.Impact ?? DBNull.Value;
                        sqliteCommand.Parameters["Risk"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.Risk ?? DBNull.Value;
                        sqliteCommand.Parameters["ResidualRisk"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.ResidualRisk ?? DBNull.Value;
                        sqliteCommand.Parameters["ResidualRiskAfterProposed"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.ResidualRiskAfterProposed ?? DBNull.Value;
                        sqliteCommand.Parameters["MitigatedStatus"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.MitigatedStatus ?? DBNull.Value;
                        sqliteCommand.Parameters["EstimatedCompletionDate"].Value =
                            (object)EditableUniqueFinding.MitigationOrCondition.EstimatedCompletionDate ?? DBNull.Value;
                        sqliteCommand.Parameters["IsApproved"].Value =
                            EditableUniqueFinding.MitigationOrCondition.IsApproved;
                        if (EditableUniqueFinding.MitigationOrCondition_ID != null)
                        {
                            sqliteCommand.Parameters["MitigationOrCondition_ID"].Value =
                                EditableUniqueFinding.MitigationOrCondition_ID;
                            databaseInterface.UpdateMitigationOrCondition(sqliteCommand);
                        }
                        else
                        {
                            databaseInterface.InsertMitigationOrCondition(sqliteCommand);
                            sqliteCommand.Parameters["MitigationOrCondition_ID"].Value =
                                databaseInterface.SelectLastInsertRowId(sqliteCommand);
                            sqliteCommand.Parameters.Add(new SQLiteParameter("UniqueFinding_ID",
                                EditableUniqueFinding.UniqueFinding_ID));
                            databaseInterface.UpdateUniqueFindingMitigationOrCondition(sqliteCommand);
                        }
                    }

                    sqliteTransaction.Commit();
                }
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to update the provided unique finding.");
                throw exception;
            }
            finally
            {
                if (!DatabaseBuilder.sqliteConnection.State.Equals(ConnectionState.Closed))
                {
                    DatabaseBuilder.sqliteConnection.Close();
                }
            }
        }

        private void UpdateUniqueFindingsBackgroundWorker_RunWorkerCompleted(object sender,
            RunWorkerCompletedEventArgs e)
        {
            try
            {
                SelectedUniqueFinding = null;
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to run post update unique finding cleanup actions.");
                throw exception;
            }
        }

        public RelayCommand DeleteUniqueFindingCommand => new RelayCommand(DeleteUniqueFinding);

        private void DeleteUniqueFinding()
        {
            try
            {
                BackgroundWorker backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += DeleteUniqueFindingBackgroundWorker_DoWork;
                backgroundWorker.RunWorkerCompleted += DeleteUniqueFindingsBackgroundWorker_RunWorkerCompleted;
                backgroundWorker.RunWorkerAsync();
            }
            catch (Exception exception)
            {
                string error = "Unable to delete the provided unique finding using a BackgroundWorker.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }

        private void DeleteUniqueFindingBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                if (SelectedUniqueFinding == null)
                {
                    return;
                }

                if (DatabaseBuilder.sqliteConnection.State.ToString().Equals("Closed"))
                {
                    DatabaseBuilder.sqliteConnection.Open();
                }

                using (SQLiteTransaction sqliteTransaction = DatabaseBuilder.sqliteConnection.BeginTransaction())
                {
                    using (SQLiteCommand sqliteCommand = DatabaseBuilder.sqliteConnection.CreateCommand())
                    {
                        // if (EditableMitigationsOrCondition.MitigationOrCondition_ID != null)
                        // {
                        //     sqliteCommand.Parameters.Add(new SQLiteParameter("MitigationOrCondition_ID",
                        //         EditableUniqueFinding.MitigationOrCondition_ID));
                        //     databaseInterface.DeleteMitigationOrCondition(sqliteCommand);
                        // }

                        sqliteCommand.Parameters.Add(new SQLiteParameter("UniqueFinding_ID",
                            EditableUniqueFinding.UniqueFinding_ID));
                        databaseInterface.DeleteUniqueFinding(sqliteCommand);
                    }

                    sqliteTransaction.Commit();
                }
            }
            catch (Exception exception)
            {
                string error = "Unable to delete the provided unique finding.";
                throw exception;
            }
            finally
            {
                if (!DatabaseBuilder.sqliteConnection.State.Equals(ConnectionState.Closed))
                {
                    DatabaseBuilder.sqliteConnection.Close();
                }
            }
        }

        private void DeleteUniqueFindingsBackgroundWorker_RunWorkerCompleted(object sender,
            RunWorkerCompletedEventArgs e)
        {
            try
            {
                Messenger.Default.Send(new NotificationMessage<string>("ModelUpdate", "VulnerabilityModel"),
                    MessengerToken.ModelUpdated);
            }
            catch (Exception exception)
            {
                LogWriter.LogError("Unable to perform the post delete unique finding cleanup actions.");
                throw exception;
            }
        }

        public RelayCommand ClearSelectedUniqueFindingCommand => new RelayCommand(ClearSelectedUniqueFinding);

        private void ClearSelectedUniqueFinding()
        {
            try
            {
                SelectedUniqueFinding = null;
            }
            catch (Exception exception)
            {
                string error = "Unable to clear the selected unique finding.";
                LogWriter.LogErrorWithDebug(error, exception);
            }
        }
    }
}