aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.DB
ModeNameSize
-rw-r--r--App.config5815logstatsplain
-rw-r--r--App.xaml712logstatsplain
d---------Controls131logstatsplain
d---------Converters314logstatsplain
d---------CustomAttributes46logstatsplain
-rw-r--r--DBModule.cs1766logstatsplain
d---------Images122logstatsplain
d---------Managers88logstatsplain
d---------Messages113logstatsplain
d---------Properties227logstatsplain
-rw-r--r--Tango.MachineStudio.DB.csproj32705logstatsplain
-rw-r--r--ViewModelLocator.cs10469logstatsplain
d---------ViewModels2070logstatsplain
d---------Views123logstatsplain
-rw-r--r--packages.config763logstatsplain
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Data;
using Tango.BL;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.Core.Commands;
using Tango.Core.DI;
using Tango.DragAndDrop;
using Tango.PPC.Common;
using Tango.PPC.Common.Messages;
using Tango.PPC.Jobs.Dialogs;
using Tango.PPC.Jobs.Messages;
using Tango.PPC.Jobs.Views;
using System.Data.Entity;
using Tango.BL.Builders;
using Tango.PPC.Jobs.NavigationObjects;
using Tango.PPC.Storage;
using Tango.Explorer;
using System.IO;
using Google.Protobuf;
using Tango.PMR.Exports;
using Tango.Settings;
using Tango.Integration.ExternalBridge;
using System.Windows.Media;
using Tango.PMR.TCC;
using Tango.Pulse;
using System.Windows.Media.Imaging;
using Tango.Touch.Components;
using Tango.PPC.Jobs.ViewContracts;
using Tango.Core.ExtensionMethods;
using Tango.PPC.Common.Synchronization;
using Tango.PPC.Jobs.NotificationItems;
using Tango.PPC.Storage.Models;

namespace Tango.PPC.Jobs.ViewModels
{
    /// <summary>
    /// Represents the jobs list view model.
    /// </summary>
    /// <seealso cref="Tango.PPC.Common.PPCViewModel" />
    public class JobsViewVM : PPCViewModel<IJobsView>
    {
        private ObservablesContext _db; //Holds the db context for the job list.
        private ObservableCollection<ColorCatalog> _catalogs; //Holds the available color catalogs for the site.
        private ObservableCollection<Rml> _rmls; //Holds the available RML for the site.
        private List<ColorSpace> _colorSpaces; //Holds the available color spaces.
        private bool _isJobsSynchronizationNotificationActive;

        public enum JobsCategory
        {
            Draft,
            History
        }

        #region Properties

        private ObservableCollection<Job> _jobs;
        /// <summary>
        /// Gets or sets the collection of jobs.
        /// </summary>
        public ObservableCollection<Job> Jobs
        {
            get { return _jobs; }
            set { _jobs = value; RaisePropertyChangedAuto(); }
        }

        private ICollectionView _draftJobsCollectionView;
        /// <summary>
        /// Gets or sets the jobs collection view.
        /// </summary>
        public ICollectionView DraftJobsCollectionView
        {
            get { return _draftJobsCollectionView; }
            set
            {
                _draftJobsCollectionView = value;
                RaisePropertyChangedAuto();
            }
        }

        private ICollectionView _historyJobsCollectionView;
        /// <summary>
        /// Gets or sets the jobs collection view.
        /// </summary>
        public ICollectionView HistoryJobsCollectionView
        {
            get { return _historyJobsCollectionView; }
            set
            {
                _historyJobsCollectionView = value;
                RaisePropertyChangedAuto();
            }
        }

        private ObservableCollection<Job> _selectedJobs;
        /// <summary>
        /// Gets or sets the selected jobs.
        /// </summary>
        public ObservableCollection<Job> SelectedJobs
        {
            get { return _selectedJobs; }
            set { _selectedJobs = value; RaisePropertyChangedAuto(); }
        }

