aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs
Path not found
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Tango.BL;
using Tango.BL.Builders;
using Tango.BL.Entities;
using Tango.Core.Commands;
using Tango.Core.DI;
using Tango.Integration.ExternalBridge;
using Tango.Logging;
using Tango.MachineStudio.Common;
using Tango.MachineStudio.Common.Authentication;
using Tango.MachineStudio.Common.Diagnostics;
using Tango.MachineStudio.Common.EventLogging;
using Tango.MachineStudio.Common.Messages;
using Tango.MachineStudio.Common.Modules;
using Tango.MachineStudio.Common.Navigation;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.Common.Speech;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.MachineStudio.Common.Web;
using Tango.MachineStudio.UI.StudioApplication;
using Tango.MachineStudio.UI.SupervisingController;
using Tango.MachineStudio.UI.TFS;
using Tango.MachineStudio.UI.Views;
using Tango.MachineStudio.UI.Windows;
using Tango.PMR.Stubs;
using Tango.Settings;
using Tango.SharedUI;
using Tango.SharedUI.Controls;
using Tango.SharedUI.Helpers;
using Tango.Transport;
using Tango.Transport.Adapters;

namespace Tango.MachineStudio.UI.ViewModels
{
    /// <summary>
    /// Represents the Machine Studio main view, view model.
    /// </summary>
    /// <seealso cref="Tango.SharedUI.ViewModel{Tango.MachineStudio.UI.SupervisingController.IMainView}" />
    public class MainViewVM : StudioViewModel
    {
        private IStudioModule _currentModule;
        private INavigationManager _navigation;
        private bool _isDisconnecting;
        private Thread _updateCheckThread;
        private IEventLogger _eventLogger;
        private MachineStudioSettings _settings;
        private MachineStudioWebClient _machineStudioWebClient;
        private IExternalBridgeSecureClient _reconnectionMachine;
        private MachineLoginViewVM _reconnectionMachineConfig;

        /// <summary>
        /// Gets or sets the current loaded module.
        /// </summary>
        public IStudioModule CurrentModule
        {
            get { return _currentModule; }
            set { _currentModule = value; RaisePropertyChangedAuto(); }
        }

        private bool _isApplicationReady;
        public bool IsApplicationReady
        {
            get { return _isApplicationReady; }
            set { _isApplicationReady = value; RaisePropertyChangedAuto(); }
        }

        private bool _isModuleLoaded;
        /// <summary>
        /// Gets or sets a value indicating whether any module is loaded at the moment.
        /// </summary>
        public bool IsModuleLoaded
        {
            get { return _isModuleLoaded; }
            set { _isModuleLoaded = value; RaisePropertyChangedAuto(); }
        }

        private bool _isMenuOpened;
        /// <summary>
        /// Gets or sets a value indicating whether the side menu is opened.
        /// </summary>
        public bool IsMenuOpened
        {
            get { return _isMenuOpened; }
            set { _isMenuOpened = value; RaisePropertyChangedAuto(); }
        }

