using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Tango.Scripting { internal static class ExtensionMethods { public static List ToLines(this String str) { //return str.Split(new[] { '\r', '\n' }).ToList(); if (str == null) return new List(); return str.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList(); } } }
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using Tango.Core.Commands;
using Tango.Core.Helpers;
using Tango.BL.Entities;
using Tango.MachineStudio.Common.Notifications;
using Tango.SharedUI;
using SimpleValidator.Extensions;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.MachineStudio.Common;
using Tango.BL;
using Tango.AutoComplete.Editors;
using System.Data.Entity;
using Tango.BL.Builders;
using Tango.MachineStudio.MachineDesigner.Contracts;
using System.Windows.Threading;
using Tango.Core.Threading;
using Tango.MachineStudio.RML.ViewModels;
using Tango.Settings;
using Tango.MachineStudio.RML.Models;
using Tango.BL.ActionLogs;
using Tango.MachineStudio.Common.Authentication;
using Tango.BL.DTO;

namespace Tango.MachineStudio.MachineDesigner.ViewModels
{
    public class MainViewVM : StudioViewModel<IMainView>
    {
        private INotificationProvider _notification;
        private IActionLogManager _actionLogManager;
        private IAuthenticationProvider _authentication;
        private ActionTimer _machines_action_timer;
        private ActionTimer _dispensers_action_timer;
        private MachineDTO _machineBeforeSave;
        private List<Site> _all_sites;

        #region Properties

        private bool _isNewMachine;
        public bool IsNewMachine
        {
            get { return _isNewMachine; }
            set { _isNewMachine = value; RaisePropertyChangedAuto(); }
        }

        private ObservablesStaticCollections _machinesAdapter;
        public ObservablesStaticCollections MachinesAdapter
        {
            get { return _machinesAdapter; }
            set { _machinesAdapter = value; RaisePropertyChangedAuto(); }
        }

        private ObservablesStaticCollections _activeMachineAdapter;
        public ObservablesStaticCollections ActiveMachineAdapter
        {
            get { return _activeMachineAdapter; }
            set { _activeMachineAdapter = value; RaisePropertyChangedAuto(); }
        }

        private Machine _selectedMachine;
        /// <summary>
        /// Gets or sets the selected machine from the drop down.
        /// </summary>
        public Machine SelectedMachine
        {
            get { return _selectedMachine; }
            set
            {
                if (_selectedMachine != value)
                {
                    _selectedMachine = value;
                    RaisePropertyChangedAuto();
                    InvalidateRelayCommands();
                }
            }
        }

        private Machine _activeMachine;
        /// <summary>
        /// Gets or sets the active machine.
        /// </summary>
        public Machine ActiveMachine
        {
            get { return _activeMachine; }
            set { _activeMachine = value; RaisePropertyChangedAuto(); }
        }

        private IdsPack _selectedIds;
        /// <summary>
        /// Gets or sets the selected ids pack.
        /// </summary>
        public IdsPack SelectedIds
        {
            get { return _selectedIds; }
            set { _selectedIds = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
        }

        private String _dispensersFilter;
        /// <summary>
        /// Gets or sets the dispensers filter.
        /// </summary>
        public String DispensersFilter
        {
            get { return _dispensersFilter; }
            set { _dispensersFilter = value; RaisePropertyChangedAuto(); OnDispensersFilterChanged(); }
        }

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

        private Spool _selectedSpool;
        public Spool SelectedSpool
        {
            get { return _selectedSpool; }
            set { _selectedSpool = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
        }

        private Site _selectedSite;
        public Site SelectedSite
        {
            get { return _selectedSite; }
            set { _selectedSite = value; RaisePropertyChangedAuto(); }
        }

        private List<Site> _sites;
        public List<Site> Sites
        {
            get { return _sites; }
            set { _sites = value; RaisePropertyChangedAuto(); }
        }


        private ColorCalibrationViewVM _colorCalibrationViewVM;
        public ColorCalibrationViewVM ColorCalibrationViewVM
        {
            get { return _colorCalibrationViewVM; }
            set { _colorCalibrationViewVM = value; RaisePropertyChangedAuto(); }
        }

        private HardwareConfigurationViewVM _hardwareConfigurationViewVM;
        public HardwareConfigurationViewVM HardwareConfigurationViewVM
        {
            get { return _hardwareConfigurationViewVM; }
            set
            {
                _hardwareConfigurationViewVM = value;
                RaisePropertyChangedAuto();
            }
        }

        private MachineUpdatesViewVM _machineUpdatesViewVM;
        public MachineUpdatesViewVM MachineUpdatesViewVM
        {
            get { return _machineUpdatesViewVM; }
            set { _machineUpdatesViewVM = value; RaisePropertyChangedAuto(); }
        }

        private TupViewVM _tupViewVM;
        public TupViewVM TupViewVM
        {
            get { return _tupViewVM; }
            set { _tupViewVM = value; RaisePropertyChangedAuto(); }
        }

        #endregion

        #region Commands

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

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

        /// <summary>
        /// Gets or sets the remove ids command.
        /// </summary>
        public RelayCommand RemoveIdsCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove machine command.
        /// </summary>
        public RelayCommand RemoveMachineCommand { get; set; }

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

        /// <summary>
        /// Gets or sets the edit machine command.
        /// </summary>
        public RelayCommand EditMachineCommand { get; set; }

        /// <summary>
        /// Gets or sets the back to machines command.
        /// </summary>
        public RelayCommand BackToMachinesCommand { get; set; }

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

        /// <summary>
        /// Gets or sets the remove spool command.
        /// </summary>
        public RelayCommand RemoveSpoolCommand { get; set; }

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

        /// <summary>
        /// Gets or sets the reset device registration command.
        /// </summary>
        public RelayCommand ResetDeviceRegistrationCommand { get; set; }

        /// <summary>
        /// Gets or sets the make prototype command.
        /// </summary>
        public RelayCommand MakePrototypeCommand { get; set; }

        #endregion

        #region Constructors

        public MainViewVM()
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MainViewVM"/> class.
        /// </summary>
        public MainViewVM(INotificationProvider notification, IAuthenticationProvider authentication, IActionLogManager actionLogManager)
        {
            MachinesAdapter = new ObservablesStaticCollections(ObservablesContext.CreateDefault());
            _notification = notification;
            _authentication = authentication;
            _actionLogManager = actionLogManager;

            _machines_action_timer = new ActionTimer(TimeSpan.FromMilliseconds(200));
            _dispensers_action_timer = new ActionTimer(TimeSpan.FromMilliseconds(200));

            AddIdsCommand = new RelayCommand(AddIds, (x) => ActiveMachine != null && ActiveMachine.Configuration != null && ActiveMachine.Configuration.IdsPacks.Count < 8);
            RemoveIdsCommand = new RelayCommand(RemoveIds, (x) => SelectedIds != null);
            EditMachineCommand = new RelayCommand(() => LoadSelectedMachine(), () => SelectedMachine != null);
            BackToMachinesCommand = new RelayCommand(() => View.NavigateTo(MachineDesignerNavigationView.MachinesView));
            SaveCommand = new RelayCommand(SaveMachine);
            AddMachineCommand = new RelayCommand(AddNewMachine);
            RemoveMachineCommand = new RelayCommand(RemoveSelectedMachine, () => SelectedMachine != null);
            AddSpoolCommand = new RelayCommand(AddNewSpool);
            RemoveSpoolCommand = new RelayCommand(RemoveSpool, () => SelectedSpool != null);
            CloneMachineCommand = new RelayCommand(CloneMachine, () => SelectedMachine != null);
            ResetDeviceRegistrationCommand = new RelayCommand(ResetDeviceRegistration);
            MakePrototypeCommand = new RelayCommand(MakePrototype);

            MachineUpdatesViewVM = new MachineUpdatesViewVM(_notification);
            TupViewVM = new TupViewVM(_notification);
        }

        #endregion

        #region Application Ready

        public override async void OnApplicationReady()
        {
            MachinesAdapter.MachineVersions = (await MachinesAdapter.Context.MachineVersions.ToListAsync()).ToObservableCollection();

        }

        #endregion

        private void OnDispensersFilterChanged()
        {
            if (!String.IsNullOrWhiteSpace(DispensersFilter))
            {
                _dispensers_action_timer.ResetReplace(() =>
                {
                    Task.Factory.StartNew(() =>
                    {
                        ActiveMachineAdapter.Dispensers = ActiveMachineAdapter.Context.Dispensers.Where(x => x.SerialNumber.ToLower().StartsWith(DispensersFilter.ToLower())).OrderBy(x => x.SerialNumber).ToSynchronizedObservableCollection();
                    });
                });
            }
        }

        #region Drag Drop Handlers

        /// <summary>
        /// Drops the ids pack.
        /// </summary>
        /// <param name="idsPack1">The ids pack1.</param>
        /// <param name="idsPack2">The ids pack2.</param>
        public void DropIdsPack(IdsPack idsPack1, IdsPack idsPack2)
        {
            ActiveMachine.Configuration.IdsPacks.Swap(idsPack1, idsPack2);
            ColorCalibrationViewVM.InvalidateCalibrationDataAndColorConversion();
        }

        /// <summary>
        /// Drops the touch panel.
        /// </summary>
        /// <param name="applicationDisplayPanelVersion">The application display panel version.</param>
        public void DropTouchPanel(ApplicationDisplayPanelVersion applicationDisplayPanelVersion)
        {
            ActiveMachine.Configuration.ApplicationDisplayPanelVersion = applicationDisplayPanelVersion;
            ActiveMachine.Configuration.ApplicationDisplayPanelVersionGuid = applicationDisplayPanelVersion.Guid;
        }

        /// <summary>
        /// Drops the application firmware version.
        /// </summary>
        /// <param name="applicationFirmwareVersion">The application firmware version.</param>
        public void DropApplicationFirmwareVersion(ApplicationFirmwareVersion applicationFirmwareVersion)
        {
            ActiveMachine.Configuration.ApplicationFirmwareVersion = applicationFirmwareVersion;
            ActiveMachine.Configuration.ApplicationFirmwareVersionGuid = applicationFirmwareVersion.Guid;
        }

        /// <summary>
        /// Drops the hardware version.
        /// </summary>
        /// <param name="hardwareVersion">The hardware version.</param>
        public void DropHardwareVersion(HardwareVersion hardwareVersion)
        {
            ActiveMachine.Configuration.HardwareVersion = hardwareVersion;
            ActiveMachine.Configuration.HardwareVersionGuid = hardwareVersion.Guid;
        }

        /// <summary>
        /// Drops the embedded firmware.
        /// </summary>
        /// <param name="embeddedFirmwareVersion">The embedded firmware version.</param>
        public void DropEmbeddedFirmware(EmbeddedFirmwareVersion embeddedFirmwareVersion)
        {
            ActiveMachine.Configuration.EmbeddedFirmwareVersion = embeddedFirmwareVersion;
            ActiveMachine.Configuration.EmbeddedFirmwareVersionGuid = embeddedFirmwareVersion.Guid;
        }

        /// <summary>
        /// Drops the application os version.
        /// </summary>
        /// <param name="applicationOsVersion">The application os version.</param>
        public void DropApplicationOsVersion(ApplicationOsVersion applicationOsVersion)
        {
            ActiveMachine.Configuration.ApplicationOsVersion = applicationOsVersion;
            ActiveMachine.Configuration.ApplicationOsVersionGuid = applicationOsVersion.Guid;
        }

        /// <summary>
        /// Drops the dispenser.
        /// </summary>
        /// <param name="dispenser">The dispenser.</param>
        /// <param name="idsPack">The ids pack.</param>
        public void DropDispenser(Dispenser dispenser, IdsPack idsPack)
        {
            idsPack.Dispenser = dispenser;
            idsPack.DispenserGuid = dispenser.Guid;
        }

        /// <summary>
        /// Drops the type of the cartridge.
        /// </summary>
        /// <param name="cartridgeType">Type of the cartridge.</param>
        /// <param name="idsPack">The ids pack.</param>
        public void DropCartridgeType(CartridgeType cartridgeType, IdsPack idsPack)
        {
            idsPack.CartridgeType = cartridgeType;
            idsPack.CartridgeTypeGuid = cartridgeType.Guid;
        }

        /// <summary>
        /// Drops the type of the mid tank.
        /// </summary>
        /// <param name="midTankType">Type of the mid tank.</param>
        /// <param name="idsPack">The ids pack.</param>
        public void DropMidTankType(MidTankType midTankType, IdsPack idsPack)
        {
            idsPack.MidTankType = midTankType;
            idsPack.MidTankTypeGuid = midTankType.Guid;
        }

        /// <summary>
        /// Drops the type of the liquid.
        /// </summary>
        /// <param name="liquidType">Type of the liquid.</param>
        /// <param name="idsPack">The ids pack.</param>
        public void DropLiquidType(LiquidType liquidType, IdsPack idsPack)
        {
            idsPack.LiquidType = liquidType;
            idsPack.LiquidTypeGuid = liquidType.Guid;
            ColorCalibrationViewVM.InvalidateCalibrationDataAndColorConversion();
        }

        /// <summary>
        /// Drops the ids formula.
        /// </summary>
        /// <param name="idsPackFormula">The ids pack formula.</param>
        /// <param name="idsPack">The ids pack.</param>
        /// <exception cref="NotImplementedException"></exception>
        public void DropIdsFormula(IdsPackFormula idsPackFormula, IdsPack idsPack)
        {
            idsPack.IdsPackFormula = idsPackFormula;
            idsPack.IdsPackFormulaGuid = idsPackFormula.Guid;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Removes the selected IDS pack.
        /// </summary>
        private void RemoveIds()
        {
            ActiveMachineAdapter.Context.IdsPacks.Remove(SelectedIds);
            SelectedIds = null;
            ColorCalibrationViewVM.InvalidateCalibrationDataAndColorConversion();
        }

        /// <summary>
        /// Adds a new IDS pack.
        /// </summary>
        private void AddIds()
        {
            ActiveMachineAdapter.Context.IdsPacks.Add(new IdsPack() { Configuration = ActiveMachine.Configuration });
            InvalidateRelayCommands();
        }

        private async void LoadSelectedMachine(bool newMachine = false, bool clone = false, MachineCreationDialogVM machineCreationDialogVM = null)
        {
            IsNewMachine = false;

            using (_notification.PushTaskItem("Loading machine details..."))
            {
                try
                {
                    IsFree = false;

                    if (ActiveMachineAdapter != null)
                    {
                        ActiveMachineAdapter.Context.Dispose();
                    }

                    ActiveMachineAdapter = new ObservablesStaticCollections(ObservablesContext.CreateDefault());
                    ActiveMachineAdapter.Organizations = (await ActiveMachineAdapter.Context.Organizations.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.MachineVersions = (await ActiveMachineAdapter.Context.MachineVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.Rmls = (await ActiveMachineAdapter.Context.Rmls.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.ColorSpaces = (await ActiveMachineAdapter.Context.ColorSpaces.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.SpoolTypes = (await ActiveMachineAdapter.Context.SpoolTypes.ToListAsync()).ToObservableCollection();

                    ActiveMachineAdapter.ApplicationDisplayPanelVersions = (await ActiveMachineAdapter.Context.ApplicationDisplayPanelVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.ApplicationFirmwareVersions = (await ActiveMachineAdapter.Context.ApplicationFirmwareVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.ApplicationOsVersions = (await ActiveMachineAdapter.Context.ApplicationOsVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.EmbeddedFirmwareVersions = (await ActiveMachineAdapter.Context.EmbeddedFirmwareVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.DispenserTypes = (await ActiveMachineAdapter.Context.DispenserTypes.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.LiquidTypes = (await ActiveMachineAdapter.Context.LiquidTypes.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.MidTankTypes = (await ActiveMachineAdapter.Context.MidTankTypes.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.CartridgeTypes = (await ActiveMachineAdapter.Context.CartridgeTypes.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.IdsPackFormulas = (await ActiveMachineAdapter.Context.IdsPackFormulas.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.HardwareVersions = (await ActiveMachineAdapter.Context.HardwareVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.MachineVersions = (await ActiveMachineAdapter.Context.MachineVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.Organizations = (await ActiveMachineAdapter.Context.Organizations.ToListAsync()).ToObservableCollection();

                    bool initHwConfig = true;

                    Configuration machineConfigBeforeClone = null;

                    if (!newMachine)
                    {
                        ActiveMachine = (await new MachineBuilder(ActiveMachineAdapter.Context).Set(SelectedMachine.Guid).WithOrganization().WithConfiguration().WithSpools().BuildAsync());
                        _machineBeforeSave = MachineDTO.FromObservable(ActiveMachine);


                        if (clone)
                        {
                            machineConfigBeforeClone = ActiveMachine.Configuration;

                            IsNewMachine = true;
                            ActiveMachine = ActiveMachine.Clone();
                            ActiveMachine.Name = machineCreationDialogVM.Name;
                            ActiveMachine.SerialNumber = machineCreationDialogVM.SerialNumber;
                            ActiveMachine.IsDeviceRegistered = false;
                            ActiveMachine.DeviceId = null;
                            ActiveMachine.DeviceName = null;
                            ActiveMachineAdapter.Context.Machines.Add(ActiveMachine);
                        }
                    }
                    else
                    {
                        IsNewMachine = true;

                        if (machineCreationDialogVM.SelectedMachineVersion == null)
                        {
                            ActiveMachine = new Machine();
                            ActiveMachine.Configuration = new Configuration();
                            ActiveMachine.SerialNumber = machineCreationDialogVM.SerialNumber;
                            ActiveMachine.Name = machineCreationDialogVM.Name;
                            ActiveMachineAdapter.Context.Machines.Add(ActiveMachine);
                        }
                        else
                        {
                            try
                            {
                                initHwConfig = false;
                                ActiveMachine = await machineCreationDialogVM.SelectedMachineVersion.CreatePrototypeMachine(ActiveMachineAdapter.Context);
                                ActiveMachine.SerialNumber = machineCreationDialogVM.SerialNumber;
                                ActiveMachine.Name = machineCreationDialogVM.Name;
                                ActiveMachineAdapter.Context.Machines.Add(ActiveMachine);

                                HardwareConfigurationViewVM = new HardwareConfigurationViewVM(_notification);
                                var version = await new HardwareVersionBuilder(ActiveMachineAdapter.Context).Set(ActiveMachine.Configuration.HardwareVersionGuid).WithHardwareComponents().BuildAsync();
                                HardwareConfigurationViewVM.Init(ActiveMachine.Configuration);
                            }
                            catch (Exception ex)
                            {
                                _notification.ShowError($"Invalid machine version prototype.\n{ex.FlattenMessage()}");
                                View.NavigateTo(MachineDesignerNavigationView.MachinesView);
                                return;
                            }
                        }
                    }

                    if ((newMachine || clone) && machineCreationDialogVM.GenerateDispensers)
                    {
                        for (int i = 0; i < 8; i++)
                        {
                            var serial = machineCreationDialogVM.SerialNumber + "-" + (i + 1);

                            var existingDispenser = await ActiveMachineAdapter.Context.Dispensers.Include(x => x.IdsPacks).SingleOrDefaultAsync(x => x.SerialNumber == serial);

                            if (existingDispenser != null)
                            {
                                if (existingDispenser.IsInstalled)
                                {
                                    _notification.ShowError($"Dispenser '{serial}' already exists. Cannot create machine.");
                                    return;
                                }
                                else
                                {
                                    if (!_notification.ShowErrorQuestion($"Dispenser '{serial}' already exists and is not installed. Do you wish to assign the existing dispenser to this machine?"))
                                    {
                                        return;
                                    }
                                }
                            }

                            Dispenser dispenser = new Dispenser();

                            if (existingDispenser == null)
                            {
                                dispenser.SerialNumber = serial;

                                if (newMachine)
                                {
                                    dispenser.NlPerPulse = machineCreationDialogVM.DispenserFactor;
                                    dispenser.DispenserTypeGuid = ActiveMachineAdapter.DispenserTypes.First().Guid;
                                }
                                else
                                {
                                    var packBefore = machineConfigBeforeClone.NoneEmptyIdsPacks.SingleOrDefault(x => x.PackIndex == i);

                                    if (packBefore != null)
                                    {
                                        dispenser.NlPerPulse = packBefore.Dispenser.NlPerPulse;
                                        dispenser.DispenserTypeGuid = packBefore.Dispenser.DispenserType.Guid;
                                    }
                                    else
                                    {
                                        continue;
                                    }
                                }

                                dispenser.ProductionDate = DateTime.UtcNow;

                                ActiveMachineAdapter.Context.Dispensers.Add(dispenser);
                            }
                            else
                            {
                                dispenser = existingDispenser;
                            }

                            var idsPack = ActiveMachine.Configuration.NoneEmptyIdsPacks.SingleOrDefault(x => x.PackIndex == i);

                            if (idsPack != null)
                            {
                                idsPack.Dispenser = dispenser;
                                idsPack.DispenserGuid = dispenser.Guid;
                            }
                        }
                    }
                    ActiveMachine.OrganizationChanged -= ActiveMachine_OrganizationChanged;
                    ActiveMachine.OrganizationChanged += ActiveMachine_OrganizationChanged;

                    _all_sites = await ActiveMachineAdapter.Context.Sites.ToListAsync();

                    var sites = ActiveMachine.Organization != null ? _all_sites.Where(x => x.OrganizationGuid == ActiveMachine.OrganizationGuid).ToList() : new List<Site>();
                    sites.Insert(0, new Site() { Name = "NONE", ID = -1 });
                    Sites = sites;

                    SelectedSite = Sites.SingleOrDefault(x => x.Guid == ActiveMachine.SiteGuid);

                    ColorCalibrationViewVM = new ColorCalibrationViewVM(_notification, ActiveMachine, _activeMachineAdapter.Context)
                    {
                        Rmls = ActiveMachineAdapter.Rmls,
                        LiquidTypesRmls = ActiveMachineAdapter.Rmls.FirstOrDefault().LiquidTypesRmls,
                        SelectedRML = ActiveMachineAdapter.Rmls.FirstOrDefault(),
                    };

                    if (initHwConfig)
                    {
                        HardwareConfigurationViewVM = new HardwareConfigurationViewVM(_notification);
                        HardwareConfigurationViewVM.Init(ActiveMachine.Configuration);
                    }

                    if (!IsNewMachine)
                    {
                        await MachineUpdatesViewVM.Init(ActiveMachine, ActiveMachineAdapter.Context);
                        TupViewVM.Init(ActiveMachine);
                    }

                    ActiveMachine.Configuration.HardwareVersionChanged += Configuration_HardwareVersionChanged;

                    View.NavigateTo(MachineDesignerNavigationView.MachineDetailsView);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, $"Error loading machine details for serial number {SelectedMachine.SerialNumber}");
                    _notification.ShowError($"An error occurred while trying to load the selected machine details.\n{ex.Message}");
                }
                finally
                {
                    IsFree = true;
                    InvalidateRelayCommands();
                }
            }
        }

        private async void Configuration_HardwareVersionChanged(object sender, HardwareVersion e)
        {
            var version = ActiveMachine.Configuration.HardwareVersion;

            if (version != null)
            {
                using (_notification.PushTaskItem("Loading hardware version..."))
                {
                    try
                    {
                        if (HardwareConfigurationViewVM == null)
                        {
                            HardwareConfigurationViewVM = new HardwareConfigurationViewVM(_notification);
                        }

                        version = await new HardwareVersionBuilder(ActiveMachineAdapter.Context).Set(version.Guid).WithHardwareComponents().BuildAsync();
                        HardwareConfigurationViewVM.Init(ActiveMachine.Configuration);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex);
                    }
                }
            }
        }

        private async void SaveMachine()
        {
            foreach (var ids in ActiveMachine.Configuration.IdsPacks)
            {
                ids.PackIndex = ActiveMachine.Configuration.IdsPacks.IndexOf(ids);
                ids.Configuration = ActiveMachine.Configuration;
                ids.ConfigurationGuid = ActiveMachine.Configuration.Guid;
            }

            List<String> errors = new List<string>();

            if (ActiveMachine.MachineVersion == null)
            {
                errors.Add("Machine version is required.");
            }

            if (ActiveMachine.Name.IsNullOrWhiteSpace())
            {
                errors.Add("Machine name is required.");
            }

            if (ActiveMachine.Organization == null)
            {
                errors.Add("Machine organization is required.");
            }

            if (ActiveMachine.SerialNumber.IsNullOrWhiteSpace())
            {
                errors.Add("Machine serial number is required.");
            }

            if (ActiveMachine.Configuration.ApplicationDisplayPanelVersion == null)
            {
                errors.Add("Touch Panel is required.");
            }

            if (ActiveMachine.Configuration.ApplicationFirmwareVersion == null)
            {
                errors.Add("Application firmware is required.");
            }

            if (ActiveMachine.Configuration.ApplicationOsVersion == null)
            {
                errors.Add("Application operation system is required.");
            }

            if (ActiveMachine.Configuration.EmbeddedFirmwareVersion == null)
            {
                errors.Add("Embedded firmware is required.");
            }

            if (ActiveMachine.Configuration.HardwareVersion == null)
            {
                errors.Add("Hardware version is required.");
            }

            foreach (var pack in ActiveMachine.Configuration.IdsPacks)
            {
                if (pack.LiquidType != null || pack.CartridgeType != null || pack.Dispenser != null || pack.IdsPackFormula != null || pack.MidTankType != null)
                {
                    if (pack.CartridgeType == null)
                    {
                        errors.Add(String.Format("Cartridge type is required on IDS pack '{0}'.", pack.PackIndex));
                    }
                    if (pack.Dispenser == null)
                    {
                        errors.Add(String.Format("Dispenser is required on IDS pack '{0}'.", pack.PackIndex));
                    }
                    if (pack.LiquidType == null)
                    {
                        errors.Add(String.Format("Liquid type is required on IDS pack '{0}'.", pack.PackIndex));
                    }
                    if (pack.MidTankType == null)
                    {
                        errors.Add(String.Format("Mid Tank type is required on IDS pack '{0}'.", pack.PackIndex));
                    }
                    if (pack.IdsPackFormula == null)
                    {
                        errors.Add(String.Format("Formula type is required on IDS pack '{0}'.", pack.PackIndex));
                    }

                    pack.IsEmpty = false;
                }
                else
                {
                    pack.IsEmpty = true;
                }
            }

            foreach (var pack in ActiveMachine.Configuration.IdsPacks.Where(x => x.Dispenser != null))
            {
                if (ActiveMachine.Configuration.IdsPacks.Where(x => x.Dispenser == pack.Dispenser).Count() > 1)
                {
                    errors.Add($"Dispenser '{pack.Dispenser.SerialNumber}' is installed on multiple IDS packs.");
                }

                if (ActiveMachineAdapter.Context.IdsPacks.Count(x => x.ConfigurationGuid != pack.ConfigurationGuid && x.DispenserGuid == pack.DispenserGuid) > 0)
                {
                    errors.Add($"Dispenser '{pack.Dispenser.SerialNumber}' is already installed on a different machine.");
                }
            }

            if (ActiveMachine.Spools.GroupBy(x => x.SpoolType).Any(x => x.Count() > 1))
            {
                errors.Add($"Same spool type is registered multiple times.");
            }

            if (errors.Count > 0)
            {
                String errorsString = "Please fix the following validation errors before trying to save." + Environment.NewLine + Environment.NewLine;
                errorsString += String.Join(Environment.NewLine, errors.Distinct());
                _notification.ShowError(errorsString);
                return;
            }

            try
            {
                IsFree = false;

                using (_notification.PushTaskItem("Saving Machine Details..."))
                {

                    ActiveMachine.ConfigurationGuid = ActiveMachine.Configuration.Guid;
                    ActiveMachine.LastUpdated = DateTime.UtcNow;
                    ActiveMachine.SiteGuid = SelectedSite == null ? null : (SelectedSite.ID == -1 ? null : SelectedSite.Guid);

                    ColorCalibrationViewVM.Save();

                    var hwConfig = HardwareConfigurationViewVM.GetResultingHardwareConfiguration();
                    ActiveMachine.Configuration.SetHardwareConfiguration(hwConfig);
                    await ActiveMachineAdapter.Context.SaveChangesAsync();

                    if (IsNewMachine)
                    {
                        _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.MachineCreated, _authentication.CurrentUser, ActiveMachine.Name, ActiveMachine, "New machine created using Machine Studio.");
                    }
                    else
                    {
                        var machineAfterDTO = MachineDTO.FromObservable(ActiveMachine);
                        _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.MachineSaved, _authentication.CurrentUser, _machineBeforeSave.Name, _machineBeforeSave, machineAfterDTO, "Machine saved using Machine Studio.");
                        _machineBeforeSave = machineAfterDTO;
                    }

                    if (SelectedMachine != null)
                    {
                        await SelectedMachine.Reload(MachinesAdapter.Context);
                    }

                    ColorCalibrationViewVM.Invalidate();
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error saving machine details.");
                _notification.ShowError("An error occurred while trying to save the machine details" + Environment.NewLine + ex.Message);
            }
            finally
            {
                IsFree = true;
                InvalidateRelayCommands();
            }
        }

        private void AddNewMachine()
        {
            MachineCreationDialogVM vm = new MachineCreationDialogVM();
            vm.IsNewMachine = true;
            vm.MachineVersions = MachinesAdapter.MachineVersions.ToList();
            _notification.ShowModalDialog<MachineCreationDialogVM, Views.MachineCreationDialog>(vm, (x) =>
            {
                if (MachinesAdapter.Context.Machines.Any(y => y.SerialNumber == vm.SerialNumber || y.Name.ToLower() == vm.Name.ToLower()))
                {
                    _notification.ShowError("Machine serial number or name already exists.");
                    return;
                }

                if (vm.SelectedMachineVersion != null && String.IsNullOrWhiteSpace(vm.SelectedMachineVersion.PrototypeMachineData))
                {
                    _notification.ShowError("The selected version does not contain any prototype machine data.");
                    return;
                }

                LoadSelectedMachine(true, false, vm);
            }, () => { });
        }

        private async void RemoveSelectedMachine()
        {
            if (_notification.ShowQuestion("Are you sure you want to delete the selected machine?"))
            {
                using (_notification.PushTaskItem("Removing machine..."))
                {
                    try
                    {
                        IsFree = false;
                        await SelectedMachine.DeleteCascadeAsync(MachinesAdapter.Context);
                        _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.MachineDeleted, _authentication.CurrentUser, SelectedMachine.Name, SelectedMachine, "Machine deleted using Machine Studio.");
                        MachinesAdapter.Context.Machines.Remove(SelectedMachine);
                        MachinesAdapter.Machines.Remove(SelectedMachine);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, $"Error removing machine {SelectedMachine.SerialNumber}.");
                        _notification.ShowError($"An error occurred while trying to delete the selected machine.\n{ex.Message}");
                    }
                    finally
                    {
                        IsFree = true;
                    }
                }
            }
        }

        private void ResetDeviceRegistration()
        {
            if (_notification.ShowQuestion("Are you sure you wish to reset this machine device registration?"))
            {
                ActiveMachine.IsDeviceRegistered = false;
                ActiveMachine.DeviceId = null;
                ActiveMachine.DeviceName = null;
            }
        }

        private async void MakePrototype()
        {
            if (ActiveMachine.MachineVersion == null)
            {
                _notification.ShowError("Machine version must be selected in order to make a prototype.");
                return;
            }

            if (_notification.ShowQuestion($"Are you sure you wish to make this machine configuration as a prototype for version '{ActiveMachine.MachineVersion.Name}' ?"))
            {
                using (_notification.PushTaskItem($"Making prototype machine for '{ActiveMachine.MachineVersion.Name}'..."))
                {
                    try
                    {
                        IsFree = false;

                        using (var db = ObservablesContext.CreateDefault())
                        {
                            var machineVersion = await db.MachineVersions.SingleOrDefaultAsync(x => x.Guid == ActiveMachine.MachineVersionGuid);
                            await machineVersion.ApplyPrototypeMachine(ActiveMachine, db);
                            await db.SaveChangesAsync();
                        }
                    }
                    catch (Exception ex)
                    {
                        _notification.ShowError($"Error making machine version prototype\n{ex.FlattenMessage()}");
                    }
                    finally
                    {
                        IsFree = true;
                    }
                }
            }
        }

        #endregion

        private void CloneMachine()
        {
            MachineCreationDialogVM vm = new MachineCreationDialogVM();
            vm.IsNewMachine = false;
            vm.MachineVersions = MachinesAdapter.MachineVersions.ToList();
            _notification.ShowModalDialog<MachineCreationDialogVM, Views.MachineCreationDialog>(vm, (x) =>
            {
                if (MachinesAdapter.Context.Machines.Any(y => y.SerialNumber == vm.SerialNumber || y.Name.ToLower() == vm.Name.ToLower()))
                {
                    _notification.ShowError("Machine serial number or name already exists.");
                    return;
                }

                LoadSelectedMachine(false, true, vm);
            }, () => { });
        }

        private void AddNewSpool()
        {
            _activeMachineAdapter.Context.Spools.Add(new Spool()
            {
                Machine = ActiveMachine,
            });
        }

        private void RemoveSpool()
        {
            if (SelectedSpool != null)
            {
                _activeMachineAdapter.Context.Spools.Remove(SelectedSpool);
                ActiveMachine.Spools.Remove(SelectedSpool);
            }
        }

        private void OnFilterChanged()
        {
            if (Filter != null)
            {
                _machines_action_timer.ResetReplace(() =>
                {
                    Task.Factory.StartNew(() =>
                    {
                        try
                        {
                            IsFree = false;
                            MachinesAdapter.Machines = MachinesAdapter.Context.Machines.Where(x => x.SerialNumber.StartsWith(Filter)).Include(x => x.Organization).Include(x => x.MachineVersion).OrderBy(x => x.SerialNumber).ToSynchronizedObservableCollection();
                        }
                        catch
                        {

                        }
                        finally
                        {
                            IsFree = true;
                        }
                    });
                });
            }
        }

        private void ActiveMachine_OrganizationChanged(object sender, Organization e)
        {
            var sites = ActiveMachine.Organization != null ? _all_sites.Where(x => x.OrganizationGuid == ActiveMachine.OrganizationGuid).ToList() : new List<Site>();
            sites.Insert(0, new Site() { Name = "NONE", ID = -1 });
            Sites = sites;

            SelectedSite = Sites.SingleOrDefault(x => x.Guid == ActiveMachine.SiteGuid);
        }
    }
}