        private bool _isLoadingJobs;
        /// <summary>
        /// Gets or sets a value indicating whether this instance is loading jobs.
        /// </summary>
        public bool IsLoadingJobs
        {
            get { return _isLoadingJobs; }
            set { _isLoadingJobs = value; RaisePropertyChangedAuto(); }
        }

        private bool _isMultiSelecting;
        /// <summary>
        /// Gets or sets a value indicating whether this the jobs list is in multi select mode.
        /// </summary>
        public bool IsMultiSelecting
        {
            get { return _isMultiSelecting; }
            set { _isMultiSelecting = value; RaisePropertyChangedAuto(); }
        }

        private int _selectedCategoryIndex;
        /// <summary>
        /// Gets or sets the index of the selected category.
        /// </summary>
        public int SelectedCategoryIndex
        {
            get { return _selectedCategoryIndex; }
            set
            {
                _selectedCategoryIndex = value;
                RaisePropertyChangedAuto();
                RaisePropertyChanged(nameof(SelectedCategory));
            }
        }

        /// <summary>
        /// Gets or sets the selected category.
        /// </summary>
        public JobsCategory SelectedCategory
        {
            get { return (JobsCategory)SelectedCategoryIndex; }
            set
            {
                if (SelectedCategoryIndex != value.ToInt32())
                {
                    SelectedCategoryIndex = value.ToInt32();
                    Filter = null;
                }
            }
        }

        private String _filter;
        /// <summary>
        /// Gets or sets the search filter.
        /// </summary>
        public String Filter
        {
            get { return _filter; }
            set { _filter = value; RaisePropertyChangedAuto(); OnFilterChanged(); }
        }

        private ICollectionFilter _collectionFilter;
        public ICollectionFilter CollectionFilter
        {
            get { return _collectionFilter; }
            set { _collectionFilter = value; RaisePropertyChangedAuto(); }
        }


        #endregion

        #region Commands

        /// <summary>
        /// Gets or sets the job selected command.
        /// </summary>
        public RelayCommand<Object> JobSelectedCommand { get; set; }

        /// <summary>
        /// Gets or sets the job drag and drop command.
        /// </summary>
        public RelayCommand<DropEventArgs> JobDragedDroppedCommand { get; set; }

        /// <summary>
        /// Gets or sets the clear selection command.
        /// </summary>
        public RelayCommand ClearSelectionCommand { get; set; }

        /// <summary>
        /// Gets or sets the add job command.
        /// </summary>
        public RelayCommand AddJobCommand { get; set; }

        /// <summary>
        /// Gets or sets the delete jobs command.
        /// </summary>
        public RelayCommand DeleteJobsCommand { get; set; }

        /// <summary>
        /// Gets or sets the clone jobs command.
        /// </summary>
        public RelayCommand CloneJobsCommand { get; set; }

