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;
namespace Tango.PPC.Jobs.ViewModels
{
///
/// Represents the jobs list view model.
///
///
public class JobsViewVM : PPCViewModel
{
private ObservablesContext _db; //Holds the db context for the job list.
private ObservableCollection _catalogs; //Holds the available color catalogs.
public enum JobsCategory
{
Draft,
History
}
#region Properties
private ObservableCollection _jobs;
///
/// Gets or sets the collection of jobs.
///
public ObservableCollection Jobs
{
get { return _jobs; }
set { _jobs = value; RaisePropertyChangedAuto(); }
}
private ICollectionView _draftJobsCollectionView;
///
/// Gets or sets the jobs collection view.
///
public ICollectionView DraftJobsCollectionView
{
get { return _draftJobsCollectionView; }
set
{
_draftJobsCollectionView = value;
RaisePropertyChangedAuto();
}
}
private ICollectionView _historyJobsCollectionView;
///
/// Gets or sets the jobs collection view.
///
public ICollectionView HistoryJobsCollectionView
{
get { return _historyJobsCollectionView; }
set
{
_historyJobsCollectionView = value;
RaisePropertyChangedAuto();
}
}
private ObservableCollection _selectedJobs;
///
/// Gets or sets the selected jobs.
///
public ObservableCollection SelectedJobs
{
get { return _selectedJobs; }
set { _selectedJobs = value; RaisePropertyChangedAuto(); }
}
private bool _isLoadingJobs;
///
/// Gets or sets a value indicating whether this instance is loading jobs.
///
public bool IsLoadingJobs
{
get { return _isLoadingJobs; }
set { _isLoadingJobs = value; RaisePropertyChangedAuto(); }
}
private bool _isMultiSelecting;
///
/// Gets or sets a value indicating whether this the jobs list is in multi select mode.
///
public bool IsMultiSelecting
{
get { return _isMultiSelecting; }
set { _isMultiSelecting = value; RaisePropertyChangedAuto(); }
}
private int _selectedCategoryIndex;
///
/// Gets or sets the index of the selected category.
///
public int SelectedCategoryIndex
{
get { return _selectedCategoryIndex; }
set
{
_selectedCategoryIndex = value;
RaisePropertyChangedAuto();
RaisePropertyChanged(nameof(SelectedCategory));
}
}
///
/// Gets or sets the selected category.
///
public JobsCategory SelectedCategory
{
get { return (JobsCategory)SelectedCategoryIndex; }
set
{
if (SelectedCategoryIndex != value.ToInt32())
{
SelectedCategoryIndex = value.ToInt32();
Filter = null;
}
}
}
private String _filter;
///
/// Gets or sets the search filter.
///
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
///
/// Gets or sets the job selected command.
///
public RelayCommand JobSelectedCommand { get; set; }
///
/// Gets or sets the job drag and drop command.
///
public RelayCommand JobDragedDroppedCommand { get; set; }
///
/// Gets or sets the clear selection command.
///
public RelayCommand ClearSelectionCommand { get; set; }
///
/// Gets or sets the add job command.
///
public RelayCommand AddJobCommand { get; set; }
///
/// Gets or sets the delete jobs command.
///
public RelayCommand DeleteJobsCommand { get; set; }
///
/// Gets or sets the clone jobs command.
///
public RelayCommand CloneJobsCommand { get; set; }
///
/// Gets or sets the export job command.
///
public RelayCommand ExportJobCommand { get; set; }
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
public JobsViewVM()
{
Jobs = new ObservableCollection();
SelectedJobs = new ObservableCollection();
JobSelectedCommand = new RelayCommand((x) => SelectJob(x as Job));
JobDragedDroppedCommand = new RelayCommand((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(HandleJobRemovedMessage);
RegisterForMessage(HandleJobSavedMessage);
RegisterForMessage((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
///
/// Called when a job has been dragged and dropped into another job.
///
/// The dragged job.
/// The dropped job.
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
///
/// Selects the job.
///
/// The job.
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.Status == Integration.Operation.MachineStatuses.ReadyToDye)
{
await NavigationManager.NavigateWithObject(new JobSummeryNavigationObject()
{
Context = _db,
Job = job,
});
}
else
{
await NavigationManager.NavigateTo(nameof(JobView));
}
}
///
/// Loads the jobs from database.
///
public void LoadJobs(Action onCompleted = null)
{
try
{
LogManager.Log("Loading machine jobs...");
Task.Factory.StartNew(() =>
{
IsLoadingJobs = true;
Thread.Sleep(500);
_db = ObservablesContext.CreateDefault();
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(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(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.");
}
}
///
/// Clears the job selection.
///
public void ClearSelection()
{
SelectedJobs.Clear();
IsMultiSelecting = false;
}
///
/// Adds a new job.
///
private async void AddNewJob(Color? colorProfile = null, TwnFile twnFile = null)
{
try
{
LogManager.Log("Adding new job...");
var settings = SettingsManager.Default.GetOrCreate();
var machine = MachineProvider.Machine;
JobCreationViewVM vm = new JobCreationViewVM(
machine.SupportedJobTypes.Count > 0 ? machine.SupportedJobTypes : Enum.GetValues(typeof(JobTypes)).Cast().ToList(),
machine.SupportedColorSpaces.Count > 0 ? machine.SupportedColorSpaces : Enum.GetValues(typeof(ColorSpaces)).Cast().Where(x => x.IsUserSpace() || (ApplicationManager.IsInTechnicianMode && x == ColorSpaces.Volume)).ToList()
);
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 = machine.SupportedJobTypes.FirstOrDefault();
}
if (settings.LastJobColorSpace != null)
{
vm.SelectedColorSpace = settings.LastJobColorSpace.Value.IsUserSpace() ? settings.LastJobColorSpace.Value : ColorSpaces.Catalog;
}
else
{
var space = machine.SupportedColorSpaces.FirstOrDefault();
vm.SelectedColorSpace = space.IsUserSpace() ? space : ColorSpaces.Catalog;
}
if (colorProfile != null || twnFile != null)
{
vm.SupportedColorSpaces = new List() { ColorSpaces.RGB };
vm.SelectedColorSpace = vm.SupportedColorSpaces.First();
}
if (twnFile != null)
{
vm.SupportedJobTypes = new List() { JobTypes.Embroidery };
vm.SelectedJobType = vm.SupportedJobTypes.First();
}
if (twnFile == null)
{
if (machine.SupportedJobTypes.Count != 1 || machine.SupportedColorSpaces.Count != 1)
{
vm = await NotificationProvider.ShowDialog(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(catalogVM);
if (!catalogVM.DialogResult)
{
return;
}
}
}
}
else
{
vm.SelectedJobType = machine.SupportedJobTypes.First();
vm.SelectedColorSpace = machine.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.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.MachineGuid = MachineProvider.Machine.Guid;
job.UserGuid = AuthenticationProvider.CurrentUser.Guid;
job.RmlGuid = machine.DefaultRml != null ? machine.DefaultRmlGuid : Adapter.Rmls.FirstOrDefault().Guid;
job.WindingMethodGuid = Adapter.WindingMethods.FirstOrDefault().Guid;
job.SpoolTypeGuid = machine.DefaultSpoolType != null ? machine.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(machine.DefaultSegmentLength > 0 ? machine.DefaultSegmentLength : 100);
}
else
{
job.AddSolidSegment(colorProfile.Value, machine.DefaultSegmentLength > 0 ? machine.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(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.");
}
}
///
/// Deletes the specified jobs from db.
///
/// The jobs.
private async void DeleteJobs(ObservableCollection 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.");
}
}
///
/// Clones the specified jobs.
///
/// The jobs.
private async void CloneJobs(ObservableCollection jobs)
{
try
{
LogManager.Log($"Cloning selected jobs:\n{jobs.Select(x => x.Name).ToList().ToJsonString()}");
int index = Jobs.Max(x => x.JobIndex);
List clonedJobs = new List();
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.");
}
}
///
/// Called when the search filter has been changed
///
private void OnFilterChanged()
{
if (DraftJobsCollectionView != null && HistoryJobsCollectionView != null)
{
CollectionFilter.RaiseFilterChanged();
View.ScrollToTop();
}
}
#endregion
#region Message Handling
///
/// Handles the job removed message.
///
/// The MSG.
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");
}
}
///
/// Handles the job saved message.
///
/// The MSG.
private void HandleJobSavedMessage(JobSavedMessage msg)
{
LogManager.Log("JobSavedMessage message received.");
LoadJobs();
}
#endregion
#region Override Methods
///
/// Called when the application has been started.
///
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 c = ObservablesContext.CreateDefault())
{
_catalogs = (await c.ColorCatalogs.ToListAsync()).ToObservableCollection();
}
}
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;
ClearSelection();
var result = await NavigationManager.
NavigateForResult(
new Storage.Models.StorageNavigationRequest()
{
Intent = Storage.Models.StorageNavigationIntent.SaveFile,
DefaultFileName = selected_job.Name,
Filter = ExplorerFileDefinition.Job.Extension,
Title = "Save Job File",
});
if (result != null)
{
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}");
}
}
}
#endregion
#region Handle Job File Loading From Storage
private async void HandleJobFileLoaded(ExplorerFileItem jobFile)
{
var vm = await NotificationProvider.ShowDialog();
if (vm.DialogResult)
{
using (ObservablesContext jobContext = ObservablesContext.CreateDefault())
{
try
{
JobFile jFile = JobFile.Parser.ParseFrom(File.ReadAllBytes(jobFile.Path));
var job = await Job.FromJobFile(jFile, MachineProvider.Machine.Guid, AuthenticationProvider.CurrentUser.Guid);
jobContext.Jobs.Add(job);
await jobContext.SaveChangesAsync();
LoadJobs(() =>
{
if (vm.ImportAndEdit)
{
var postJob = Jobs.SingleOrDefault(x => x.Guid == job.Guid);
if (postJob != null)
{
SelectJob(postJob, true);
}
}
});
}
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}");
}
}
}
}
#endregion
#region Handle TCC File Loading From Storage
private async void HandleColorProfileFileLoaded(ExplorerFileItem tccFile)
{
try
{
DetectionColorFile tcc = DetectionColorFile.Parser.ParseFrom(File.ReadAllBytes(tccFile.Path));
var vm = await NotificationProvider.ShowDialog(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(ExplorerFileItem twnFile)
{
TwnFile twn = TwnFile.FromFile(twnFile.Path);
BitmapSource preview = twn.Thumbnail.ToBitmapSource();
var vm = await NotificationProvider.ShowDialog(new ImportTwnFileViewVM()
{
Thumbnail = preview,
});
if (vm.DialogResult)
{
AddNewJob(null, twn);
}
}
#endregion
#region Color Profile Request
private void ExternalBridgeService_ColorProfileRequest(object sender, ColorProfileRequestEventArgs e)
{
InvokeUI(async () =>
{
var vm = await NotificationProvider.ShowDialog(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
}
}