aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/PPC/Modules/Tango.PPC.JobsV2/ViewModels/JobViewVM.cs
blob: 2ffa84992b4db9d7c15f8cf8dbabeb6b3b707fec (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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
using RealTimeGraphX.DataPoints;
using RealTimeGraphX.WPF;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Xml.Serialization;
using Tango.BL.Entities;
using Tango.Core.Commands;
using Tango.MachineStudio.Technician.Editors;
using Tango.MachineStudio.Technician.Helpers;
using Tango.SharedUI.Helpers;

namespace Tango.MachineStudio.Technician.TechItems
{
    /// <summary>
    /// Represents a multi channel real-time graph item.
    /// </summary>
    /// <seealso cref="Tango.MachineStudio.Technician.TechItems.TechItem" />
    [TechItem(5)]
    public class MultiGraphItem : TechItem
    {
        private DispatcherTimer _timer;
        private DateTime _recording_start_time;

        public event Action RecordingStarted;
        public event Action RecordingStopped;

        private TechMonitor _techMonitor;
        /// <summary>
        /// Gets or sets the db tech monitor.
        /// </summary>
        [XmlIgnore]
        public TechMonitor TechMonitor
        {
            get { return _techMonitor; }
            set
            {
                TechMonitor old = _techMonitor;

                _techMonitor = value;
                RaisePropertyChangedAuto();

                ItemGuid = value != null ? value.Guid : null;

                TechName = _techMonitor != null ? _techMonitor.Description : null;
            }
        }

        private MultiGraphElementEditor _editor;
        /// <summary>
        /// Gets or sets the item editor.
        /// </summary>
        [XmlIgnore]
        public MultiGraphElementEditor Editor
        {
            get { return _editor; }
            set
            {
                _editor = value;
                SetEditorSettings();
            }
        }

        private int _duration;
        public int Duration
        {
            get { return _duration; }
            set
            {
                _duration = value;
                RaisePropertyChangedAuto();
                SetEditorSettings();
            }
        }
        private double _min;
        /// <summary>
        /// Gets or sets the minimum graph value.
        /// </summary>
        public double Min
        {
            get { return _min; }
            set
            {
                _min = value; RaisePropertyChangedAuto();
                SetEditorSettings();
            }
        }

        private double _max;
        /// <summary>
        /// Gets or sets the maximum graph value.
        /// </summary>
        public double Max
        {
            get { return _max; }
            set
            {
                _max = value; RaisePropertyChangedAuto();
                SetEditorSettings();
            }
        }

        private bool _useAutoRange;
        /// <summary>
        /// Gets or sets a value indicating whether [use automatic range].
        /// </summary>
        public bool UseAutoRange
        {
            get { return _useAutoRange; }
            set
            {
                _useAutoRange = value; RaisePropertyChangedAuto();
                SetEditorSettings();
            }
        }

        private int _decimalPlaces;
        /// <summary>
        /// Gets or sets the decimal places of y-axis.
        /// </summary>
        public int DecimalPlaces
        {
            get { return _decimalPlaces; }
            set { _decimalPlaces = value; RaisePropertyChangedAuto(); }
        }

        private bool _isPaused;
        /// <summary>
        /// Gets or sets a value indicating whether this instance is paused.
        /// </summary>
        [XmlIgnore]
        public bool IsPaused
        {
            get { return _isPaused; }
            set
            {
                _isPaused = value; RaisePropertyChangedAuto();
                SetEditorSettings();
            }
        }

        private bool _isRecording;
        /// <summary>
        /// Gets or sets a value indicating whether this instance is recording.
        /// </summary>
        [XmlIgnore]
        public bool IsRecording
        {
            get { return _isRecording; }
            set { _isRecording = value; RaisePropertyChangedAuto(); }
        }

        private TimeSpan _recordingTime;
        [XmlIgnore]
        public TimeSpan RecordingTime
        {
            get { return _recordingTime; }
            set { _recordingTime = value; RaisePropertyChangedAuto(); }
        }

        [XmlIgnore]
        public RelayCommand ClearCommand { get; set; }

        [XmlIgnore]
        public RelayCommand ToggleRecordingCommand { get; set; }

        /// <summary>
        /// Gets or sets the reset minimum maximum to default command.
        /// </summary>
        [XmlIgnore]
        public RelayCommand ResetMinMaxToDefaultCommand { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="MultiGraphItem"/> class.
        /// </summary>
        public MultiGraphItem() : base()
        {
            _useAutoRange = true;
            DecimalPlaces = 1;
            _duration = 10;
            _timer = new DispatcherTimer();
            _timer.Tick += _timer_Tick;
            _timer.Interval = TimeSpan.FromSeconds(1);

            Max = 100;
            Name = "Multi Channel Graph";
            Description = "Multi channel real-time graph";
            Image = ResourceHelper.GetImageFromResources("Images/multi-graph.png");

            ClearCommand = new RelayCommand(() => 
            {
                if (Editor != null)
                {
                    Editor.InnerGraph.Controller.Clear();
                }
            });

            ToggleRecordingCommand = new RelayCommand(ToggleRecording);

            ResetMinMaxToDefaultCommand = new RelayCommand(() =>
            {
                if (TechMonitor != null)
                {
                    Min = TechMonitor.Min;
                    Max = TechMonitor.Max;
                }
            });
        }

        private void SetEditorSettings()
        {
            if (Editor != null)
            {
                var controller = Editor.InnerGraph.Controller as WpfGraphController<TimeSpanDataPoint,DoubleDataPoint>;

                controller.Range.MaximumX = new TimeSpanDataPoint(TimeSpan.FromSeconds(_duration));
                controller.Range.MinimumY = new DoubleDataPoint(_min);
                controller.Range.MaximumY = new DoubleDataPoint(_max);
                controller.Range.AutoY = _useAutoRange;
                controller.IsPaused = _isPaused;
            }
        }

        private void OnTechMonitorChanged()
        {
            if (TechMonitor != null)
            {
                Min = TechMonitor.Min;
                Max = TechMonitor.Max;
            }
        }

        private void _timer_Tick(object sender, EventArgs e)
        {
            RecordingTime = DateTime.Now - _recording_start_time;
        }

        private void ToggleRecording()
        {
            if (!IsRecording)
            {
                RecordingStarted?.Invoke();
            }
            else
            {
                RecordingStopped?.Invoke();
            }
        }

        public void StartRecording()
        {
            _recording_start_time = DateTime.Now;
            IsRecording = true;
            _timer.Start();
        }

        public void StopRecording()
        {
            _timer.Stop();
            IsRecording = false;
            RecordingTime = TimeSpan.FromSeconds(0);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MultiGraphItem"/> class.
        /// </summary>
        /// <param name="techMonitor">The db tech monitor.</param>
        public MultiGraphItem(TechMonitor techMonitor) : this()
        {
            TechMonitor = techMonitor;
        }

        /// <summary>
        /// Clones this instance.
        /// </summary>
        /// <returns></returns>
        public override TechItem Clone()
        {
            MultiGraphItem cloned = base.Clone() as MultiGraphItem;
            cloned.TechMonitor = TechMonitor;
            cloned.Min = Min;
            cloned.Max = Max;
            cloned.UseAutoRange = UseAutoRange;
            cloned.Duration = Duration;
            cloned.DecimalPlaces = DecimalPlaces;
            return cloned;
        }
    }
}
d='n1602' href='#n1602'>1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.BL;
using Tango.BL.Entities;
using Tango.PPC.Common;
using Tango.PPC.Jobs.Messages;
using System.Data.Entity;
using Tango.Core.Commands;
using System.Windows;
using Tango.Touch.Controls;
using System.Windows.Media;
using Tango.DragAndDrop;
using System.ComponentModel;
using System.Windows.Data;
using Tango.PPC.Jobs.Dialogs;
using Tango.PPC.Jobs.Views;
using System.Runtime.InteropServices;
using System.Threading;
using Tango.SharedUI.Helpers;
using Tango.PPC.Common.Navigation;
using Tango.PPC.Jobs.NavigationObjects;
using Tango.PPC.Jobs.ViewContracts;
using System.Collections.ObjectModel;
using Tango.PPC.Common.Models;
using Tango.Logging;
using Tango.PPC.Common.Messages;
using Tango.BL.Builders;
using Tango.PPC.Jobs.AppButtons;
using Tango.Core.Threading;
using System.Diagnostics;
using System.Runtime.ExceptionServices;
using Tango.Explorer;
using Tango.PPC.Storage;
using System.IO;
using Tango.ColorConversion;
using Tango.Integration.Operation;
using Tango.BL.Enumerations;
using Tango.PPC.Common.Lubrication;
using Tango.PPC.Jobs.Models;
using Tango.Core;
using Tango.PPC.Jobs.UndoRedoCommands;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Tango.PPC.Jobs.ColorCorrectionTool;
using Tango.PPC.Common.Printing;
using Tango.Core.ExtensionMethods;
using Tango.PPC.Common.Resume;

namespace Tango.PPC.Jobs.ViewModels
{
    /// <summary>
    /// Represents the selected job view model.
    /// </summary>
    /// <seealso cref="Tango.PPC.Common.PPCViewModel" />
    public class JobViewVM : PPCViewModel<IJobView>, INavigationObjectReceiver<JobNavigationObject>, INavigationBlocker
    {
        private ObservablesContext _db;
        private bool _can_navigate_back;
        private bool _not_show_warning;
        //private Thread _check_gamut_thread;
        private Job _job_to_load;
        private JobNavigationIntent _job_to_load_intent;
        private static Dictionary<String, List<FineTuneItem>> _jobs_fine_tune_items;
        public static StartPrintingButton _start_printing_btn;
        private ActionTimer _volumeConversionTimer;
        private IColorConverter _converter;
        private string _current_job_string;
        private bool startingJob = false;
        private List<ColorCatalog> _catalogs;
        private string _jsonJobModelLoaded;

        #region Properties

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

        private JobModel _jobModel;
        public JobModel JobModel
        {
            get { return _jobModel; }
            set
            {
                _jobModel = value;
                RaisePropertyChangedAuto();
            }
        }

        public bool CanEdit
        {
            get {
                return Job != null && 
                    (!MachineProvider.MachineOperator.IsPrinting 
                        || MachineProvider.MachineOperator.RunningJob == null 
                        || MachineProvider.MachineOperator.RunningJob.Guid != Job.Guid) 
                    && !HasResumeModel;
            }
        }

        public bool CanDropResume
        {
            get
            {
                return Job != null && HasResumeModel &&
                    (!MachineProvider.MachineOperator.IsPrinting
                      ||  (MachineProvider.MachineOperator.RunningJob != null
                        && MachineProvider.MachineOperator.RunningJob.Guid != Job.Guid));
            }
        }

        private ICollectionView _segmentsCollectionView;
        /// <summary>
        /// Gets or sets the job segments collection view.
        /// </summary>
        public ICollectionView SegmentsCollectionView
        {
            get { return _segmentsCollectionView; }
            set
            {
                _segmentsCollectionView = value;
                RaisePropertyChangedAuto();
            }
        }

        private List<ColorSpace> _colorSpaces;
        /// <summary>
        /// Gets or sets the available color spaces.
        /// </summary>
        public List<ColorSpace> ColorSpaces
        {
            get { return _colorSpaces; }
            set { _colorSpaces = value; RaisePropertyChangedAuto(); }
        }

        private List<Rml> _rmls;
        /// <summary>
        /// Gets or sets the available RMLS.
        /// </summary>
        public List<Rml> Rmls
        {
            get { return _rmls; }
            set { _rmls = value; RaisePropertyChangedAuto(); }
        }

        private Rml _selectedRML;
        /// <summary>
        /// Gets or sets the selected RML.
        /// </summary>
        public Rml SelectedRML
        {
            get { return _selectedRML; }
            set { _selectedRML = value; RaisePropertyChangedAuto(); OnSelectedRmlChanged(); }
        }

        private List<SpoolType> _spoolTypes;
        /// <summary>
        /// Gets or sets the available spool types.
        /// </summary>
        public List<SpoolType> SpoolTypes
        {
            get { return _spoolTypes; }
            set { _spoolTypes = value; RaisePropertyChangedAuto(); }
        }

        private List<Customer> _customers;
        /// <summary>
        /// Gets or sets the available customers.
        /// </summary>
        public List<Customer> Customers
        {
            get { return _customers; }
            set { _customers = value; RaisePropertyChangedAuto(); }
        }

        private String _customersFilter;
        /// <summary>
        /// Gets or sets the customers filter.
        /// </summary>
        public String CustomersFilter
        {
            get { return _customersFilter; }
            set { _customersFilter = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the customers automatic complete provider.
        /// </summary>
        public AutoCompleteProvider<Customer> CustomersAutoCompleteProvider { get; set; }

        private ObservableCollection<FineTuneItem> _fineTuneItems;
        /// <summary>
        /// Gets or sets the fine tune items.
        /// </summary>
        public ObservableCollection<FineTuneItem> FineTuneItems
        {
            get { return _fineTuneItems; }
            set { _fineTuneItems = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<FineTuneItem> _approvalFineTuneItems;
        /// <summary>
        /// Gets or sets the fine tune items.
        /// </summary>
        public ObservableCollection<FineTuneItem> ApprovalFineTuneItems
        {
            get { return _approvalFineTuneItems; }
            set { _approvalFineTuneItems = value; RaisePropertyChangedAuto(); }
        }

        private List<ColorCatalog> _availableCatalogs;
        public List<ColorCatalog> AvailableCatalogs
        {
            get { return _availableCatalogs; }
            set { _availableCatalogs = value; RaisePropertyChangedAuto(); }
        }



        private bool _isFullMode;

        public bool IsFullMode
        {
            get { return _isFullMode; }
            set
            {
                _isFullMode = value;
                RaisePropertyChangedAuto();
            }
        }

        private bool _isBasicMode;

        public bool IsBasicMode
        {
            get { return _isBasicMode; }
            set
            {
                if (_isBasicMode != value)
                {
                    if (value == true && JobModel != null && JobModel.Segments.Count > 0)
                    {
                        var firstSegment = JobModel.Segments.Where(x => x.SegmentIndex == 1).FirstOrDefault();
                        if (JobModel.Segments.Count > 1 || firstSegment.IsGradient)
                        {
                            SetOrDiscardAll();
                            return;
                        }
                        JobModel.EnableInterSegment = false;
                    }
                    _isBasicMode = value;
                    RaisePropertyChangedAuto();
                }
            }
        }

        private bool _isSummaryOpened;

        public bool IsSummaryOpened
        {
            get { return _isSummaryOpened; }
            set
            {
                _isSummaryOpened = value;
                RaisePropertyChangedAuto();
            }
        }
        private bool _isWeigthView;

        public bool IsWeightView
        {
            get { return _isWeigthView; }
            set
            {
                if (_isWeigthView != value)
                {
                    _isWeigthView = value;

                    RaisePropertyChangedAuto();
                }
            }
        }
        
        private JobResumeModel _resumeModel;
        public JobResumeModel ResumeModel
        {
            get { return _resumeModel; }
            set { _resumeModel = value; RaisePropertyChangedAuto(); RaisePropertyChanged(nameof(HasResumeModel)); RaisePropertyChanged(nameof(CanEdit)); }
        }

        public bool HasResumeModel
        {
            get { return ResumeModel != null; }
        }

        #endregion

        #region Commands

        /// <summary>
        /// Gets or sets the add solid segment command.
        /// </summary>
        public RelayCommand<ISegmentModel> AddNewSegmentCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove segment command.
        /// </summary>
        public RelayCommand<SegmentModel> RemoveSegmentCommand { get; set; }

        /// <summary>
        /// Gets or sets the copy segment command.
        /// </summary>
        public RelayCommand<SegmentModel> DuplicateSegmentCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove job command.
        /// </summary>
        public RelayCommand<SegmentModel> AddColorCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove job command.
        /// </summary>
        public RelayCommand<BrushStopModel> EditColorCommand { get; set; }

        /// <summary>
        /// Gets or sets the replace brush stop command.
        /// </summary>
        public RelayCommand<BrushStop> ReplaceBrushStopCommand { get; set; }

        /// <summary>
        /// Gets or sets the dye command.
        /// </summary>
        public RelayCommand DyeCommand { get; set; }

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

        public RelayCommand EditJobDetailsCommand { get; set; }

        public RelayCommand RepeatUnitsCommand { get; set; }

        public RelayCommand JobModeSwitchCommand { get; set; }

        public RelayCommand<SegmentsGroupModel> UngroupSegmentsCommand { get; set; }
        public RelayCommand<SegmentsGroupModel> DeleteSegmentsGroupCommand { get; set; }
        public RelayCommand<SegmentsGroupModel> RepeatSegmentsGroupCommand { get; set; }

        public RelayCommand NavigateBackToJobs { get; set; }

        public RelayCommand DropResumeCommand { get; set; }

        #endregion

        #region collapsed mode commands

        public RelayCommand InsertWhiteGapCommand { get; set; }
        public RelayCommand ReverseCommand { get; set; }
        public RelayCommand DeleteSegmentCommand { get; set; }
        public RelayCommand RepeateSegmentCommand { get; set; }
        public RelayCommand PasteCommand { get; set; }
        public RelayCommand CopyCommand { get; set; }
        public RelayCommand UndoCommand { get; set; }
        public RelayCommand RedoCommand { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes the <see cref="JobViewVM"/> class.
        /// </summary>
        static JobViewVM()
        {

            _jobs_fine_tune_items = new Dictionary<string, List<FineTuneItem>>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="JobViewVM"/> class.
        /// </summary>
        public JobViewVM()
        {
            _converter = new DefaultColorConverter();

            _volumeConversionTimer = new ActionTimer(TimeSpan.FromMilliseconds(50));

            RegisterForMessage<JobSelectedMessage>(HandleJobSelectedMessage);

            FineTuneItems = new ObservableCollection<FineTuneItem>();
            ApprovalFineTuneItems = new ObservableCollection<FineTuneItem>();

            CustomersAutoCompleteProvider = new AutoCompleteProvider<Customer>((customer, filter) =>
            {
                return customer.Name.ToLower().StartsWith(filter != null ? filter.ToLower() : String.Empty);
            });


            //Initialize Commands
            AddNewSegmentCommand = new RelayCommand<ISegmentModel>(AddNewSegment);
            RemoveSegmentCommand = new RelayCommand<SegmentModel>(RemoveSegment);
            DuplicateSegmentCommand = new RelayCommand<SegmentModel>(DuplicateSegment);

            AddColorCommand = new RelayCommand<SegmentModel>(AddColor);
            EditColorCommand = new RelayCommand<BrushStopModel>(EditColor);

            DyeCommand = new RelayCommand(StartJob, CanStartJob);

            ExportEmbroideryCommand = new RelayCommand(ExportEmbroidery);
            RepeatUnitsCommand = new RelayCommand(RepeatUnits);

            EditJobDetailsCommand = new RelayCommand(EditJobDetails);
            JobModeSwitchCommand = new RelayCommand(JobModeSwitch);
            InsertWhiteGapCommand = new RelayCommand(InsertWhiteGap);
            ReverseCommand = new RelayCommand(Reverse);
            DeleteSegmentCommand = new RelayCommand(DeleteSegments);
            DeleteSegmentsGroupCommand = new RelayCommand<SegmentsGroupModel>(DeleteSegmentsGroup);
            RepeateSegmentCommand = new RelayCommand(RepeateSegments);
            UngroupSegmentsCommand = new RelayCommand<SegmentsGroupModel>(UngroupSegments);
            RepeatSegmentsGroupCommand = new RelayCommand<SegmentsGroupModel>(RepeatSegmentsGroup);
            PasteCommand = new RelayCommand(Paste);
            CopyCommand = new RelayCommand(Copy);
            UndoCommand = new RelayCommand(Undo);//(x) => { return UndoRedoManager.Instance.IsEnableUndoOperation(); }
            RedoCommand = new RelayCommand(Redo);//(x) => { return UndoRedoManager.Instance.IsEnableRedoOperation();}
            DropResumeCommand = new RelayCommand(DropResume, (x)=> CanDropResume);


            NavigateBackToJobs = new RelayCommand(NavigateBack);
            IsFullMode = true;
            IsSummaryOpened = true;
            _not_show_warning = false;
        }

        #endregion

        #region Job Management

        /// <summary>
        /// Loads the job.
        /// </summary>
        private async void LoadJob()
        {
            try
            {
                if (!(_job_to_load == null || (_job_to_load != null && Job != null && _job_to_load.Guid == Job.Guid)))
                {
                    //View.ScrollToTop();

                    LogManager.Log($"Loading selected job '{_job_to_load.Name}'...");
                    IsFree = false;

                    _can_navigate_back = false;

                    if (_db != null)
                    {
                        if (Job != null)
                        {
                            //Job.RmlChanged -= OnRmlChanged;
                            Job.NameChanged -= Job_NameChanged;
                        }

                        if (Rmls != null)
                        {
                            Rmls.Where(x => x.Cct != null && x.Cct.Data != null).ToList().ForEach(x => x.Cct.Data = null);
                            Rmls.ForEach(x => x.Cct = null);

                            if (SelectedRML != null)
                            {
                                SelectedRML.Cct = null;
                                SelectedRML = null;
                            }

                            Rmls = null;
                        }

                        _db.Dispose();
                        GC.Collect();
                    }

                    _db = ObservablesContext.CreateDefault();

                    _catalogs = await new CatalogsCollectionBuilder(_db)
                        .SetAll()
                        .WithGroups()
                        .WithItems()
                        .ForSite(MachineProvider.Machine.SiteGuid)
                        .BuildListAsync();

                    Job = await new JobBuilder(_db).Set(_job_to_load.Guid)
                        .WithConfiguration()
                        .WithUser()
                        .WithSegments()
                        .WithBrushStops()
                        .WithSegmentsGroups()
                        .BuildAsync();

                    try
                    {
                        ResumeModel = JobResumeManager.GetJobResumeModel(Job.Guid);

                        if (ResumeModel != null)
                        {
                            LogManager.Log($"Job resume info found:\n{ResumeModel.ToJsonString()}");
                        }
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error retrieving job resume info from db.");
                    }

                    RaisePropertyChanged(nameof(CanEdit));
                     DropResumeCommand.RaiseCanExecuteChanged();

                    Job.NameChanged -= Job_NameChanged;
                    Job.NameChanged += Job_NameChanged;

                    Job.ValidateOnPropertyChanged = true;

                    //GetLubricationLevel();
                    //await SetSpoolTension(Job.Rml);

                    LogManager.Log("Loading RMLS...");
                    Rmls = (await new RmlsCollectionBuilder(_db).SetAll().WithSpools().ForHeadType(MachineProvider.Machine.MachineHeadType).ForSite(MachineProvider.Machine.SiteGuid).BuildAsync()).OrderBy(x => x.FinalName).ToList();
                    //Rmls = (await new RmlsCollectionBuilder(_db).SetAll().WithSpools().BuildAsync()).OrderBy(x => x.FinalName).ToList();
                    LogManager.Log("Loading Color Spaces...");
                    ColorSpaces = await _db.ColorSpaces.Where(x => x.Code != (int)BL.Enumerations.ColorSpaces.CMYK).ToListAsync();
                    LogManager.Log("Loading Spool Types...");
                    SpoolTypes = await _db.SpoolTypes.ToListAsync();
                    LogManager.Log("Loading Customers...");
                    Customers = await _db.Customers.Where(x => x.OrganizationGuid == MachineProvider.Machine.OrganizationGuid).ToListAsync();

                    AvailableCatalogs = await new CatalogsCollectionBuilder(_db).SetAll().WithGroups().WithItems().ForSite(MachineProvider.Machine.SiteGuid).BuildListAsync();

                    _selectedRML = Job.Rml;
                    RaisePropertyChanged(nameof(SelectedRML));

                    await LoadRML(_selectedRML);
                    if (BuildProvider.IsEureka && Job.Segments.Count == 1 && Job.Segments[0].BrushStops.Count <= 1)
                    {
                        IsBasicMode = true;
                    }
                    else IsBasicMode = false;
                    IsWeightView = false;

                    LoadJobModel();

                    _job_to_load = null;

                    _current_job_string = Job.ToJobFileWhenLoaded().ToString();
                }

                if (!_jobs_fine_tune_items.ContainsKey(Job.Guid) && Job.JobFineTuningStatus == BL.Enumerations.FineTuningStatuses.PendingApproval)
                {
                    Job.JobFineTuningStatus = BL.Enumerations.FineTuningStatuses.Unspecified;
                }

                LogManager.Log($"Job editing state = '{Job.JobEditingState}'.");

                //if (Job.JobEditingState == BL.Enumerations.EditingStates.SampleDye && Job.JobSampleDyeStatus == BL.Enumerations.SampleDyeStatuses.PendingApproval)
                //{
                //    LogManager.Log("Directing view to display sample dye region.");
                //    View.DisplaySampleDye();
                //}
                //else if (Job.JobEditingState == BL.Enumerations.EditingStates.FineTuning && Job.JobFineTuningStatus == BL.Enumerations.FineTuningStatuses.PendingApproval)
                //{
                //    LogManager.Log("Directing view to display fine tuning region.");
                //    View.DisplayFineTuning();
                //}

                IsFullMode = true;
                DyeCommand.RaiseCanExecuteChanged();
            }
            catch (Exception ex)
            {
                IsFree = true;
                LogManager.Log(ex, $"Error loading job '{(_job_to_load != null ? _job_to_load.Name : "null")}'");
                await NotificationProvider.ShowError("An error occurred while trying to load the selected job.");
                _can_navigate_back = true;
                await NavigationManager.NavigateBack();
            }
            finally
            {
                IsFree = true;
            }
        }

        private void LoadJobModel()
        {
            SpoolType PPCSpoolType = null;

            if (Settings.SpoolTypeGuid.IsNotNullOrEmpty())
            {
                PPCSpoolType = _spoolTypes.FirstOrDefault(x => x.Guid == Settings.SpoolTypeGuid);
            }
            else
            {
                PPCSpoolType = _spoolTypes.FirstOrDefault(x => x.Type == BL.Enumerations.SpoolTypes.StandardSpool);
            }

            if (Job.SpoolType.Type == BL.Enumerations.SpoolTypes.FlatSpool)
            {
                PPCSpoolType = _spoolTypes.FirstOrDefault(x => x.Type == BL.Enumerations.SpoolTypes.FlatSpool);
            }

            var jobModel = new JobModel(ColorSpaces, BuildProvider.IsEureka)
            {
                Name = Job.Name,
                Guid = Job.Guid,
                ID = Job.ID,
                CreationDate = Job.CreationDate,
                LengthPercentageFactor = Job.LengthPercentageFactor,
                NumberOfUnits = Job.NumberOfUnits,
                IsAllSegmentsPerSpool = Job.IsAllSegmentsPerSpool,
                Rml = Job.Rml,
                ColorSpace = Job.ColorSpace,
                SpoolType = Job.SpoolType,
                SpoolTypeName = PPCSpoolType.Name,
                User = Job.User,
                Machine = Job.Machine,
                JobType = Job.JobType,
                InterSegmentLength = Job.InterSegmentLength,
                EnableInterSegment = Job.EnableInterSegment,
                NumberSpools = (BuildProvider.IsEureka ? Job.NumberOfSpools : 1),
                Copies = (BuildProvider.IsEureka ? Job.NumberOfUnits * Job.NumberOfSpools : Job.NumberOfUnits)
            };
            Dictionary<string, SegmentsGroupModel> guidToGroup = new Dictionary<string, SegmentsGroupModel>();
            if (Job.Version < 2)
            {
                int segmentindex = 1;
                foreach (var segm in Job.OrderedSegments)
                {
                    if (segm.BrushStops.Count > 1)
                    {
                        var brushes = segm.BrushStops;
                        Segment currentSegment = segm;
                        double lengthOfOldSegment = segm.Length;
                        for (int index = 0; index < (brushes.Count - 1); index++)
                        {
                            SegmentModel csegmentModel = new SegmentModel(jobModel, segm.Guid)
                            {
                                Name = segm.Name,
                                Length = segm.Length,
                                SegmentIndex = segm.SegmentIndex,
                                IsInterSegment = segm.IsInterSegment,
                                Job = jobModel
                            };

                            csegmentModel.SegmentIndex = segmentindex++;

                            brushes[index].Segment = currentSegment;
                            brushes[index + 1].Segment = currentSegment;
                            csegmentModel.Length = (lengthOfOldSegment * (brushes[index + 1].OffsetPercent - brushes[index].OffsetPercent) / 100d);
                            BrushStopModel brushStopModelFirst = new BrushStopModel(brushes[index], csegmentModel, 1);
                            BrushStopModel brushStopModelSecond = new BrushStopModel(brushes[index + 1], csegmentModel, 1);
                            csegmentModel.CreateGradientBrushes(brushStopModelFirst, brushStopModelSecond);

                            jobModel.Segments.Add(csegmentModel);
                        }
                    }
                    else
                    {
                        SegmentModel segmentModel = LoadSegmentModel(segm, jobModel);
                        segmentModel.SegmentIndex = segmentindex++;
                        jobModel.Segments.Add(segmentModel);
                    }
                }

            }
            else
            {
                var segments = Job.OrderedSegmentsWithGroups;
                foreach (var segment in segments)
                {
                    if (segment is Segment simpleSegment)
                    {
                        SegmentModel segmentModel = LoadSegmentModel(simpleSegment, jobModel);
                        jobModel.Segments.Add(segmentModel);
                    }
                    else if (segment is SegmentsGroup group)
                    {
                        SegmentsGroupModel segmentsGroupModel = new SegmentsGroupModel(jobModel) { SegmentIndex = group.SegmentIndex, Repeats = group.Repeats };

                        foreach (var innerSegment in group.OrderedSegments)
                        {
                            SegmentModel segmentModel = LoadSegmentModel(innerSegment, jobModel);
                            jobModel.Segments.Add(segmentModel);
                            segmentsGroupModel.Segments.Add(segmentModel);
                            segmentModel.SegmentsGroupModel = segmentsGroupModel;
                        }
                        jobModel.SegmentsGroups.Add(segmentsGroupModel);
                    }
                }
            }

            jobModel.InterSegmentLength = Job.EnableInterSegment ? Job.InterSegmentLength : 0;
            jobModel.LoadGroupingSegments();
            JobModel = jobModel;
            //create grouping
            SegmentsCollectionView = CollectionViewSource.GetDefaultView(JobModel.GroupingSegments);
            SegmentsCollectionView.SortDescriptions.Add(new SortDescription(nameof(SegmentModel.SegmentIndex), ListSortDirection.Ascending));
            UndoRedoManager.Instance.ClearAll();
            ArrangeSegmentsIndixes();
            JsonSerializerSettings settings = new JsonSerializerSettings()
            {
                ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
                PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects
            };
            _jsonJobModelLoaded = JsonConvert.SerializeObject(JobModel, Formatting.Indented, settings);
        }

        /// <summary>
        /// Loads the segment model.
        /// </summary>
        /// <param name="segm">The segm.</param>
        /// <param name="jobModel">The job model.</param>
        /// <returns></returns>
        private SegmentModel LoadSegmentModel(Segment segm, JobModel jobModel)
        {
            SegmentModel segmentModel = new SegmentModel(jobModel, segm.Guid)
            {
                Name = segm.Name,
                Length = segm.Length,
                SegmentIndex = segm.SegmentIndex,
                IsInterSegment = segm.IsInterSegment,
                Job = jobModel,
            };
            foreach (var brushStop in segm.BrushStops)
            {
                BrushStopModel brushStopModel = new BrushStopModel(brushStop, segmentModel, Job.Version);
                segmentModel.BrushStops.Add(brushStopModel);
            }

            segmentModel.ArrangeBrushStopsPosition();
            segmentModel.UpdateMiddleColorBrush();
            return segmentModel;
        }

        private void Job_NameChanged(object sender, string e)
        {
            DyeCommand.RaiseCanExecuteChanged();
        }

        /// <summary>
        /// Starts the job.
        /// </summary>
        private async void StartJob()
        {
            if (startingJob) return;

            try
            {
                Debug.WriteLine("Job Starting...");
                startingJob = true;
                LogManager.Log("Start job command pressed. Starting job and navigating to job progress view...");
                await Save();

                var printConfig = new PrintingConfiguration();

                if (HasResumeModel)
                {
                    printConfig.FirstUnitStartPosition = ResumeModel.FirstUnitStartPosition;
                    printConfig.GlobalStartPosition = ResumeModel.GlobalStartPosition;
                    printConfig.RemainingUnits = ResumeModel.RemainingUnits;
                    //LogManager.Log($"!!!!!  Start Job resume :    FirstUnitStartPosition = {ResumeModel.FirstUnitStartPosition}   GlobalStartPosition = {ResumeModel.GlobalStartPosition}    RemainingUnits = {ResumeModel.RemainingUnits}", LogCategory.Debug);

                }

                var handler = await PrintingManager.Print(Job, _db, printConfig);

                RaisePropertyChanged(nameof(CanEdit));
                DropResumeCommand.RaiseCanExecuteChanged();

                if (!BuildProvider.IsEureka)
                {
                    await NavigationManager.NavigateTo<JobsV2Module>(nameof(JobProgressView));
                }

                startingJob = false;
            }
            catch (InsufficientLiquidQuantityException)
            {
                //Ignore..
            }
            catch (OperationCanceledException)
            {
                //Ignore..
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Could not start the current job.");
                await NotificationProvider.ShowError($"{ex.Message}.");
            }
            finally
            {
                startingJob = false;
            }
        }

        /// <summary>
        /// Determines whether this instance [can start job].
        /// </summary>
        private bool CanStartJob()
        {
            try
            {
                //var test = JobModel != null && !JobModel.Segments.SelectMany(x => x.BrushStops).Where(x => x.Position == BrushStopModel.PositionStatus.FirstColor || x.Position == BrushStopModel.PositionStatus.SecondColor).ToList().Exists(x => x.IsOutOfGamut);
                //return Job != null && Job.Validate(_db) && !Job.Segments.SelectMany(x => x.BrushStops).Where(x => !x.IsTransparent && !x.IsWhite).ToList().Exists(x => x.IsOutOfGamut || (x.BrushColorSpace == BL.Enumerations.ColorSpaces.Volume && x.IsLiquidVolumesOutOfRange));
                return JobModel != null && !JobModel.Segments.ToList().Exists(x => x.BrushStops.Count == 0);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                return false;
            }
        }

        private async void EditJobDetails()
        {
            try
            {
                LogManager.Log("Editing the job details.");
                JobCreationViewVM vm = new JobCreationViewVM(_spoolTypes.ToList(), _rmls, JobModel.Rml.Guid, JobModel.InterSegmentLength, true);
                vm.UseFlatSpool = Job.SpoolType.Type == BL.Enumerations.SpoolTypes.FlatSpool;
                vm.JobName = JobModel.Name;
                vm.SelectedSpoolType = JobModel.SpoolType;
                vm = await NotificationProvider.ShowDialog<JobCreationViewVM>(vm);

                if (!vm.DialogResult) return;

                Job.Name = vm.JobName;
                JobModel.Name = vm.JobName;
                if (vm.UseFlatSpool)
                {
                    Job.SpoolType = _spoolTypes.FirstOrDefault(x => x.Type == BL.Enumerations.SpoolTypes.FlatSpool);
                }
                else
                {
                    if (Settings.SpoolTypeGuid.IsNotNullOrEmpty())
                    {
                        Job.SpoolType = _spoolTypes.FirstOrDefault(x => x.Guid == Settings.SpoolTypeGuid);
                    }
                    else
                    {
                        Job.SpoolType = _spoolTypes.FirstOrDefault(x => x.Type == BL.Enumerations.SpoolTypes.StandardSpool);
                    }
                }

                JobModel.SpoolType = Job.SpoolType;
                JobModel.SpoolTypeName = Job.SpoolType.Name;

                Job.InterSegmentLength = vm.WhiteGap;
                Job.EnableInterSegment = vm.WhiteGap > 0;
                JobModel.InterSegmentLength = vm.WhiteGap;
                JobModel.EnableInterSegment = vm.WhiteGap > 0;

                //   Job.SpoolType = vm.SelectedSpoolType;
                //  JobModel.SpoolType = vm.SelectedSpoolType;//update  length!!!!

                if (vm.SelectedRML.Guid != SelectedRML.Guid)
                {
                    SelectedRML = _rmls.FirstOrDefault(x => x.Guid == vm.SelectedRML.Guid);
                    // for LAB color remove all available tests
                    foreach (var segment in JobModel.OrderedSegmentsWithGroups)
                    {
                        if (segment is SegmentModel innerSegment)
                        {
                            foreach (var stop in innerSegment.BrushStops.OrderBy(x => x.StopIndex).ToList())
                            {
                                if (stop.ColorSpace == Tango.BL.Enumerations.ColorSpaces.LAB)
                                {
                                    var testColor = TrialsLogEngine.Default.GetByBrushStopGuid(stop.Guid);
                                    if (testColor != null && testColor.TrialslogList.Count > 0)
                                    {
                                        TrialsLogEngine.Default.Delete(testColor);
                                    }
                                }
                            }
                        }
                        else if (segment is SegmentsGroupModel group)
                        {
                            foreach (var segm_group in group.Segments.OrderBy(x => x.SegmentIndex))
                            {
                                foreach (var stop in segm_group.BrushStops.OrderBy(x => x.StopIndex).ToList())
                                {
                                    if (stop.ColorSpace == Tango.BL.Enumerations.ColorSpaces.LAB)
                                    {
                                        var testColor = TrialsLogEngine.Default.GetByBrushStopGuid(stop.Guid);
                                        if (testColor != null && testColor.TrialslogList.Count > 0)
                                        {
                                            TrialsLogEngine.Default.Delete(testColor);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                if (vm.IsDuplicate)
                {
                    try
                    {
                        await Save();
                        //Duplicate new job
                        var cloned = Job.Clone();
                        cloned.JobIndex = (_db.Jobs.Max(x => x.JobIndex) + 1);
                        _db.Jobs.Add(cloned);
                        await _db.SaveChangesAsync();
                        _job_to_load = cloned;
                        LoadJob();
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error duplicate job.");
                    }
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error editing the job.");
            }
        }

        private async void RepeatUnits()
        {
            var maxLength = Job.SpoolType.Length;
            var maxRep = (maxLength == 0 ? 999 : (maxLength / JobModel.Length));

            var vm = await NotificationProvider.ShowDialog<RepeatJobViewVM>(new RepeatJobViewVM("Repeat All", JobModel.NumberOfUnits)
            {
                MaxRepeations = (int)maxRep
            });

            if (vm.DialogResult)
            {
                JobModel.NumberOfUnits = vm.Repeats;
            }
        }

        #endregion

        #region RML Changed

        private async void OnSelectedRmlChanged()
        {
            await LoadRML(SelectedRML);
        }

        private async Task LoadRML(Rml rml)
        {
            if (rml != null)
            {
                if (Job.Rml != rml || rml.Cct == null)
                {
                    bool updateRML = Job.Rml != rml;

                    Job.Rml = await new RmlBuilder(_db)
                        .Set(rml.Guid)
                        .WithActiveParametersGroup()
                        .WithCCT()
                        .WithGbdAndLub()
                        .WithCAT(MachineProvider.Machine.Guid)
                        .WithLiquidFactors()
                        .WithSpools()
                        .BuildAsync();
                    if (JobModel != null)
                    {
                        JobModel.Rml = Job.Rml;
                    }

                    if (updateRML && JobModel != null)
                    {
                        NotificationProvider.SetGlobalBusyMessage("Updating IsOutOfGammut due to the change RML...");
                        foreach (var segment in JobModel.OrderedSegmentsWithGroups)
                        {
                            if (segment is SegmentModel innerSegment)
                            {
                                innerSegment.UpdateWeightOnRMLChange(IsWeightView);
                                innerSegment.UpdateBrushStops();

                            }
                            else if (segment is SegmentsGroupModel group)
                            {
                                foreach (var segm in group.Segments)
                                {
                                    segm.UpdateWeightOnRMLChange(IsWeightView);
                                    segm.UpdateBrushStops();
                                }
                            }
                        }

                        DyeCommand.RaiseCanExecuteChanged();
                        NotificationProvider.ReleaseGlobalBusyMessage();
                    }
                    //if (updateVolumes)
                    //{
                    //    NotificationProvider.SetGlobalBusyMessage("Updating job liquid volumes...");
                    //    foreach (var stop in Job.Segments.SelectMany(x => x.BrushStops).Where(x => x.BrushColorSpace == BL.Enumerations.ColorSpaces.RGB || x.BrushColorSpace == BL.Enumerations.ColorSpaces.LAB).ToList())
                    //    {
                    //        try
                    //        {
                    //            var output = await _converter.ConvertAsync(stop, false, false);
                    //            output.ApplyOnBrushStopVolumesOnly(stop);
                    //        }
                    //        catch (Exception ex)
                    //        {
                    //            LogManager.Log(ex, $"Error updating stop volumes after changing thread on segment {stop.Segment.SegmentIndex}, stop {stop.StopIndex}.");
                    //        }
                    //    }
                    //    NotificationProvider.ReleaseGlobalBusyMessage();
                    //}
                }
            }
        }

        #endregion

        #region Segments Management

        /// <summary>
        /// Adds a new  segment.
        /// </summary>
        private void AddNewSegment(ISegmentModel segment)
        {
            try
            {
                LogManager.Log("Adding new segment...");

                UndoRedoManager.Instance.InsertAndExecuteCommand(new AddNewSegmentCommand(JobModel, segment, Settings.DefaultSegmentLength > 0 ? Settings.DefaultSegmentLength : 10));
                ArrangeSegmentsIndixes();
                DyeCommand.RaiseCanExecuteChanged();
                if (segment.SegmentIndex >= (JobModel.GroupingSegments.Count() - 1))
                {
                    View.ScrollToEnd();
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Could not add a new segment.");
                NotificationProvider.ShowError("An error occurred while trying to add a new segment.");
            }
        }

        private async void RepeatSegmentsGroup(SegmentsGroupModel group)
        {
            var maxLength = Job.SpoolType.Length == 0 ? 999 : Job.SpoolType.Length;
            var maxRep = (maxLength - JobModel.Length) / group.Length;

            var vm = await NotificationProvider.ShowDialog<RepeatJobViewVM>(new RepeatJobViewVM($"Edit \"Group {group.SegmentIndex}\" Repeat", group.Repeats)
            {
                MaxRepeations = (int)maxRep
            });

            if (vm.DialogResult)
            {
                group.Repeats = vm.Repeats;
            }
        }

        /// <summary>
        /// Removes the segment.
        /// </summary>
        private async void RemoveSegment(SegmentModel segment)
        {
            if (JobModel.Segments.Count > 1)
            {
                try
                {
                    if (await NotificationProvider.ShowQuestion("Are you sure you want to remove the selected segment?"))
                    {
                        UndoRedoManager.Instance.InsertAndExecuteCommand(new RemoveSegmentCommand(JobModel, segment));
                        ArrangeSegmentsIndixes();

                        DyeCommand.RaiseCanExecuteChanged();
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Could not remove segment.");
                    await NotificationProvider.ShowError("An error occurred while trying to delete segment.");
                }
            }
            else
            {
                await NotificationProvider.ShowInfo("A job must contain at least one color segment.");
            }
        }

        private async void DeleteSegmentsGroup(SegmentsGroupModel segmentsGroup)
        {
            if (JobModel.GroupingSegments.Count > 1)
            {
                try
                {
                    if (await NotificationProvider.ShowQuestion("Are you sure you want to remove the selected group of segments?"))
                    {

                        UndoRedoManager.Instance.InsertAndExecuteCommand(new DeleteSegmentsGroupCommand(JobModel, segmentsGroup));
                        ArrangeSegmentsIndixes();

                        DyeCommand.RaiseCanExecuteChanged();
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Could not remove group of segments.");
                    await NotificationProvider.ShowError("An error occurred while trying to delete group of segments.");
                }
            }
            else
            {
                await NotificationProvider.ShowInfo("A job must contain at least one color segment.");
            }
        }
        /// <summary>
        /// Duplicates the segment.
        /// </summary>
        private void DuplicateSegment(SegmentModel segment)
        {
            UndoRedoManager.Instance.InsertAndExecuteCommand(new DuplicateSegmentCommand(JobModel, segment));
            ArrangeSegmentsIndixes();

            DyeCommand.RaiseCanExecuteChanged();
            if (segment.SegmentIndex >= (JobModel.GroupingSegments.Count() - 1))
            {
                View.ScrollToEnd();
            }
        }

        private void ArrangeSegmentsIndixes()
        {
            JobModel.ArrangeSegmentsIndixes();
            SegmentsCollectionView.Refresh();
        }

        #endregion

        #region Brush Stops Management

        /// <summary>
        /// Click on AddColor button. Add newBrush;
        /// </summary>
        private async void AddColor(SegmentModel segment)
        {
            LogManager.Log($"Adding new color to segment {segment.SegmentIndex}.");

            if ((segment.IsGradient && segment.Length < 10) || (segment.HasColors && segment.Length < 5))
            {
                await NotificationProvider.ShowInfo("Color transitions are best visible with segment length of 5 meters and above.");
            }

            var vm = await NotificationProvider.ShowDialog<ColorSelectionViewVM>(new ColorSelectionViewVM()
            {
                DialogEditObject = new ColorSelectionViewVM.DialogObject()
                {
                    SelectedSegment = segment,
                    BrushStopForEdit = new BrushStopModel(segment),
                    IsEditingMode = false,
                    Catalogs = _catalogs
                }
            });

            if (vm.DialogResult)
            {
                AddBrushStop(segment, vm.SelectedBrushStop);
                DyeCommand.RaiseCanExecuteChanged();
            }
            // SetSegmentLiquidVolumes(segment);
        }

        /// <summary>
        /// Click on Edit Color button. Add newBrush;
        /// </summary>
        private async void EditColor(BrushStopModel brushStop)
        {
            if (brushStop == null)
            {
                await NotificationProvider.ShowError("The edit brush is null!");
                return;
            }
            SegmentModel segment = brushStop.SegmentModel;
            LogManager.Log($"Edit brush stop.");

            var vm = await NotificationProvider.ShowDialog<ColorSelectionViewVM>(new ColorSelectionViewVM()
            {
                DialogEditObject = new ColorSelectionViewVM.DialogObject()
                {
                    SelectedSegment = segment,
                    BrushStopForEdit = brushStop,
                    IsEditingMode = true,
                    Catalogs = _catalogs
                }
            });


            if (vm.DialogResult)
            {
                UndoRedoManager.Instance.InsertAndExecuteCommand(new EditBrushStopColorCommand(segment, brushStop, vm.SelectedBrushStop));
                DyeCommand.RaiseCanExecuteChanged();

            }
        }

        /// <summary>
        /// Adds the brush stop.
        /// </summary>
        public async void AddBrushStop(SegmentModel segment, BrushStopModel newBrushStop)
        {
            if (newBrushStop == null || segment == null)
                return;

            if (segment.IsGradient)
            {
                if (false == _not_show_warning)
                {
                    var vm = await NotificationProvider.ShowDialog<AddSegmentWarningDialogVM>(new AddSegmentWarningDialogVM());
                    if (vm.NotShow)
                    {
                        _not_show_warning = true;
                    }
                }
            }
            UndoRedoManager.Instance.InsertAndExecuteCommand(new AddBrushStopCommand(JobModel, segment, newBrushStop));
            ArrangeSegmentsIndixes();

        }
        #endregion

        #region Job Selection Message

        /// <summary>
        /// Handles the job selected message.
        /// </summary>
        /// <param name="message">The message.</param>
        private void HandleJobSelectedMessage(JobSelectedMessage message)
        {
            _job_to_load = message.Job;
        }

        #endregion

        #region Sample Dye

        /// <summary>
        /// Starts a sample dye.
        /// </summary>
        //private async void StartSampleDye()
        //{
        //    try
        //    {
        //        LogManager.Log("Sample dye command pressed...");

        //        await PrintingManager.PrintSample(Job, _db);

        //        await NavigationManager.NavigateTo<JobsV2Module>(nameof(JobProgressView));
        //    }
        //    catch (Exception ex)
        //    {
        //        LogManager.Log(ex, $"Error executing sample dye for job {Job.Name}.");
        //        await NotificationProvider.ShowError(ex.Message);
        //    }
        //}


        #endregion

        #region Export Embroidery

        private async void ExportEmbroidery()
        {
            try
            {
                if (!StorageProvider.IsConnected)
                {
                    await NotificationProvider.ShowError("No storage device connected.");
                    return;
                }

                var result = await NavigationManager.
                  NavigateForResult<StorageModule,
                  Storage.Views.MainView, ExplorerFileItem,
                  Storage.Models.StorageNavigationRequest>(
                  new Storage.Models.StorageNavigationRequest()
                  {
                      Intent = Storage.Models.StorageNavigationIntent.SaveFile,
                      DefaultFileName = Job.Name + Path.GetExtension(Job.EmbroideryFileName),
                      Filter = Path.GetExtension(Job.EmbroideryFileName),
                      Title = "Export Embroidery File",
                  });

                if (result != null)
                {
                    File.WriteAllBytes(Path.HasExtension(result.Path) ? result.Path : result.Path + Path.GetExtension(Job.EmbroideryFileName), Job.EmbroideryFileData);
                    await NotificationProvider.ShowSuccess("Embroidery file exported successfully.");
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error exporting embroidery file.");
                await NotificationProvider.ShowError("An error occurred while trying to save the selected embroidery file.");
            }
        }

        #endregion

        #region IPPC ViewModel Overrides

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

            MachineProvider.MachineOperator.PrintingEnded += MachineOperator_PrintingEnded;
            MachineProvider.MachineOperator.PrintingStarted += MachineOperator_PrintingStarted;

            JobResumeManager.JobResumeUpdated += JobResumeManager_JobResumeUpdated;
            JobResumeManager.JobResumeDropped += JobResumeManager_JobResumeDropped;
        }

        /// <summary>
        /// Called when the navigation system has navigated to this VM view.
        /// </summary>
        public override void OnNavigatedTo()
        {
            if (!MachineProvider.MachineOperator.IsPrinting)
            {
                _start_printing_btn.Push();
            }

            base.OnNavigatedTo();
            LoadJob();
        }



        public override void OnBeforeNavigatedTo()
        {
            base.OnBeforeNavigatedTo();
        }

        public override void OnBeforeNavigatedFrom()
        {
            base.OnBeforeNavigatedFrom();
        }

        /// <summary>
        /// Called when the navigation system has navigated from this VM view.
        /// </summary>
        public override void OnNavigatedFrom()
        {
            _start_printing_btn.Pop();

            base.OnNavigatedFrom();
            _job_to_load_intent = JobNavigationIntent.Default;
        }

        public override void OnNavigatedTo(PPCViewModel fromVM)
        {
            base.OnNavigatedTo(fromVM);
        }

        /// <summary>
        /// Called before the navigation system navigates back from this object.
        /// Return false to abort the navigation.
        /// </summary>
        /// <returns></returns>
        public async override Task<bool> OnNavigateBackRequest()
        {
            if (!IsFree) return false;
            try
            {
                if (!_can_navigate_back)
                {
                    await Save();

                    Job = null;
                    JobModel = null;
                    SegmentsCollectionView = null;
                }
                return true;
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error saving job to database. " + ex.InnerException.Message);
                await NotificationProvider.ShowError("Error saving the current job.");
                return true;
            }
        }

        private async void NavigateBack()
        {
            if (IsFree)
            {
                if (await OnNavigateBackRequest())
                {
                    LogManager.Log("Back command to Jobs pressed.");
                    await NavigationManager.NavigateTo<JobsV2Module>(nameof(JobsView));
                }

            }
        }

        public override void OnApplicationReady()
        {
            base.OnApplicationReady();

            _start_printing_btn = new StartPrintingButton(DyeCommand, MachineProvider.MachineOperator);
        }
        #endregion

        #region INavigationObjectReceiver

        public void OnNavigatedToWithObject(JobNavigationObject e)
        {
            _job_to_load_intent = e.Intent;
            _job_to_load = e.Job;
        }

        #endregion

        #region collapsed mode

        private void JobModeSwitch()
        {
            IsFullMode = !IsFullMode;
            //UndoRedoManager.Instance.ClearAll(); According to QA! request - #6314
        }

        private void InsertWhiteGap()
        {
            JobModel.InsertWhiteGapToSelectedSegments();
        }

        private void Reverse()
        {
            if (false == JobModel.GroupingSegments.ToList().Any(x => x.IsSelected))
                return;

            UndoRedoManager.Instance.InsertAndExecuteCommand(new ReverseCommand(JobModel));

            ArrangeSegmentsIndixes();

        }

        private async void DeleteSegments()
        {
            if (!JobModel.HasSelectedItems)
                return;
            if (JobModel.GroupingSegments.ToList().Where(x => x.IsSelected).ToList().Count == JobModel.GroupingSegments.Count)
            {
                await NotificationProvider.ShowInfo("A job must contain at least one segment. Please, change selection.");
                return;
            }

            if (JobModel.GroupingSegments.Count > 1)
            {
                try
                {
                    if (await NotificationProvider.ShowQuestion("Are you sure you want to remove these selected segments?"))
                    {
                        UndoRedoManager.Instance.InsertAndExecuteCommand(new RemoveSegmentsCommand(JobModel));

                        ArrangeSegmentsIndixes();

                        DyeCommand.RaiseCanExecuteChanged();
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Could not remove segments.");
                    await NotificationProvider.ShowError("An error occurred while trying to delete segments.");
                }
            }
            else
            {
                await NotificationProvider.ShowInfo("A job must contain at least one color segment.");
            }
        }

        private void RepeateSegments()
        {
            if ((JobModel.GroupingSegments.ToList().Where(x => x.IsSelected).Count()) < 2)
                return;
            UndoRedoManager.Instance.InsertAndExecuteCommand(new RepeatCommand(JobModel));
            ArrangeSegmentsIndixes();
        }

        private void UngroupSegments(SegmentsGroupModel segmentsGroup)
        {
            UndoRedoManager.Instance.InsertAndExecuteCommand(new UnGroupSegmentsCommand(JobModel, segmentsGroup));
            ArrangeSegmentsIndixes();
        }

        private void Paste()
        {
            UndoRedoManager.Instance.InsertAndExecuteCommand(new PasteSegmentsCommand(JobModel));
            ArrangeSegmentsIndixes();
        }

        private void Copy()
        {
            UndoRedoManager.Instance.InsertAndExecuteCommand(new CopySegmentCommand(JobModel));
            ArrangeSegmentsIndixes();
        }

        private void Undo()
        {
            UndoRedoManager.Instance.Undo();
            ArrangeSegmentsIndixes();
            DyeCommand.RaiseCanExecuteChanged();
        }

        private void Redo()
        {
            UndoRedoManager.Instance.Redo();
            ArrangeSegmentsIndixes();
            DyeCommand.RaiseCanExecuteChanged();
        }

        private void SetOrDiscardAll()
        {
            InvokeUI(async () =>
            {
                IsBasicMode = await MessageDiscardAllChanges();
            });
        }

        private async Task<bool> MessageDiscardAllChanges()
        {
            try
            {
                MessageDiscardAdvancedOptionsVM vm = new MessageDiscardAdvancedOptionsVM();
                vm = await NotificationProvider.ShowDialog<MessageDiscardAdvancedOptionsVM>(vm);

                if (vm.DialogResult)
                {
                    //delete all 
                    var segments = JobModel.OrderedSegmentsWithGroups;
                    var firstSegment = JobModel.Segments.Where(x => x.SegmentIndex == 1).FirstOrDefault();
                    firstSegment.SegmentsGroupModel = null;
                    JobModel.GroupingSegments.Clear();
                    JobModel.Segments.Clear();
                    JobModel.Segments.Add(firstSegment);
                    JobModel.GroupingSegments.Add(firstSegment);
                    JobModel.EnableInterSegment = false;
                    JobModel.InterSegmentLength = 0;
                    if (firstSegment.IsGradient)
                    {
                        BrushStopModel firstbrush = firstSegment.FirstBrushStop;
                        firstSegment.BrushStops.Clear();
                        firstSegment.BrushStops.Add(firstbrush);
                        firstSegment.ArrangeBrushStopsIndexes();
                    }

                    return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }

        #endregion

        #region Save from models to db

        private async Task Save()
        {
            if (JobModel == null)
                return;
            UndoRedoManager.Instance.ClearAll();

            var colorSpaces = await _db.ColorSpaces.ToListAsync();

            Job.ColorSpace = colorSpaces.FirstOrDefault();
            Job.Version = 2;
            Job.Name = JobModel.Name;
            Job.NumberOfUnits = JobModel.NumberOfUnits;
            Job.EnableInterSegment = JobModel.InterSegmentLength > 0;
            Job.InterSegmentLength = JobModel.InterSegmentLength;

            if (Job.SpoolType.Type == BL.Enumerations.SpoolTypes.FlatSpool)
            {
                Job.SpoolType = _spoolTypes.FirstOrDefault(x => x.Code == (int)BL.Enumerations.SpoolTypes.FlatSpool);
            }
            else if (Settings.SpoolTypeGuid != null)
            {
                Job.SpoolType = _spoolTypes.FirstOrDefault(x => x.Guid == Settings.SpoolTypeGuid);
            }
            else
            {
                Job.SpoolType = _spoolTypes.FirstOrDefault(x => x.Code == (int)BL.Enumerations.SpoolTypes.StandardSpool);
            }
            Job.NumberOfSpools = JobModel.NumberSpools;

            var oldSegments = Job.Segments.ToList();

            foreach (var segment in Job.OrderedSegmentsWithGroups.ToList())
            {
                if (segment is Segment)
                {
                    Segment simplesegment = segment as Segment;
                    simplesegment.BrushStops.ToList().ForEach(x =>
                    {
                        _db.BrushStops.Remove(x);
                    });
                    _db.Segments.Remove(simplesegment);
                }
                else if (segment is SegmentsGroup)
                {
                    SegmentsGroup segmentsGroup = segment as SegmentsGroup;
                    foreach (var groupSegment in segmentsGroup.Segments.ToList())
                    {
                        groupSegment.BrushStops.ToList().ForEach(x =>
                        {
                            _db.BrushStops.Remove(x);
                        });
                        _db.Segments.Remove(groupSegment);
                    }
                    _db.SegmentsGroups.Remove(segmentsGroup);
                }
            }

            Job.Segments.Clear();

            Dictionary<int, SegmentsGroup> segmentIndexToGroup = new Dictionary<int, SegmentsGroup>();

            //foreach (var segment in JobModel.Segments.OrderBy(x => x.SegmentIndex).ToList())
            foreach (var segment in JobModel.OrderedSegmentsWithGroups)
            {
                if (segment is SegmentModel innerSegment)
                {
                    var dbSegment = new Segment();
                    dbSegment.Guid = System.Guid.NewGuid().ToString();
                    dbSegment.Name = "Standard Segment";

                    dbSegment.Job = Job;
                    dbSegment.SegmentIndex = segment.SegmentIndex;
                    dbSegment.Length = segment.Length;
                    _db.Segments.Add(dbSegment);
                    List<BrushStop> brushStopList = new List<BrushStop>();//to prevent collection changed event and override offset in timer
                    foreach (var stop in innerSegment.BrushStops.OrderBy(x => x.StopIndex).ToList())
                    {
                        var dbStop = new BrushStop();
                        dbStop.Guid = stop.Guid;
                        dbStop.Segment = dbSegment;
                        //_db.BrushStops.Add(dbStop);
                        brushStopList.Add(dbStop);

                        dbStop.ColorSpace = colorSpaces.FirstOrDefault(x => x.Code == (int)stop.ColorSpace);
                        dbStop.Red = stop.Red;
                        dbStop.Green = stop.Green;
                        dbStop.Blue = stop.Blue;
                        dbStop.L = stop.L;
                        dbStop.A = stop.A;
                        dbStop.B = stop.B;
                        dbStop.Cyan = stop.Cyan;
                        dbStop.Magenta = stop.Magenta;
                        dbStop.Yellow = stop.Yellow;
                        dbStop.Black = stop.Black;
                        dbStop.BestMatchR = stop.BestMatchColor.R;
                        dbStop.BestMatchG = stop.BestMatchColor.G;
                        dbStop.BestMatchB = stop.BestMatchColor.B;

                        dbStop.OffsetPercent = stop.OffsetPercent;
                        dbStop.StopIndex = stop.StopIndex;
                        dbStop.IsOutOfGamut = stop.IsOutOfGamut;

                        dbStop.SetVolume(LiquidTypes.Cyan, stop.Cyan);
                        dbStop.SetVolume(LiquidTypes.Magenta, stop.Magenta);
                        dbStop.SetVolume(LiquidTypes.Yellow, stop.Yellow);
                        dbStop.SetVolume(LiquidTypes.Black, stop.Black);

                        dbStop.ColorCatalog = stop.ColorCatalog;
                        dbStop.ColorCatalogsItem = stop.ColorCatalogsItem;
                    }
                    _db.BrushStops.AddRange(brushStopList);
                }
                else if (segment is SegmentsGroupModel group)
                {
                    SegmentsGroup dbSegmentsGroup = new SegmentsGroup();
                    dbSegmentsGroup.Guid = System.Guid.NewGuid().ToString();
                    dbSegmentsGroup.Repeats = group.Repeats;
                    dbSegmentsGroup.SegmentIndex = group.SegmentIndex;
                    dbSegmentsGroup.Job = Job;
                    _db.SegmentsGroups.Add(dbSegmentsGroup);
                    foreach (var segm_group in group.Segments.OrderBy(x => x.SegmentIndex))
                    {
                        var dbSegment = new Segment();
                        dbSegment.Guid = System.Guid.NewGuid().ToString();
                        dbSegment.Name = "Standard Segment";

                        dbSegment.Job = Job;
                        dbSegment.SegmentIndex = segm_group.SegmentIndex;
                        dbSegment.Length = segm_group.Length;
                        dbSegment.SegmentsGroupGuid = dbSegmentsGroup.Guid;
                        _db.Segments.Add(dbSegment);
                        List<BrushStop> brushStopList = new List<BrushStop>();//to prevent collection changed event and override offset in timer
                        foreach (var stop in segm_group.BrushStops.OrderBy(x => x.StopIndex).ToList())
                        {
                            var dbStop = new BrushStop();
                            dbStop.Segment = dbSegment;
                            //_db.BrushStops.Add(dbStop);
                            brushStopList.Add(dbStop);

                            dbStop.ColorSpace = colorSpaces.FirstOrDefault(x => x.Code == (int)stop.ColorSpace);
                            dbStop.Red = stop.Red;
                            dbStop.Green = stop.Green;
                            dbStop.Blue = stop.Blue;
                            dbStop.L = stop.L;
                            dbStop.A = stop.A;
                            dbStop.B = stop.B;
                            dbStop.Cyan = stop.Cyan;
                            dbStop.Magenta = stop.Magenta;
                            dbStop.Yellow = stop.Yellow;
                            dbStop.Black = stop.Black;
                            dbStop.BestMatchR = stop.BestMatchColor.R;
                            dbStop.BestMatchG = stop.BestMatchColor.G;
                            dbStop.BestMatchB = stop.BestMatchColor.B;

                            dbStop.OffsetPercent = stop.OffsetPercent;
                            dbStop.StopIndex = stop.StopIndex;
                            dbStop.IsOutOfGamut = stop.IsOutOfGamut;

                            dbStop.SetVolume(LiquidTypes.Cyan, stop.Cyan);
                            dbStop.SetVolume(LiquidTypes.Magenta, stop.Magenta);
                            dbStop.SetVolume(LiquidTypes.Yellow, stop.Yellow);
                            dbStop.SetVolume(LiquidTypes.Black, stop.Black);

                            dbStop.ColorCatalog = stop.ColorCatalog;
                            dbStop.ColorCatalogsItem = stop.ColorCatalogsItem;
                        }
                        _db.BrushStops.AddRange(brushStopList);
                    }
                }
            }
            Job.LastUpdated = DateTime.UtcNow;
            Job.IsSynchronized = false;

            if (Job.JobStatus != JobStatuses.Draft)
            {
                JsonSerializerSettings settings = new JsonSerializerSettings()
                {
                    ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
                    PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects
                };
                string jsonJobModelSaved = JsonConvert.SerializeObject(JobModel, Formatting.Indented, settings);
                var json1 = JObject.Parse(_jsonJobModelLoaded);
                var json2 = JObject.Parse(jsonJobModelSaved);
                if (false == JToken.DeepEquals(json1, json2))
                    Job.JobStatus = BL.Enumerations.JobStatuses.Draft;
            }

            RaiseMessage(new JobSavedMessage() { Job = Job });

            await _db.SaveChangesAsync();

            JsonSerializerSettings settings1 = new JsonSerializerSettings()
            {
                ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
                PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects
            };
            _jsonJobModelLoaded = JsonConvert.SerializeObject(JobModel, Formatting.Indented, settings1);

        }

        #endregion

        #region Machine Operator Events

        private void MachineOperator_PrintingStarted(object sender, PrintingEventArgs e)
        {
            RaisePropertyChanged(nameof(CanEdit));
            InvokeUI( () =>
            {
                DropResumeCommand.RaiseCanExecuteChanged();
            });
        }

        private void MachineOperator_PrintingEnded(object sender, Integration.Operation.PrintingEventArgs e)
        {
            if (IsVisible)
            {
                _start_printing_btn.Push();
            }

            RaisePropertyChanged(nameof(CanEdit));
            InvokeUI(() =>
            {
                DropResumeCommand.RaiseCanExecuteChanged();
            });
        }


        #endregion

        #region Resume

        private void JobResumeManager_JobResumeUpdated(object sender, Common.Resume.JobResumeUpdatedEventArgs e)
        {
            if (Job.Guid == e.JobGuid)
            {
                ResumeModel = e.ResumeModel;
            }
        }

        private void JobResumeManager_JobResumeDropped(object sender, Common.Resume.JobResumeDroppedEventArgs e)
        {
            if (Job.Guid == e.JobGuid)
            {
                ResumeModel = null;
            }
        }

        private async void DropResume()
        {
            if (Job != null && HasResumeModel)
            {
                if (await NotificationProvider.ShowQuestion("Are you sure you want to Edit the Job?" + System.Environment.NewLine + "Job progress data will reset."))
                {
                    JobResumeManager.DropResume(Job.Guid);
                }
            }
        }

        #endregion
    }
}