        /// <summary>
        /// Gets or sets the export job command.
        /// </summary>
        public RelayCommand ExportJobCommand { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="JobsViewVM"/> class.
        /// </summary>
        public JobsViewVM()
        {
            Jobs = new ObservableCollection<Job>();
            SelectedJobs = new ObservableCollection<Job>();

            JobSelectedCommand = new RelayCommand<Object>((x) => SelectJob(x as Job));
            JobDragedDroppedCommand = new RelayCommand<DropEventArgs>((e) =>
            {
                Job draggedJob = e.Draggable.DataContext as Job;
                Job droppedJob = e.Droppable.DataContext as Job;

                DragAndDropJob(draggedJob, droppedJob);
            });

            ClearSelectionCommand = new RelayCommand(ClearSelection);
            AddJobCommand = new RelayCommand(() => AddNewJob());
            DeleteJobsCommand = new RelayCommand(() => DeleteJobs(SelectedJobs));
            CloneJobsCommand = new RelayCommand(() => CloneJobs(SelectedJobs));
            ExportJobCommand = new RelayCommand(ExportJob);

            RegisterForMessage<JobRemovedMessage>(HandleJobRemovedMessage);
            RegisterForMessage<JobSavedMessage>(HandleJobSavedMessage);
            RegisterForMessage<NavigatedToJobsModuleMessage>((x) => Filter = null);

            CollectionFilter = new DefaultCollectionFilter();
            CollectionFilter.RegisterFilter(item =>
            {
                var job = item as Job;

                if (job != null)
                {
                    if (String.IsNullOrEmpty(Filter))
                    {
                        return true;
                    }
                    else
                    {
                        return (job.Name.ToLower().StartsWith(Filter) || (job.Customer != null && job.Customer.Name.ToLower().StartsWith(Filter)));
                    }
                }
                else
                {
                    return true;
                }
            });
        }

        #endregion

        #region Drag & Drop

        /// <summary>
        /// Called when a job has been dragged and dropped into another job.
        /// </summary>
        /// <param name="draggedJob">The dragged job.</param>
        /// <param name="droppedJob">The dropped job.</param>
        private void DragAndDropJob(Job draggedJob, Job droppedJob)
        {
            LogManager.Log($"Job Drag & Drop '{draggedJob.Name}' => '{droppedJob.Name}'.");

            if (draggedJob.JobIndex > droppedJob.JobIndex)
            {
                draggedJob.JobIndex = droppedJob.JobIndex - 1;
            }
            else
            {
                draggedJob.JobIndex = droppedJob.JobIndex + 1;
            }

            int index = 1;

            foreach (var job in Jobs.OrderBy(x => x.JobIndex))
            {
                job.JobIndex = index++;
            }

            DraftJobsCollectionView.Refresh();
        }

        #endregion

        #region Job Selection & Loading

        /// <summary>
        /// Selects the job.
        /// </summary>
        /// <param name="job">The job.</param>
        public async void SelectJob(Job job, bool directlyToEdit = false)
        {
            if (!ApplicationManager.IsInTechnicianMode && job.ColorSpace != null && job.ColorSpace.Code == ColorSpaces.Volume.ToInt32())
            {
                await NotificationProvider.ShowError("The selected job is supported only in technician mode.");
                return;
            }

            LogManager.Log($"Job '{job.Name}' selected.");

            RaiseMessage(new JobSelectedMessage() { Job = job, Context = _db });

            if (!directlyToEdit && MachineProvider.MachineOperator.CanPrint)
            {
                await NavigationManager.NavigateWithObject<JobsModule, JobSummeryView, JobSummeryNavigationObject>(new JobSummeryNavigationObject()
                {
                    Context = _db,
                    Job = job,
                });
            }
            else
            {
                await NavigationManager.NavigateTo<JobsModule>(nameof(JobView));
            }
        }

        /// <summary>
        /// Loads the jobs from database.
        /// </summary>
        public void LoadJobs(Action onCompleted = null)
        {
            try
            {
                LogManager.Log("Loading machine jobs...");

                Task.Factory.StartNew(() =>
                {
                    IsLoadingJobs = true;

                    Thread.Sleep(500);

                    _db = ObservablesContext.CreateDefault();

                    _colorSpaces = _db.ColorSpaces.ToList();

                    var jobs = new JobsCollectionBuilder(_db).Set(x => x.MachineGuid == MachineProvider.Machine.Guid).WithSegments().WithBrushStops().WithCustomer().WithColorSpace().Build();

                    InvokeUI(() =>
                    {
                        Jobs = jobs;
                        DraftJobsCollectionView = new ListCollectionView(Jobs);
                        DraftJobsCollectionView.SortDescriptions.Add(new SortDescription(nameof(Job.LastUpdated), ListSortDirection.Descending));
                        DraftJobsCollectionView.Filter = new Predicate<object>(x =>
                        {
                            var job = x as Job;
                            return job.JobStatus == JobStatuses.Draft;
                        });


                        HistoryJobsCollectionView = new ListCollectionView(Jobs);
                        HistoryJobsCollectionView.SortDescriptions.Add(new SortDescription(nameof(Job.LastUpdated), ListSortDirection.Descending));
                        HistoryJobsCollectionView.Filter = new Predicate<object>(x =>
                        {
                            var job = x as Job;
                            return job.JobStatus != JobStatuses.Draft;
                        });

                        IsLoadingJobs = false;
                        LogManager.Log("Machine jobs loaded!");
                        onCompleted?.Invoke();
                    });
                });
            }
            catch (Exception ex)
            {
                LogManager.Log(ex);
                NotificationProvider.ShowError("An error occurred while trying to load the machine jobs.");
            }
        }

        /// <summary>
        /// Clears the job selection.
        /// </summary>
        public void ClearSelection()
        {
            SelectedJobs.Clear();
            IsMultiSelecting = false;
        }

        /// <summary>
        /// Adds a new job.
        /// </summary>
        private async void AddNewJob(Color? colorProfile = null, TwnFile twnFile = null)
        {
            try
            {
                LogManager.Log("Adding new job...");

                var settings = SettingsManager.Default.GetOrCreate<JobsModuleSettings>();

                var machine = MachineProvider.Machine;

                JobCreationViewVM vm = new JobCreationViewVM(
                    Settings.SupportedJobTypes.Count > 0 ? Settings.SupportedJobTypes : Enum.GetValues(typeof(JobTypes)).Cast<JobTypes>().ToList(),
                    Settings.SupportedColorSpaces.Count > 0 ? Settings.SupportedColorSpaces : Enum.GetValues(typeof(ColorSpaces)).Cast<ColorSpaces>().Where(x => x.IsUserSpace() || (ApplicationManager.IsInTechnicianMode && x == ColorSpaces.Volume)).ToList()
                    );

                if (_catalogs.Count == 0)
                {
                    vm.SupportedColorSpaces.Remove(ColorSpaces.Catalog);
                }

                CatalogSelectionViewVM catalogVM = new CatalogSelectionViewVM(_catalogs.ToList(), _catalogs.ToList().SingleOrDefault(x => x.Guid == settings.LastSelectedCatalogGuid));

                if (settings.LastJobType != null)
                {
                    vm.SelectedJobType = settings.LastJobType.Value;
                }
                else
                {
                    vm.SelectedJobType = Settings.SupportedJobTypes.FirstOrDefault();
                }

                if (settings.LastJobColorSpace != null)
                {
                    vm.SelectedColorSpace = settings.LastJobColorSpace.Value.IsUserSpace() ? settings.LastJobColorSpace.Value : ColorSpaces.Catalog;
                }
                else
                {
                    var space = Settings.SupportedColorSpaces.FirstOrDefault();
                    vm.SelectedColorSpace = space.IsUserSpace() ? space : ColorSpaces.Catalog;
                }

                if (colorProfile != null || twnFile != null)
                {
                    vm.SupportedColorSpaces = new List<ColorSpaces>() { ColorSpaces.RGB };
                    vm.SelectedColorSpace = vm.SupportedColorSpaces.First();
                }

                if (twnFile != null)
                {
                    vm.SupportedJobTypes = new List<JobTypes>() { JobTypes.Embroidery };
                    vm.SelectedJobType = vm.SupportedJobTypes.First();
                }

                if (twnFile == null)
                {
                    if (Settings.SupportedJobTypes.Count != 1 || Settings.SupportedColorSpaces.Count != 1)
                    {
                        vm = await NotificationProvider.ShowDialog<JobCreationViewVM>(vm);
                        if (!vm.DialogResult) return;

                        if (vm.SelectedColorSpace == ColorSpaces.Catalog)
                        {
                            if (catalogVM.SelectedCatalog == null)
                            {
                                catalogVM.SelectedCatalog = _catalogs.FirstOrDefault();
                            }

                            if (_catalogs.Count == 0)
                            {
                                await NotificationProvider.ShowError("No color catalogs found. Please selected another color space.");
                                return;
                            }
                            else if (_catalogs.Count > 1)
                            {
                                catalogVM = await NotificationProvider.ShowDialog<CatalogSelectionViewVM>(catalogVM);

                                if (!catalogVM.DialogResult)
                                {
                                    return;
                                }
                            }
                        }
                    }
                    else
                    {
                        vm.SelectedJobType = Settings.SupportedJobTypes.First();
                        vm.SelectedColorSpace = Settings.SupportedColorSpaces.First();
                    }
                }

                settings.LastJobType = vm.SelectedJobType;
                settings.LastJobColorSpace = vm.SelectedColorSpace;

                if (vm.SelectedColorSpace == ColorSpaces.Catalog)
                {
                    settings.LastSelectedCatalogGuid = catalogVM.SelectedCatalog.Guid;
                }

                settings.Save();

                Job job = new Job();
                job.LastUpdated = DateTime.UtcNow;
                job.JobSource = JobSource.Local;
                job.Name = "untitled";
                job.NumberOfHeads = 1;
                job.NumberOfUnits = 1;
                job.SampleUnitsOrMeters = 1;
                job.CreationDate = DateTime.UtcNow;
                job.JobStatus = JobStatuses.Draft;
                job.JobType = vm.SelectedJobType;
                job.EnableLubrication = true;
                job.ColorSpaceGuid = Adapter.ColorSpaces.FirstOrDefault(x => x.Code == vm.SelectedColorSpace.ToInt32()).Guid;
                job.ColorSpace = _colorSpaces.SingleOrDefault(x => x.Guid == job.ColorSpaceGuid);
                job.MachineGuid = MachineProvider.Machine.Guid;
                job.UserGuid = null;
                job.RmlGuid = (Settings.DefaultRmlGuid != null && _rmls.Select(x => x.Guid).Contains(Settings.DefaultRmlGuid)) ? Settings.DefaultRmlGuid : _rmls.FirstOrDefault().Guid;
                job.WindingMethodGuid = Adapter.WindingMethods.FirstOrDefault().Guid;
                job.SpoolTypeGuid = Settings.DefaultSpoolTypeGuid != null ? Settings.DefaultSpoolTypeGuid : Adapter.SpoolTypes.FirstOrDefault().Guid;

                if (vm.SelectedColorSpace == ColorSpaces.Catalog)
                {
                    job.ColorCatalogGuid = catalogVM.SelectedCatalog.Guid;
                }

                if (Jobs.Count > 0)
                {
                    job.JobIndex = Jobs.Max(x => x.JobIndex) + 1;
                }

                if (colorProfile == null)
                {
                    job.AddSolidSegment(Settings.DefaultSegmentLength > 0 ? Settings.DefaultSegmentLength : 100);
                }
                else
                {
                    job.AddSolidSegment(colorProfile.Value, Settings.DefaultSegmentLength > 0 ? Settings.DefaultSegmentLength : 100);
                    job.Name = $"SnapMatch {colorProfile.Value.R}, {colorProfile.Value.G}, {colorProfile.Value.B}";
                }

                if (twnFile != null)
                {
                    job.Name = twnFile.Name;
                    job.NumberOfUnits = Math.Max(twnFile.NumberOfCopies, 1);
                    job.EmbroideryFileName = twnFile.Name + "." + twnFile.EmbroideryFileFormat;
                    job.EmbroideryFileData = twnFile.EmbroideryFile;
                    job.EmbroideryJpeg = twnFile.ThumbnailData;

                    job.Segments.Clear();

                    int index = 1;

                    foreach (var segment in twnFile.Segments)
                    {
                        Segment s = new Segment();
                        s.Job = job;
                        s.SegmentIndex = index++;
                        s.Name = "Embroidery Segment";
                        s.Length = segment.Length / 100d;

                        int sIndex = 1;

                        foreach (var stop in segment.BrushStops)
                        {
                            BrushStop st = new BrushStop();
                            st.Segment = s;
                            st.StopIndex = sIndex++;
                            st.OffsetPercent = stop.Offset * 100d;
                            st.Red = stop.R;
                            st.Green = stop.G;
                            st.Blue = stop.B;
                            st.ColorSpaceGuid = job.ColorSpaceGuid;
                            s.BrushStops.Add(st);
                        }

                        job.Segments.Add(s);
                    }
                }

                _db.Jobs.Add(job);

                await _db.SaveChangesAsync();

                Jobs.Add(job);

                LogManager.Log($"Job {job.Name} added successfully.");

                RaiseMessage(new JobSelectedMessage() { Job = job, Context = _db });

                await Task.Delay(200);
                await NavigationManager.NavigateWithObject<JobsModule, JobView, JobNavigationObject>(new JobNavigationObject()
                {
                    Job = job,
                    Intent = JobNavigationIntent.NewJob
                });
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error creating new job.");
                await NotificationProvider.ShowError("An error occurred while trying to add a new job.");
            }
        }

        /// <summary>
        /// Deletes the specified jobs from db.
        /// </summary>
        /// <param name="jobs">The jobs.</param>
        private async void DeleteJobs(ObservableCollection<Job> jobs)
        {
            try
            {
                LogManager.Log($"Removing selected jobs:\n{jobs.Select(x => x.Name).ToList().ToJsonString()}");

                if (await NotificationProvider.ShowQuestion("Are you sure you want to delete the selected jobs"))
                {
                    foreach (var job in jobs)
                    {
                        await job.DeleteCascadeAsync(_db);
                        Jobs.Remove(job);
                    }

                    await _db.SaveChangesAsync();
                    ClearSelection();
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error removing selected jobs.");
                await NotificationProvider.ShowError("An error occurred while trying to remove the selected jobs.");
            }
        }

        /// <summary>
        /// Clones the specified jobs.
        /// </summary>
        /// <param name="jobs">The jobs.</param>
        private async void CloneJobs(ObservableCollection<Job> jobs)
        {
            try
            {
                LogManager.Log($"Cloning selected jobs:\n{jobs.Select(x => x.Name).ToList().ToJsonString()}");

                int index = Jobs.Max(x => x.JobIndex);

                List<Job> clonedJobs = new List<Job>();

                foreach (var job in SelectedJobs)
                {
                    var cloned = job.Clone();
                    cloned.JobIndex = ++index;
                    _db.Jobs.Add(cloned);
                    clonedJobs.Add(cloned);
                }

                await _db.SaveChangesAsync();

                foreach (var job in clonedJobs)
                {
                    Jobs.Add(job);
                }

                ClearSelection();
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error cloning selected jobs.");
                await NotificationProvider.ShowError("An error occurred while trying to clone the selected jobs.");
            }
        }

        /// <summary>
        /// Called when the search filter has been changed
        /// </summary>
        private void OnFilterChanged()
        {
            if (DraftJobsCollectionView != null && HistoryJobsCollectionView != null)
            {
                CollectionFilter.RaiseFilterChanged();
                View.ScrollToTop();
            }
        }

        #endregion

        #region Message Handling

        /// <summary>
        /// Handles the job removed message.
        /// </summary>
        /// <param name="msg">The MSG.</param>
        private void HandleJobRemovedMessage(JobRemovedMessage msg)
        {
            try
            {
                LogManager.Log("JobRemovedMessage message received, removing job from list...");
                var job = Jobs.SingleOrDefault(x => x.Guid == msg.Job.Guid);
                Jobs.Remove(job);
                _db.Entry(job).State = System.Data.Entity.EntityState.Detached;
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Could not remove job");
            }
        }

        /// <summary>
        /// Handles the job saved message.
        /// </summary>
        /// <param name="msg">The MSG.</param>
        private void HandleJobSavedMessage(JobSavedMessage msg)
        {
            LogManager.Log("JobSavedMessage message received.");
            LoadJobs();
        }

        #endregion

        #region Override Methods

        /// <summary>
        /// Called when the application has been started.
        /// </summary>
        public override void OnApplicationStarted()
        {
            LoadJobs();

            ExternalBridgeService.ColorProfileRequest += ExternalBridgeService_ColorProfileRequest;
        }

        public async override void OnApplicationReady()
        {
            base.OnApplicationReady();
            StorageProvider.RegisterFileHandler(ExplorerFileDefinition.Job.Extension, HandleJobFileLoaded);
            StorageProvider.RegisterFileHandler(ExplorerFileDefinition.ColorProfile.Extension, HandleColorProfileFileLoaded);
            StorageProvider.RegisterFileHandler(ExplorerFileDefinition.Pulse.Extension, HandlePulseFileLoaded);

            //Load catalogs.
            using (ObservablesContext db = ObservablesContext.CreateDefault())
            {
                _catalogs = await new CatalogsCollectionBuilder(db).SetAll().ForSite(MachineProvider.Machine.SiteGuid).BuildAsync();
                _rmls = await new RmlsCollectionBuilder(db).SetAll().ForHeadType(MachineProvider.Machine.MachineHeadType).ForSite(MachineProvider.Machine.SiteGuid).BuildAsync();
            }

            MachineDataSynchronizer.SynchronizationEnded += MachineDataSynchronizer_SynchronizationEnded;
        }

        public override void OnNavigatedTo()
        {
            base.OnNavigatedTo();
            Filter = null;
        }

        #endregion

        #region Job Export

        private async void ExportJob()
        {
            var selected_job = SelectedJobs.FirstOrDefault();
            if (selected_job == null) return;

            var selectedJobs = SelectedJobs.ToList();

            ClearSelection();

            var result = await NavigationManager.
                NavigateForResult<StorageModule,
                Storage.Views.MainView, ExplorerFileItem,
                StorageNavigationRequest>(
                new StorageNavigationRequest()
                {
                    Intent = selectedJobs.Count == 1 ? StorageNavigationIntent.SaveFile : StorageNavigationIntent.SaveFiles,
                    DefaultFileName = selected_job.Name,
                    Filter = ExplorerFileDefinition.Job.Extension,
                    Title = selectedJobs.Count == 1 ? "Save Job File" : "Save Job Files",
                });

            if (result != null)
            {
                if (selectedJobs.Count == 1)
                {
                    try
                    {
                        var jobFile = await selected_job.ToJobFile();

                        using (FileStream fs = new FileStream(result.Path + ExplorerFileDefinition.Job.Extension, FileMode.Create))
                        {
                            jobFile.WriteTo(fs);
                        }

                        await NotificationProvider.ShowSuccess("Job saved successfully.");
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, $"Error saving job {selected_job.Name} to file.");
                        await NotificationProvider.ShowError($"An error occurred while trying to save the job.\n{ex.Message}");
                    }
                }
                else
                {
                    foreach (var job in selectedJobs)
                    {
                        try
                        {
                            var jobFile = await job.ToJobFile();

                            using (FileStream fs = new FileStream(Path.Combine(result.Path, jobFile.Name.ToValidFileName()) + ExplorerFileDefinition.Job.Extension, FileMode.Create))
                            {
                                jobFile.WriteTo(fs);
                            }
                        }
                        catch (Exception ex)
                        {
                            LogManager.Log(ex, $"Error saving job {job.Name} to file.");
                            await NotificationProvider.ShowError($"An error occurred while trying to save the job.\n{ex.Message}");
                        }
                    }

                    await NotificationProvider.ShowSuccess("Jobs saved successfully.");
                }
            }
        }

        #endregion

        #region Handle Job File Loading From Storage

        private async void HandleJobFileLoaded(List<ExplorerFileItem> jobFiles)
        {
            var vm = await NotificationProvider.ShowDialog<ImportJobViewVM>();

            if (vm.DialogResult)
            {
                using (ObservablesContext jobContext = ObservablesContext.CreateDefault())
                {
                    foreach (var jobFile in jobFiles)
                    {
                        try
                        {
                            JobFile jFile = JobFile.Parser.ParseFrom(File.ReadAllBytes(jobFile.Path));
                            var job = await Job.FromJobFile(jFile, MachineProvider.Machine.Guid, null);
                            job.JobSource = JobSource.Local;
                            jobContext.Jobs.Add(job);
                            await jobContext.SaveChangesAsync();
                        }
                        catch (Exception ex)
                        {
                            LogManager.Log(ex, $"Error occurred while trying to import job from file {jobFile.Path}.");
                            await NotificationProvider.ShowError($"An error occurred while trying to import the selected job file.\n{ex.Message}");
                        }
                    }

                    LoadJobs(() =>
                    {
                        //Editing of a job is currently deprecated due to enabling multiple job imports.
                        //if (vm.ImportAndEdit)
                        //{
                        //    var postJob = Jobs.SingleOrDefault(x => x.Guid == job.Guid);
                        //    if (postJob != null)
                        //    {
                        //        SelectJob(postJob, true);
                        //    }
                        //}
                    });
                }
            }
        }

        #endregion

        #region Handle TCC File Loading From Storage

        private async void HandleColorProfileFileLoaded(List<ExplorerFileItem> tccFiles)
        {
            var tccFile = tccFiles.FirstOrDefault();

            try
            {
                DetectionColorFile tcc = DetectionColorFile.Parser.ParseFrom(File.ReadAllBytes(tccFile.Path));

                var vm = await NotificationProvider.ShowDialog<ImportColorProfileViewVM>(new ImportColorProfileViewVM()
                {
                    Color = Color.FromRgb(
                       (byte)tcc.ProcessedColor.R,
                       (byte)tcc.ProcessedColor.G,
                       (byte)tcc.ProcessedColor.B),
                });

                if (vm.DialogResult)
                {
                    AddNewJob(vm.Color);
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, $"Error occurred while trying to import detection color from file {tccFile.Path}.");
                await NotificationProvider.ShowError($"An error occurred while trying to import the selected detection color.\n{ex.Message}");
            }
        }

        #endregion

        #region Handle Pulse TWN Loading From Storage

        private async void HandlePulseFileLoaded(List<ExplorerFileItem> twnFiles)
        {
            var twnFile = twnFiles.FirstOrDefault();

            TwnFile twn = TwnFile.FromFile(twnFile.Path);
            BitmapSource preview = twn.Thumbnail.ToBitmapSource();

            var vm = await NotificationProvider.ShowDialog<ImportTwnFileViewVM>(new ImportTwnFileViewVM()
            {
                Thumbnail = preview,
            });

            if (vm.DialogResult)
            {
                AddNewJob(null, twn);
            }
        }

        #endregion

        #region Handle New Synchronized Jobs

        private void MachineDataSynchronizer_SynchronizationEnded(object sender, SynchronizationEndedEventArgs e)
        {
            if (e.NewChangedJobs > 0 && !_isJobsSynchronizationNotificationActive)
            {
                _isJobsSynchronizationNotificationActive = true;

                var item = NotificationProvider.PushNotification<NewSynchronizardJobsNotificationItem>();
                item.Pressed += (_, __) =>
                {
                    _isJobsSynchronizationNotificationActive = false;
                    LoadJobs(() =>
                    {
                        NotificationProvider.ShowSuccess("Your job list is now synchronized.");
                    });
                };
                item.Closed += (_, __) =>
                {
                    _isJobsSynchronizationNotificationActive = false;
                };
            }
        }

        #endregion

        #region Color Profile Request

        private void ExternalBridgeService_ColorProfileRequest(object sender, ColorProfileRequestEventArgs e)
        {
            InvokeUI(async () =>
            {
                var vm = await NotificationProvider.ShowDialog<ColorProfileReceivedViewVM>(new ColorProfileReceivedViewVM()
                {
                    Color = Color.FromRgb(
                        (byte)e.Request.DetectionColor.R,
                        (byte)e.Request.DetectionColor.G,
                        (byte)e.Request.DetectionColor.B),
                });

                if (vm.DialogResult)
                {
                    e.Approve();
                    AddNewJob(vm.Color);
                }
                else
                {
                    e.Decline();
                }
            });
        }

        #endregion
    }
}