aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.IDE/Themes/ButtonStyle.xaml
blob: f30adc85d629add8d41488841914c87168b5108e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:Tango.Scripting.IDE.Themes">

    <Style x:Key="FocusVisual">
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate>
                    <Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="TangoButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
        <Setter Property="Background" Value="{DynamicResource Button.Static.Background}"/>
        <Setter Property="BorderBrush" Value="{DynamicResource Button.Static.Border}"/>
        <Setter Property="Foreground" Value="{DynamicResource ControlForegroundKey}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Padding" Value="1"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                        <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsDefaulted" Value="true">
                            <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.MouseOver.Border}"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.MouseOver.Border}"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="true">
                            <Setter Property="Background" TargetName="border" Value="{DynamicResource Button.Pressed.Background}"/>
                            <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.Pressed.Border}"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Background" TargetName="border" Value="{DynamicResource Button.Disabled.Background}"/>
                            <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.Disabled.Border}"/>
                            <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{DynamicResource Button.Disabled.Foreground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="TangoToolBarButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Width" Value="26"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                        <Grid>
                            <Rectangle x:Name="background"  Fill="Transparent" Opacity="1"/>
                            <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsDefaulted" Value="true">
                            <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="Fill" TargetName="background" Value="{DynamicResource Toolbar.Button.MaouseMove.Background}"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="true">
                            <Setter Property="Fill" TargetName="background" Value="{DynamicResource ControlBrushColorKey}"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Background" TargetName="border" Value="{DynamicResource Button.Disabled.Background}"/>
                            <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.Disabled.Border}"/>
                            <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{DynamicResource Button.Disabled.Foreground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.Core;
using Tango.PPC.Common.Application;
using Tango.PPC.Common.BackupRestore;
using Tango.Core.ExtensionMethods;
using Tango.Core.DI;
using Tango.PPC.Common.Connection;
using System.IO;
using Tango.Core.Helpers;
using Tango.BL;
using Tango.Settings;
using Tango.Core.DB;
using System.Data.SqlClient;
using Ionic.Zip;
using Tango.BL.Entities;
using Tango.PPC.Common.Authentication;
using Tango.Integration.Upgrade;
using Tango.Core.IO;
using Tango.BL.Enumerations;

namespace Tango.PPC.Common.BackupRestore
{
    public class DefaultBackupManager : ExtendedObject, IBackupManager
    {
        private const string BACKUP_FILE_NAME = "Backup.json";
        private const string DATABASE_FILE_NAME = "Tango.bak";
        private const int VERSION = 1;
        private const string PASSWORD = "1Creativity";

        [TangoInject(TangoInjectMode.WhenAvailable)]
        private IPPCApplicationManager _applicationManager;

        [TangoInject(TangoInjectMode.WhenAvailable)]
        private IMachineProvider _machineProvider;

        [TangoInject(TangoInjectMode.WhenAvailable)]
        private IAuthenticationProvider _authenticationProvider;

        public DefaultBackupManager()
        {
            TangoIOC.Default.Inject(this);
        }

        public event EventHandler<BackupRestoreProgressEventArgs> Progress;

        public Task CreateBackup(string filePath, String name, BackupSettings settings)
        {
            return Task.Factory.StartNew(() =>
            {
                var tempFolder = TemporaryManager.CreateFolder();

                try
                {
                    //Basic
                    LogManager.Log($"Starting backup operation to file '{filePath}'...");
                    LogManager.Log($"Backup settings:\n{settings.ToJsonString()}");
                    OnProgress(BackupRestoreStage.Initializing);

                    LogManager.Log($"Temporary folder created on {tempFolder.Path}.");

                    BackupFile backupFile = new BackupFile();
                    backupFile.Version = VERSION;
                    backupFile.Date = DateTime.Now;
                    backupFile.Settings = settings;
                    backupFile.Name = name;
                    backupFile.MachineSerialNumber = _machineProvider.Machine.SerialNumber;
                    backupFile.MachineGuid = _machineProvider.Machine.Guid;
                    backupFile.ApplicationVersion = _applicationManager.Version.ToString();

                    //Firmware
                    try
                    {
                        LogManager.Log("Extracting firmware version from local tfp package...");
                        using (var st = File.OpenRead(Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "firmware_package.tfp")))
                        {
                            backupFile.FirmwareVersion = _machineProvider.MachineOperator.GetFirmwarePackageInfo(st).Result.FileDescriptors.SingleOrDefault(x => x.Destination == PMR.FirmwareUpgrade.VersionFileDestination.Mcu).Version;
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new FileLoadException("Could extract the firmware version from the TFP package.", ex);
                    }

                    LogManager.Log($"Backup file generated:\n{backupFile.ToJsonString()}");

                    if (settings.Mode == BackupMode.Jobs)
                    {
                        //Jobs
                        LogManager.Log("Starting jobs backup...");
                        OnProgress(BackupRestoreStage.BackingupJobs);
                        using (ObservablesContext db = ObservablesContext.CreateDefault())
                        {
                            var jobs = db.Jobs.ToList();

                            foreach (var job in jobs)
                            {
                                try
                                {
                                    LogManager.Log($"Backing up job '{job.Name}'...");
                                    var jobFile = job.ToJobFile().Result;
                                    backupFile.JobFiles.Add(jobFile);

                                    OnProgress(BackupRestoreStage.BackingupJobs, jobs.IndexOf(job) + 1, jobs.Count, false);
                                }
                                catch (Exception ex)
                                {
                                    throw new InvalidOperationException($"Error extracting job {job.Name}.", ex);
                                }
                            }
                        }
                        LogManager.Log("Jobs backup completed.");
                    }
                    else
                    {
                        //User Settings
                        LogManager.Log("Backing up application settings...");
                        OnProgress(BackupRestoreStage.BackingupSettings);
                        backupFile.SettingsFile = File.ReadAllText(SettingsManager.Default.FilePath);

                        //Application Version
                        LogManager.Log("Backing up application files...");
                        OnProgress(BackupRestoreStage.BackingupApplication);

                        try
                        {
                            PathHelper.CopyDirectory(AssemblyHelper.GetCurrentAssemblyFolder(), tempFolder, true, (current, total) =>
                            {
                                OnProgress(BackupRestoreStage.BackingupApplication, current, total, false);
                            });
                        }
                        catch (Exception ex)
                        {
                            throw new IOException($"Error occurred while copying application files.", ex);
                        }

                        //Database
                        LogManager.Log("Backing up database...");
                        OnProgress(BackupRestoreStage.BackingupDatabase);
                        try
                        {
                            var dataSource = ObservablesContext.GetActualDataSource();
                            using (var dbManager = DbManager.FromDataSource(dataSource))
                            {
                                Directory.CreateDirectory("C:\\Backups");
                                var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}";
                                if (File.Exists(dbBackupFile))
                                {
                                    File.Delete(dbBackupFile);
                                }
                                dbManager.Backup(dataSource.Catalog, dbBackupFile);
                                File.Move(dbBackupFile, Path.Combine(tempFolder, DATABASE_FILE_NAME));
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new IOException("Error creating database backup", ex);
                        }

                        LogManager.Log("Database backup completed.");
                    }

                    //Backup.json
                    try
                    {
                        OnProgress(BackupRestoreStage.WritingConfiguration);
                        var backupFilePath = Path.Combine(tempFolder, BACKUP_FILE_NAME);
                        LogManager.Log($"Writing backup configuration file '{backupFilePath}'...");
                        File.WriteAllText(backupFilePath, backupFile.ToJsonString());
                    }
                    catch (Exception ex)
                    {
                        throw new IOException("Error writing backup configuration file.", ex);
                    }

                    //Compression
                    LogManager.Log($"Generating {filePath}...");
                    using (ZipFile zip = new ZipFile())
                    {
                        zip.Password = PASSWORD;
                        zip.AddDirectory(tempFolder);

                        zip.SaveProgress += (x, e) =>
                        {
                            if (e.EventType == ZipProgressEventType.Saving_AfterWriteEntry)
                            {
                                LogManager.Log($"Compressing '{e.CurrentEntry.FileName}'...");
                                OnProgress(BackupRestoreStage.CompressingFiles, e.EntriesSaved + 1, e.EntriesTotal, false);
                            }
                        };

                        zip.ParallelDeflateThreshold = -1;
                        zip.Save(filePath);
                    }

                    //Done
                    LogManager.Log("Backup operation completed!!!");
                    OnProgress(BackupRestoreStage.Done, 100, 100, false);
                    tempFolder.Delete();
                }
                catch (Exception ex)
                {
                    tempFolder.Delete();

                    OnProgress(BackupRestoreStage.Error, 100, 100, false);
                    LogManager.Log(ex, "Could not complete the backup operation.");
                    throw ex;
                }
            });
        }

        /// <summary>
        /// Extracts the backup configuration from the specified backup file.
        /// </summary>
        /// <param name="filePath">The file path.</param>
        /// <returns></returns>
        public Task<BackupFile> ExtractBackupConfiguration(string filePath)
        {
            return Task.Factory.StartNew<BackupFile>(() =>
            {
                using (ZipFile zip = ZipFile.Read(filePath))
                {
                    zip.Password = PASSWORD;
                    var reader = zip.Entries.SingleOrDefault(x => x.FileName == BACKUP_FILE_NAME).OpenReader();
                    String json = String.Empty;

                    using (StreamReader stReader = new StreamReader(reader))
                    {
                        json = stReader.ReadToEnd();
                    }

                    var backupFile = BackupFile.FromJson(json);
                    reader.Close();
                    reader.Dispose();
                    return backupFile;
                }
            });
        }

        public Task<RestoreResult> Restore(string filePath, RestoreSettings settings)
        {
            TaskCompletionSource<RestoreResult> completionSource = new TaskCompletionSource<RestoreResult>();

            String dbRollbackFile = null;
            bool shouldRollback = false;

            Task.Factory.StartNew(() =>
            {
                LogManager.Log($"Starting restore operation from file '{filePath}'...");
                OnProgress(BackupRestoreStage.Initializing);

                var tempFolder = TemporaryManager.CreateFolder();
                tempFolder.Persist = true;

                var restoreResult = new RestoreResult() { FolderPath = tempFolder };

                try
                {
                    LogManager.Log("Extracting backup file configuration...");

                    BackupFile backupFile = null;

                    //Extract Configuration
                    try
                    {
                        OnProgress(BackupRestoreStage.ExtractingBackupConfiguration);
                        backupFile = ExtractBackupConfiguration(filePath).Result;
                        restoreResult.BackupFile = backupFile;
                        LogManager.Log($"Backup settings:\n{backupFile.Settings.ToJsonString()}");
                    }
                    catch (Exception ex)
                    {
                        throw new IOException("Error extracting backup configuration.", ex);
                    }

                    //Validate Version
                    if (backupFile.Version > VERSION)
                    {
                        throw new NotSupportedException($"Backup file version {backupFile} is not supported.");
                    }

                    //Validate Machine Serial Number
                    if (backupFile.MachineGuid != null)
                    {
                        if (backupFile.Settings.Mode == BackupMode.Full && backupFile.MachineGuid != _machineProvider.Machine.Guid)
                        {
                            throw new InvalidOperationException($"The specified backup file targets machine '{backupFile.MachineSerialNumber}'. Cannot perform the restore operation.");
                        }
                    }

                    //Validate Machine State
                    LogManager.Log("Validating machine state...");
                    OnProgress(BackupRestoreStage.ValidatingMachineState);
                    if (_machineProvider.MachineOperator.IsPrinting)
                    {
                        LogManager.Log("The machine is currently printing. Aborting!");
                        throw new InvalidOperationException("Cannot perform restore operation while machine is dyeing.");
                    }

                    if (backupFile.Settings.Mode == BackupMode.Full && _machineProvider.MachineOperator.State != Transport.TransportComponentState.Connected)
                    {
                        LogManager.Log("Backup is configured to restore the firmware but machine is not connected!");
                        throw new InvalidOperationException("The restore operation is configured to restore the firmware version but the machine is currently disconnected.");
                    }

                    //Create Restore Point
                    try
                    {
                        LogManager.Log("Creating database rollback file...");
                        var dataSource = ObservablesContext.GetActualDataSource();
                        using (var dbManager = DbManager.FromDataSource(dataSource))
                        {
                            Directory.CreateDirectory("C:\\Backups");
                            dbRollbackFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak";
                            LogManager.Log($"Creating database rollback to '{dbRollbackFile}'...");
                            dbManager.Backup(dataSource.Catalog, dbRollbackFile);
                            LogManager.Log("Database rollback created successfully.");
                            shouldRollback = true;
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new InvalidDataException("Error creating database rollback file.", ex);
                    }

                    if (backupFile.Settings.Mode == BackupMode.Jobs)
                    {
                        //Restore Jobs
                        OnProgress(BackupRestoreStage.RestoringJobs);
                        LogManager.Log("Starting jobs restore...");

                        using (ObservablesContext db = ObservablesContext.CreateDefault())
                        {
                            var jobs = db.Jobs.ToList();
                            var jobFiles = backupFile.JobFiles;

                            if (settings.AllowDeleteJobs)
                            {
                                try
                                {
                                    LogManager.Log("Removing existing jobs...");
                                    foreach (var job in jobs.ToList())
                                    {
                                        LogManager.Log($"Removing job '{job.Name}'...");
                                        job.Delete(db);
                                        jobs.Remove(job);
                                    }

                                    db.SaveChanges();
                                }
                                catch (Exception ex)
                                {
                                    throw new Exception("Error removing existing jobs from database.", ex);
                                }
                            }

                            foreach (var jobFile in jobFiles)
                            {
                                LogManager.Log($"Importing job '{jobFile.Name}'...");

                                try
                                {
                                    var existingJob = jobs.FirstOrDefault(x => x.Name == jobFile.Name);

                                    if (existingJob != null)
                                    {
                                        if (settings.OverwriteExistingJobs)
                                        {
                                            try
                                            {
                                                LogManager.Log("Job already exist, overwriting...");

                                                var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, null).Result;
                                                newJob.Guid = existingJob.Guid;

                                                existingJob.Delete(db);
                                                jobs.Remove(existingJob);

                                                db.SaveChanges();
                                                db.Jobs.Add(newJob);
                                                db.SaveChanges();
                                            }
                                            catch (Exception ex)
                                            {
                                                throw new InvalidOperationException("Error overwriting job.", ex);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, null).Result;
                                        db.Jobs.Add(newJob);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    throw new InvalidOperationException("Error importing job.", ex);
                                }

                                OnProgress(BackupRestoreStage.RestoringJobs, jobFiles.IndexOf(jobFile) + 1, jobFiles.Count, false);
                            }

                            OnProgress(BackupRestoreStage.RestoringJobs);
                            db.SaveChanges();
                        }

                        LogManager.Log("Jobs restored successfully.");
                        OnProgress(BackupRestoreStage.Done, 100, 100, false);
                        completionSource.SetResult(restoreResult);
                    }
                    else
                    {
                        //Extract zip file
                        LogManager.Log("Starting backup file extraction...");
                        OnProgress(BackupRestoreStage.ExtractingContent);
                        try
                        {
                            using (ZipFile zip = new ZipFile(filePath))
                            {
                                zip.Password = PASSWORD;

                                zip.ExtractProgress += (x, e) =>
                                {
                                    if (e.EventType == ZipProgressEventType.Extracting_AfterExtractEntry)
                                    {
                                        LogManager.Log($"Extracting '{e.CurrentEntry.FileName}'...");
                                        OnProgress(BackupRestoreStage.ExtractingContent, e.EntriesExtracted + 1, e.EntriesTotal, false);
                                    }
                                };

                                zip.ParallelDeflateThreshold = -1;
                                zip.ExtractAll(tempFolder);
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new IOException("Error extracting backup content.", ex);
                        }

                        //Overwrite settings
                        LogManager.Log("Validating user settings...");
                        if (backupFile.SettingsFile != null)
                        {
                            try
                            {
                                LogManager.Log("Overwriting settings file...");
                                OnProgress(BackupRestoreStage.RestoringSettings);
                                File.WriteAllText(SettingsManager.Default.FilePath, backupFile.SettingsFile);
                            }
                            catch (Exception ex)
                            {
                                throw new IOException("Error overwriting user settings.", ex);
                            }
                        }
                        else
                        {
                            LogManager.Log("No user settings, skipping...");
                        }

                        //Restore database
                        var backupFilePath = Path.Combine(tempFolder, DATABASE_FILE_NAME);
                        LogManager.Log($"Looking for file database backup on '{backupFilePath}'...");
                        if (File.Exists(backupFilePath))
                        {
                            LogManager.Log("Restoring database...");
                            OnProgress(BackupRestoreStage.RestoringDatabase);
                            try
                            {
                                var dataSource = ObservablesContext.GetActualDataSource();
                                using (var dbManager = DbManager.FromDataSource(dataSource))
                                {
                                    Directory.CreateDirectory("C:\\Backups");
                                    var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}";
                                    File.Copy(backupFilePath, dbBackupFile, true);
                                    dbManager.Restore(dataSource.Catalog, dbBackupFile);
                                    File.Delete(dbBackupFile);
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new IOException("Error restoring database backup", ex);
                            }

                            LogManager.Log("Database backup completed.");
                        }
                        else
                        {
                            LogManager.Log("Database backup file not found, skipping...");
                        }

                        //Remove extra files from application temp folder
                        OnProgress(BackupRestoreStage.RemovingTemporaryFiles);
                        LogManager.Log("Removing redundant files from temp folder...");
                        try
                        {
                            File.Delete(backupFilePath);
                        }
                        catch { }
                        try
                        {
                            File.Delete(Path.Combine(tempFolder, BACKUP_FILE_NAME));
                        }
                        catch { }

                        //Update firmware
                        var tfpFile = Path.Combine(tempFolder, "firmware_package.tfp");
                        LogManager.Log($"Looking for tfp file on '{tfpFile}'...");
                        if (File.Exists(tfpFile))
                        {
                            OnProgress(BackupRestoreStage.RestoringFirmware);
                            LogManager.Log("Restoring firmware version...");

                            var stream = new FileStream(tfpFile, FileMode.Open);

                            _machineProvider.MachineOperator.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;


                            var handler = _machineProvider.MachineOperator.UpgradeFirmware(stream, _machineProvider.Machine.IsDemo).Result;
                            handler.Failed += (_, ex) =>
                            {
                                stream.Dispose();
                                OnRestoreException(ex, completionSource, shouldRollback, dbRollbackFile, tempFolder);
                            };
                            handler.Completed += (_, __) =>
                            {
                                OnProgress(BackupRestoreStage.RestoringFirmware, 100, 100, false);
                                stream.Dispose();
                                LogManager.Log("Full backup restored successfully.");
                                UpdateTangoVersionAfterRestore(backupFile);
                                OnProgress(BackupRestoreStage.Done, 100, 100, false);
                                completionSource.SetResult(restoreResult);
                            };
                            handler.Canceled += (_, __) =>
                            {
                                stream.Dispose();
                                OnRestoreException(new Exception("The operation has been canceled."), completionSource, shouldRollback, dbRollbackFile, tempFolder);
                            };
                            handler.Progress += (_, e) =>
                            {
                                OnProgress(BackupRestoreStage.RestoringFirmware, e.Current, e.Total, false);
                            };
                        }
                        else
                        {
                            LogManager.Log("Firmware package file not found, skipping...");
                            UpdateTangoVersionAfterRestore(backupFile);
                            OnProgress(BackupRestoreStage.Done, 100, 100, false);
                            completionSource.SetResult(restoreResult);
                        }
                    }
                }
                catch (Exception ex)
                {
                    OnRestoreException(ex, completionSource, shouldRollback, dbRollbackFile, tempFolder);
                }
            });

            return completionSource.Task;
        }

        private void UpdateTangoVersionAfterRestore(BackupFile backupFile)
        {
            LogManager.Log("Creating a new TangoUpdate entry after successful restore...");
            try
            {
                using (ObservablesContext db = ObservablesContext.CreateDefault())
                {
                    db.TangoUpdates.Add(new TangoUpdate()
                    {
                        EndDate = DateTime.UtcNow,
                        LastUpdated = DateTime.UtcNow,
                        MachineGuid = _machineProvider.Machine.Guid,
                        StartDate = DateTime.UtcNow,
                        Status = (int)TangoUpdateStatuses.OfflineUpdateCompleted,
                        ApplicationVersion = backupFile.ApplicationVersion,
                        FirmwareVersion = backupFile.FirmwareVersion,
                    });

                    db.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error creating TangoUpdate entry...");
            }
        }

        private void OnRestoreException(Exception ex, TaskCompletionSource<RestoreResult> completionSource, bool shouldRollback, String dbRollbackFile, TemporaryFolder tempFolder)
        {
            if (shouldRollback)
            {
                LogManager.Log("Rolling back database changes...");

                var dataSource = ObservablesContext.GetActualDataSource();
                using (var dbManager = DbManager.FromDataSource(dataSource))
                {
                    try
                    {
                        OnProgress(BackupRestoreStage.RollingBackChanges);
                        dbManager.Restore(dataSource.Catalog, dbRollbackFile);
                        LogManager.Log("Database restored successfully.");
                    }
                    catch (Exception e)
                    {
                        LogManager.Log(e, "Error rolling back database.");
                    }
                    finally
                    {
                        try
                        {
                            File.Delete(dbRollbackFile);
                        }
                        catch { }
                    }
                }
            }

            tempFolder.Delete();
            OnProgress(BackupRestoreStage.Error, 100, 100, false);
            LogManager.Log(ex, "Could not complete the restore operation.");
            completionSource.SetException(ex);
        }

        protected virtual void OnProgress(BackupRestoreStage stage, double progress = 0, double maxProgress = 100, bool isIntermediate = true)
        {
            Progress?.Invoke(this, new BackupRestoreProgressEventArgs()
            {
                Stage = stage,
                Progress = progress,
                MaxProgress = maxProgress,
                IsIntermediate = isIntermediate,
            });
        }
    }
}