        private bool _isMachineErrorsOpened;
        /// <summary>
        /// Gets or sets a value indicating whether to display the machine errors.
        /// </summary>
        public bool IsMachineErrorsOpened
        {
            get { return _isMachineErrorsOpened; }
            set { _isMachineErrorsOpened = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the start module command.
        /// </summary>
        public RelayCommand<IStudioModule> StartModuleCommand { get; set; }

        /// <summary>
        /// Gets or sets the open module in window command.
        /// </summary>
        public RelayCommand<IStudioModule> OpenModuleInWindowCommand { get; set; }

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

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

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

        /// <summary>
        /// Gets or sets the sign-out command.
        /// </summary>
        public RelayCommand SignoutCommand { get; set; }

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

        /// <summary>
        /// Gets or sets the update center command.
        /// </summary>
        public RelayCommand UpdateCenterCommand { get; set; }

        /// <summary>
        /// Gets or sets the toggle speech command.
        /// </summary>
        public RelayCommand ToggleSpeechCommand { get; set; }

        /// <summary>
        /// Gets or sets the display HTML command.
        /// </summary>
        public RelayCommand<MachinesEvent> ResolveMachineEventCommand { get; set; }

        /// <summary>
        /// Gets or sets the report issue command.
        /// </summary>
        public RelayCommand ReportIssueCommand { get; set; }

        /// <summary>
        /// Gets or sets the open resolved bugs.
        /// </summary>
        public RelayCommand OpenResolvedBugsCommand { get; set; }

        /// <summary>
        /// Gets or sets the open developer console command.
        /// </summary>
        public RelayCommand OpenDeveloperConsoleCommand { get; set; }

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

        /// <summary>
        /// Gets or sets the about command.
        /// </summary>
        public RelayCommand<MachineStudioTheme> ChangeAppThemeCommand { get; set; }

        private IAuthenticationProvider _authenticationProvider;
        /// <summary>
        /// Gets or sets the authentication provider.
        /// </summary>
        public IAuthenticationProvider AuthenticationProvider
        {
            get { return _authenticationProvider; }
            set { _authenticationProvider = value; RaisePropertyChangedAuto(); }
        }

        private IStudioModuleLoader _studioModuleLoader;
        /// <summary>
        /// Gets or sets the studio module loader.
        /// </summary>
        public IStudioModuleLoader StudioModuleLoader
        {
            get { return _studioModuleLoader; }
            set { _studioModuleLoader = value; RaisePropertyChangedAuto(); }
        }

        private INotificationProvider _notificationProvider;
        /// <summary>
        /// Gets or sets the notification provider.
        /// </summary>
        public INotificationProvider NotificationProvider
        {
            get { return _notificationProvider; }
            set { _notificationProvider = value; RaisePropertyChangedAuto(); }
        }

        private IStudioApplicationManager _applicationManager;
        /// <summary>
        /// Gets or sets the application manager.
        /// </summary>
        public IStudioApplicationManager ApplicationManager
        {
            get { return _applicationManager; }
            set { _applicationManager = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the TFS client.
        /// </summary>
        public TeamFoundationServiceExtendedClient TFSClient { get; set; }

        /// <summary>
        /// Gets or sets the speech provider.
        /// </summary>
        public ISpeechProvider SpeechProvider { get; set; }

        private bool _isUpdateAvailable;
        /// <summary>
        /// Gets or sets a value indicating whether a new version update is available.
        /// </summary>
        public bool IsUpdateAvailable
        {
            get { return _isUpdateAvailable; }
            set { _isUpdateAvailable = value; RaisePropertyChangedAuto(); }
        }

        private String _latestVersion;
        /// <summary>
        /// Gets or sets the latest version.
        /// </summary>
        public String LatestVersion
        {
            get { return _latestVersion; }
            set { _latestVersion = value; RaisePropertyChangedAuto(); }
        }

        private bool _disableCheckForUpdates;
        /// <summary>
        /// Gets or sets a value indicating whether [disable check for updates].
        /// </summary>
        public bool DisableCheckForUpdates
        {
            get { return _disableCheckForUpdates; }
            set { _disableCheckForUpdates = value; RaisePropertyChangedAuto(); }
        }

        private IDiagnosticsFrameProvider _diagnosticsFrameProvider;
        /// <summary>
        /// Gets or sets the diagnostics frame provider.
        /// </summary>
        public IDiagnosticsFrameProvider DiagnosticsFrameProvider
        {
            get { return _diagnosticsFrameProvider; }
            set { _diagnosticsFrameProvider = value; RaisePropertyChangedAuto(); }
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="MainViewVM"/> class.
        /// </summary>
        /// <param name="view">The view.</param>
        /// <param name="authenticationProvider">The authentication provider.</param>
        /// <param name="studioModuleLoader">The studio module loader.</param>
        /// <param name="notificationProvider">The notification provider.</param>
        /// <param name="applicationManager">The application manager.</param>
        /// <param name="navigationManager">The navigation manager.</param>
        public MainViewVM(
            IAuthenticationProvider authenticationProvider,
            IStudioModuleLoader studioModuleLoader,
            INotificationProvider notificationProvider,
            IStudioApplicationManager applicationManager,
            INavigationManager navigationManager,
            IEventLogger eventLogger,
            IDiagnosticsFrameProvider frameProvider,
            ISpeechProvider speechProvider,
            TeamFoundationServiceExtendedClient tfs,
            MachineStudioWebClient machineStudioWebClient) : base()
        {
            _machineStudioWebClient = machineStudioWebClient;
            TFSClient = tfs;
            _eventLogger = eventLogger;
            _navigation = navigationManager;
            AuthenticationProvider = authenticationProvider;
            StudioModuleLoader = studioModuleLoader;
            NotificationProvider = notificationProvider;
            ApplicationManager = applicationManager;
            DiagnosticsFrameProvider = frameProvider;
            SpeechProvider = speechProvider;

            _settings = SettingsManager.Default.GetOrCreate<MachineStudioSettings>();

            StartModuleCommand = new RelayCommand<IStudioModule>(StartModule);

            HomeCommand = new RelayCommand(Home);
            ConnectCommand = new RelayCommand(ConnectToMachine);
            SignoutCommand = new RelayCommand(SignOut);
            DisconnectCommand = new RelayCommand(DisconnectFromMachine, (x) => ApplicationManager.IsMachineConnected && !_isDisconnecting);
            OpenModuleInWindowCommand = new RelayCommand<IStudioModule>((x) => { OpenModuleInWindow(x); });
            ExitCommand = new RelayCommand(ExitApplication);
            UpdateCenterCommand = new RelayCommand(NavigateToUpdateCenter);

            _updateCheckThread = new Thread(UpdateCheckThreadMethod);
            _updateCheckThread.IsBackground = true;
            _updateCheckThread.Start();

            ApplicationManager.ConnectedMachineChanged += (sender, machine) =>
            {
                if (machine != null)
                {
                    machine.MachineEventsStateProvider.NewEvents -= MachineEventsStateProvider_NewEvents;
                    machine.MachineEventsStateProvider.NewEvents += MachineEventsStateProvider_NewEvents;

                    machine.MachineEventsStateProvider.EventsResolved -= MachineEventsStateProvider_EventsResolved;
                    machine.MachineEventsStateProvider.EventsResolved += MachineEventsStateProvider_EventsResolved;
                }
            };

            ToggleSpeechCommand = new RelayCommand(() => { SpeechProvider.Mute = !SpeechProvider.Mute; });

            ResolveMachineEventCommand = new RelayCommand<MachinesEvent>(ResolveMachineEvent);

            ReportIssueCommand = new RelayCommand(ReportIssue);
            OpenResolvedBugsCommand = new RelayCommand(OpenResolvedBugs);
            OpenDeveloperConsoleCommand = new RelayCommand(OpenDeveloperConsole);

            TangoMessenger.Default.Register<Messages.ForcedUpdateMessage>((x) => DisableCheckForUpdates = true);

            AboutCommand = new RelayCommand(ShowAboutDialog);

            ChangeAppThemeCommand = new RelayCommand<MachineStudioTheme>(ChangeTheme);

            ApplicationManager.ReconnectionRequired += ApplicationManager_ReconnectionRequired;
        }

        private void ApplicationManager_ReconnectionRequired(object sender, EventArgs e)
        {
            ConnectToMachineSecure(_reconnectionMachine, _reconnectionMachineConfig);
        }

        private void MachineEventsStateProvider_EventsResolved(object sender, IEnumerable<MachinesEvent> e)
        {
            if (ApplicationManager.ConnectedMachine.MachineEventsStateProvider.Events.Count == 0)
            {
                IsMachineErrorsOpened = false;
            }
        }

        private void MachineEventsStateProvider_NewEvents(object sender, IEnumerable<MachinesEvent> e)
        {
            IsMachineErrorsOpened = true;
        }

        private void UpdateCheckThreadMethod()
        {
            while (!DisableCheckForUpdates)
            {
                Thread.Sleep(TimeSpan.FromMinutes(0.2));

                try
                {
                    if (_authenticationProvider.CurrentUser != null)
                    {
                        CheckForUpdatesResponse response = _machineStudioWebClient.CheckForUpdates(new CheckForUpdatesRequest()
                        {
                            Version = _applicationManager.Version.ToString(),
                        }).Result;

                        IsUpdateAvailable = response.IsUpdateAvailable;
                        LatestVersion = response.Version;
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error in version update periodic check...");
                }

                Thread.Sleep(TimeSpan.FromMinutes(4));
            }
        }

        /// <summary>
        /// Disconnected from the current connected machine.
        /// </summary>
        private async void DisconnectFromMachine()
        {
            using (_notificationProvider.PushTaskItem("Disconnecting from machine..."))
            {
                try
                {
                    _isDisconnecting = true;
                    InvalidateRelayCommands();
                    String serial = ApplicationManager.ConnectedMachine.SerialNumber;
                    await ApplicationManager.ConnectedMachine.Disconnect();
                    ApplicationManager.SetConnectedMachine(null);

                    PostMessage(new MachineConnectionChangedMessage() { Machine = null });
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Could not disconnect from machine.");
                }
                finally
                {
                    _isDisconnecting = false;
                    InvalidateRelayCommands();
                }
            }
        }

        /// <summary>
        /// Signs-out the current logged-in user.
        /// </summary>
        private void SignOut()
        {
            _authenticationProvider.Logout();
            Home();
            _navigation.NavigateTo(NavigationView.LoginView);
            CurrentModule = null;
            IsMenuOpened = false;
        }

        /// <summary>
        /// Opens the machine connection dialog to allow the user to scan and connect to remote machines view USB/TCP.
        /// </summary>
        private void ConnectToMachine()
        {
            if (ApplicationManager.ConnectedMachine == null)
            {
                _notificationProvider.ShowModalDialog<MachineConnectionViewVM>(async (x) =>
                {
                    if (x.SelectedMachine != null)
                    {
                        if (ApplicationManager.IsMachineConnected)
                        {
                            using (_notificationProvider.PushTaskItem("Disconnecting..."))
                            {
                                await ApplicationManager.ConnectedMachine.Disconnect();
                                await Task.Delay(1000);
                            }
                        }

                        x.SelectedMachine.EnableDiagnostics = x.EnableDiagnostics;
                        x.SelectedMachine.EnableEmbeddedDebugging = x.EnableDiagnostics;
                        x.SelectedMachine.EnableEventsNotification = x.EnableDiagnostics;
                        x.SelectedMachine.UseKeepAlive = x.EnableKeepAlive;
                        x.SelectedMachine.JobUnitsMethod = _settings.JobUnitsMethod;
                        x.SelectedMachine.JobRunsLogger.JobSource = BL.Enumerations.JobSource.Remote;

                        if (x.SelectedMachine is ExternalBridgeTcpClient)
                        {
                            x.SelectedMachine.As<ExternalBridgeTcpClient>().EnableApplicationLogs = x.EnableApplicationLogs;
                            x.SelectedMachine.RequestTimeout = _settings.ExternalBridgeRequestTimeout;
                            x.SelectedMachine.ContinuousRequestTimeout = _settings.ExternalBridgeContinuousRequestTimeout;
                        }

                        if (x.SelectedMachine.Adapter is TcpTransportAdapter)
                        {
                            (x.SelectedMachine.Adapter as TcpTransportAdapter).WriteMode = _settings.TcpTransportAdapterWriteMode;
                        }

                        if (x.SelectedMachine.RequiresAuthentication)
                        {
                            ConnectToMachineSecure(x.SelectedMachine as IExternalBridgeSecureClient);
                        }
                        else
                        {
                            _notificationProvider.ShowModalDialog<MachineSerialViewVM>(async (vm) =>
                            {
                                if (vm.SelectedMachine != null)
                                {
                                    using (NotificationProvider.PushTaskItem("Connecting to " + x.SelectedMachine.ToString() + "..."))
                                    {
                                        try
                                        {
                                            await x.SelectedMachine.Connect();
                                            x.SelectedMachine.SerialNumber = vm.SelectedMachine.SerialNumber;
                                            ApplicationManager.SetConnectedMachine(x.SelectedMachine);

                                            PostMessage(new MachineConnectionChangedMessage() { Machine = x.SelectedMachine });
                                            _settings.LastVirtualMachineSerialNumber = vm.SelectedMachine.SerialNumber;
                                            _settings.Save();

                                            if (x.UploadHardwareConfiguration)
                                            {
                                                UploadHardwareConfiguration(false);
                                            }
                                        }
                                        catch (Exception ex)
                                        {
                                            LogManager.Log(ex);

                                            _notificationProvider.ShowError("Could not connect to the selected machine." + Environment.NewLine + ex.Message);

                                        }

                                        InvalidateRelayCommands();
                                    }
                                }
                            });
                        }

                        InvalidateRelayCommands();
                    }

                    InvalidateRelayCommands();
                });
            }
            else
            {
                _notificationProvider.ShowModalDialog<ConnectedMachineViewVM>(async (x) =>
                {
                    if (x.Result == ConnectedMachineViewVM.ConnectedMachineVMResult.Disconnect)
                    {
                        DisconnectFromMachine();
                    }
                    else if (x.Result == ConnectedMachineViewVM.ConnectedMachineVMResult.UploadHardwareConfig)
                    {
                        if (NotificationProvider.ShowQuestion("This will reset the machine hardware configuration to the database configuration. Are you sure?"))
                        {
                            using (NotificationProvider.PushTaskItem("Uploading hardware configuration..."))
                            {
                                UploadHardwareConfiguration();
                            }
                        }
                    }
                    else if (x.Result == ConnectedMachineViewVM.ConnectedMachineVMResult.Reset)
                    {
                        if (NotificationProvider.ShowQuestion("This will reset the embedded device. Are you sure?"))
                        {
                            using (NotificationProvider.PushTaskItem("Resetting the embedded device..."))
                            {
                                try
                                {
                                    await ApplicationManager.ConnectedMachine.Reset();
                                    NotificationProvider.ShowInfo("Embedded device has been restarted.");
                                }
                                catch (Exception ex)
                                {
                                    LogManager.Log(ex, "Error resetting embedded device.");
                                    NotificationProvider.ShowError("Error resetting embedded device." + Environment.NewLine + ex.Message);
                                }
                            }
                        }
                    }
                    else if (x.Result == ConnectedMachineViewVM.ConnectedMachineVMResult.TurnOffHeaters)
                    {
                        if (NotificationProvider.ShowQuestion("This will reset the process parameters. Are you sure?"))
                        {
                            using (NotificationProvider.PushTaskItem("Resetting process parameters..."))
                            {
                                try
                                {
                                    await ApplicationManager.ConnectedMachine.UploadProcessParameters(new ProcessParametersTable());
                                    NotificationProvider.ShowInfo("Heaters are turned off.");
                                }
                                catch (Exception ex)
                                {
                                    LogManager.Log(ex, "Error resetting process parameters.");
                                    NotificationProvider.ShowError("Error resetting process parameters." + Environment.NewLine + ex.Message);
                                }
                            }
                        }
                    }
                });
            }

            InvalidateRelayCommands();
        }

        private async void ConnectToMachineSecure(IExternalBridgeSecureClient machine, MachineLoginViewVM config)
        {
            using (NotificationProvider.PushTaskItem("Connecting to machine " + machine.ToString() + "..."))
            {
                try
                {
                    await machine.Connect(new PMR.Integration.ExternalBridgeLoginRequest()
                    {
                        AppID = "Machine Studio",
                        HostName = Environment.MachineName,
                        Password = config.Password,
                        UserGuid = AuthenticationProvider.CurrentUser.Guid,
                        Intent = config.Intent,
                        UserName = AuthenticationProvider.CurrentUser.Contact.FullName,
                        RequireSafetyLevelOperations = config.RequireSafetyOperations
                    });

                    _reconnectionMachine = machine;
                    _reconnectionMachineConfig = config;

                    ApplicationManager.SetConnectedMachine(machine);
                    machine.SessionClosed -= Machine_SessionClosed;
                    machine.SessionClosed += Machine_SessionClosed;
                    PostMessage(new MachineConnectionChangedMessage() { Machine = machine });

                    //if (x.UploadHardwareConfiguration)
                    //{
                    //    UploadHardwareConfiguration(false);
                    //}

                }
                catch (ResponseErrorException ex)
                {
                    LogManager.Log(ex);
                    _notificationProvider.ShowError("Could not connect to the selected machine." + Environment.NewLine + ex.Container.ErrorMessage);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex);
                    _notificationProvider.ShowError("Could not connect to the selected machine." + Environment.NewLine + ex.Message);
                }

                InvalidateRelayCommands();
            }
        }

        private void ConnectToMachineSecure(IExternalBridgeSecureClient machine)
        {
            //Check machine exist on my database first
            if (machine.Machine == null)
            {
                _notificationProvider.ShowError($"The specified machine '{machine.SerialNumber}' could not be found on the database. Aborting connection.");
                return;
            }

            _notificationProvider.ShowModalDialog<MachineLoginViewVM>((login) =>
            {
                ConnectToMachineSecure(machine, login);
            });
        }

        private void Machine_SessionClosed(object sender, EventArgs e)
        {
            InvokeUI(() =>
            {
                _notificationProvider.ShowError("The remote machine has closed the current session. Machine disconnected.");
                ApplicationManager.SetConnectedMachine(null);
            });
        }

        private async void UploadHardwareConfiguration(bool showSuccessMessage = true)
        {
            try
            {
                using (ObservablesContext db = ObservablesContext.CreateDefault())
                {
                    var config = (await new ConfigurationBuilder(db).Set(ApplicationManager.Machine.ConfigurationGuid).WithIdsPacks().WithHardwareVersion().BuildAsync());

                    await ApplicationManager.ConnectedMachine.UploadHardwareConfiguration(config.HardwareVersion, config);
                }

                if (showSuccessMessage)
                {
                    NotificationProvider.ShowInfo("Hardware configuration uploaded successfully.");
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error uploading hardware configuration.");
                NotificationProvider.ShowError("Error uploading hardware configuration." + Environment.NewLine + ex.Message);
            }
        }

        /// <summary>
        /// Navigates to the home screen.
        /// </summary>
        private void Home()
        {
            StartModule(null);
        }

        /// <summary>
        /// Starts the specified module.
        /// </summary>
        /// <param name="module">The module.</param>
        internal async void StartModule(IStudioModule module)
        {
            IsMenuOpened = false;

            if ((_applicationManager as DefaultStudioApplicationManager).IsModuleInNewWindow(module)) return;

            if (module != null)
            {
                LogManager.Log(String.Format("Starting module '{0}'...", module.Name));

                if (!(MainView.Self as MainView).NavigationControl.Elements.ToList().Exists(x => x.GetType() == module.MainViewType))
                {
                    LogManager.Log("Module was not initialized. Initializing...");

                    FrameworkElement view = Activator.CreateInstance(module.MainViewType) as FrameworkElement;
                    NavigationControl.SetNavigationName(view, module.Name);
                    (MainView.Self as MainView).NavigationControl.Elements.Add(view);
                }
            }

            foreach (var m in StudioModuleLoader.AllModules.Where(x => x != module && !x.InNewWindow))
            {
                m.IsLoaded = false;
            }

            if (module != null)
            {
                CurrentModule = module;
                CurrentModule.IsLoaded = true;
                IsModuleLoaded = true;

                LogManager.Log(String.Format("Navigating to module '{0}'...", module.Name));
                (MainView.Self as MainView).NavigationControl.NavigateTo(module.Name);
            }
            else
            {
                IsModuleLoaded = false;
                LogManager.Log(String.Format("Navigating to Home..."));
                (MainView.Self as MainView).NavigationControl.NavigateTo("Home", () =>
                 {
                     CurrentModule = module;
                 });
            }
        }

        /// <summary>
        /// Opens the module in a new window.
        /// </summary>
        /// <param name="module">The module.</param>
        private async void OpenModuleInWindow(IStudioModule module, Rect? bounds = null, WindowState? state = null, bool setHomeModule = true)
        {
            if (module == null) return;

            try
            {
                module.InNewWindow = true;

                if (setHomeModule)
                {
                    StartModule(null);
                }

                await Task.Delay(1000);

                LogManager.Log(String.Format("Starting module '{0}' in new window...", module.Name));

                if (!(MainView.Self as MainView).NavigationControl.Elements.ToList().Exists(x => x.GetType() == module.MainViewType))
                {
                    LogManager.Log("Module was not initialized. Initializing...");
                    FrameworkElement v = Activator.CreateInstance(module.MainViewType) as FrameworkElement;
                    NavigationControl.SetNavigationName(v, module.Name);
                    (MainView.Self as MainView).NavigationControl.Elements.Add(v);
                }

                LogManager.Log("Detaching module view...");
                var view = (MainView.Self as MainView).NavigationControl.GetAndDetach(module.Name);

                ModuleWindowVM vm = new ModuleWindowVM(module);
                ModuleWindow window = new ModuleWindow(this, vm, view);

                if (bounds.HasValue)
                {
                    window.WindowStartupLocation = WindowStartupLocation.Manual;
                    window.WindowState = WindowState.Normal;
                    window.Left = bounds.Value.Left;
                    window.Top = bounds.Value.Top;
                    window.Width = bounds.Value.Width;
                    window.Height = bounds.Value.Height;
                    window.Loaded += (_, __) =>
                    {
                        window.WindowState = state.Value;
                    };
                }

                window.Closing += (x, y) =>
                {
                    LogManager.Log(String.Format("Closing module '{0}' on new window...", module.Name));

                    window.grid.Children.Remove(view);
                    module.InNewWindow = false;
                };

                window.Owner = MainWindow.Instance;

                LogManager.Log("Opening new window...");
                window.Show();

                (_applicationManager as DefaultStudioApplicationManager).RegisterOpenedWindow(window);
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error popping out module " + module.Name);
                _notificationProvider.ShowError("Error popping out module " + module.Name);
            }
        }

        /// <summary>
        /// Navigates to update center.
        /// </summary>
        private void NavigateToUpdateCenter()
        {
            _navigation.NavigateTo(NavigationView.UpdateView);
        }

        /// <summary>
        /// Exits the application.
        /// </summary>
        private void ExitApplication()
        {
            _applicationManager.ShutDown();
        }

        /// <summary>
        /// Displays the HTML.
        /// </summary>
        /// <param name="machineEvent">The HTML page.</param>
        private void ResolveMachineEvent(MachinesEvent machineEvent)
        {

        }

        /// <summary>
        /// Shows the about dialog.
        /// </summary>
        private void ShowAboutDialog()
        {
            NotificationProvider.ShowModalDialog<AboutViewVM>((x) => { });
        }

        private void ReportIssue()
        {
            Tango.TFS.WorkItem bug = null;

            try
            {
                bug = TFSClient.CreateBug();
            }
            catch (Exception ex)
            {
                _notificationProvider.ShowError(ex.Message);
                return;
            }

            _notificationProvider.ShowModalDialog<ReportIssueViewVM, ReportIssueView>(new ReportIssueViewVM(TFSClient.Project, bug), async (vm) =>
             {
                 using (_notificationProvider.PushTaskItem("Uploading bug report..."))
                 {
                     try
                     {
                         TFSClient.FinalizeBug(vm.WorkItem);
                         await TFSClient.UploadWorkItem(vm.WorkItem);
                     }
                     catch (Exception ex)
                     {
                         LogManager.Log(ex, "Error while trying to upload bug report.");
                         _notificationProvider.ShowError("An error occurred while trying to create the issue." + Environment.NewLine + ex.Message);
                     }
                 }

             }, null);
        }

        private void OpenResolvedBugs()
        {
            ResolvedIssuesViewVM vm = null;

            vm = new ResolvedIssuesViewVM(TFSClient, async (item) =>
            {
                //Approve
                using (_notificationProvider.PushTaskItem("Approving issue..."))
                {
                    vm.IsAvailable = false;
                    try
                    {
                        await TFSClient.CloseWorkItem(item);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, $"Error while trying to approve resolved bug '{item.ID}'.");
                        _notificationProvider.ShowError("An error occurred while trying to update the issue." + Environment.NewLine + ex.Message);
                    }
                    vm.IsAvailable = true;

                    if (TFSClient.ResolvedWorkItems.Count == 0)
                    {
                        vm.Close();
                    }
                }
            }, async (item) =>
            {
                //Decline
                using (_notificationProvider.PushTaskItem("Reactivating issue..."))
                {
                    vm.IsAvailable = false;
                    try
                    {
                        await TFSClient.ReactivateWorkItem(item);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, $"Error while trying to decline resolved bug '{item.ID}'.");
                        _notificationProvider.ShowError("An error occurred while trying to update the issue." + Environment.NewLine + ex.Message);
                    }

                    vm.IsAvailable = true;

                    if (TFSClient.ResolvedWorkItems.Count == 0)
                    {
                        vm.Close();
                    }
                }
            });

            _notificationProvider.ShowModalDialog<ResolvedIssuesViewVM, ResolvedIssuesView>(vm, (_) => { }, null);
        }

        private void OpenDeveloperConsole()
        {
            Console.ConsoleWindow console = new Console.ConsoleWindow();
            ApplicationManager.RegisterOpenedWindow(console);
            console.Owner = MainWindow.Instance;
            console.Show();
        }

        public override void OnApplicationReady()
        {
            if (!IsApplicationReady)
            {
                if (!Keyboard.IsKeyDown(Key.LeftCtrl))
                {
                    var settings = SettingsManager.Default.GetOrCreate<MachineStudioSettings>();

                    foreach (var item in settings.StudioModulesBounds)
                    {
                        var module = StudioModuleLoader.AllModules.SingleOrDefault(x => x.Name == item.Name);

                        if (module != null && !module.InNewWindow)
                        {
                            OpenModuleInWindow(module, item.Bounds, item.State, false);
                        }
                    }

                    if (settings.LastMainModuleName != null)
                    {
                        var m = StudioModuleLoader.UserModules.SingleOrDefault(x => x.Name == settings.LastMainModuleName);

                        if (m != null)
                        {
                            StartModule(m);
                        }
                    }
                }

                IsApplicationReady = true;
            }
        }

        private void ChangeTheme(MachineStudioTheme theme)
        {
            var settings = SettingsManager.Default.GetOrCreate<MachineStudioSettings>();
            //add current theme
            settings.Theme = theme;
            settings.Save();

            _notificationProvider.ShowInfo("Changes will be applies after application restart.");
        }
    }
}