aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/PPC/Tango.PPC.UI/RemoteActions/DefaultRemoteActionsService.cs
blob: 1b8780f91628d7144dc1a9f217ee42d13b35a6d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tango.Core.DI;
using Tango.Core.Threading;
using Tango.Integration.ExternalBridge;
using Tango.PPC.Common.ExternalBridge;
using Tango.PPC.Common.RemoteActions;
using Tango.PPC.Common.Threading;
using Tango.PPC.Shared.RemoteActions;

namespace Tango.PPC.UI.RemoteActions
{
    [TangoCreateWhenRegistered]
    public class DefaultRemoteActionsService : IRemoteActionsService, IExternalBridgeRequestHandler
    {
        [TangoInject]
        private IDispatcherProvider DispatcherProvider { get; set; }

        public DefaultRemoteActionsService(IPPCExternalBridgeService externalBridge)
        {
            externalBridge.RegisterRequestHandler(this);
        }

        public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
        {
            //Do nothing.
        }

        [ExternalBridgeRequestHandlerMethod(typeof(SimulateApplicationExceptionRequest), RequestHandlerLoggingMode.LogRequestName)]
        public async Task OnSimulateApplicationExceptionRequest(SimulateApplicationExceptionRequest request, String token, ExternalBridgeReceiver receiver)
        {
            await receiver.SendGenericResponse(new SimulateApplicationExceptionResponse(), token);

            Thread.Sleep(500);

            DispatcherProvider.Invoke(() => 
            {
                if (request.CrashApplication)
                {
                    App.ExceptionTrapper.Disable();
                    throw new OutOfMemoryException("This is a simulated exception to cause the application to crash.");
                }
                else
                {
                    throw new ApplicationException("This is a simulated exception to cause an unhandled application error.");
                }
            });
        }
    }
}
using Google.Protobuf;
using MaterialDesignThemes.Wpf;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
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.Input;
using System.Windows.Threading;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.Core;
using Tango.Core.Commands;
using Tango.Core.ExtensionMethods;
using Tango.Core.Helpers;
using Tango.FSE.Common;
using Tango.FSE.Common.FileAssociation;
using Tango.FSE.Common.Navigation;
using Tango.FSE.Common.Notifications;
using Tango.FSE.Procedures.Contracts;
using Tango.FSE.Procedures.Dialogs;
using Tango.FSE.Procedures.Messages;
using Tango.FSE.Procedures.Navigation;
using Tango.FSE.Procedures.Views;
using Tango.Integration.Operation;
using Tango.Scripting.Basic;
using Tango.Scripting.Editors;
using Tango.SharedUI.Components;
using Tango.SharedUI.Helpers;
using Tango.Transport;
using static Tango.FSE.Procedures.ViewModels.ProcedureDesignerViewVM;

namespace Tango.FSE.Procedures.ViewModels
{
    public class ProcedureDesignerViewVM : FSEViewModel<IProcedureDesignerView>, IProcedureLogger, INavigationObjectReceiver<NavigationObject>
    {
        public class NavigationObject
        {
            public PublishedProcedureProject Project { get; set; }
        }

        public enum ToolWindows
        {
            Output,
            Errors,
            Inputs,
            Variables,
            Results,
            Publish
        }

        private System.Timers.Timer _compileTimer;
        private String _projectFile;
        private String PROJECT_FILE_EXTENSION = ".pproj";
        private String PROJECT_DIALOG_FILTER = $"Procedure Project Files|*.pproj";
        private bool _isProjectChanged;
        private TaskItem _symbolsTaskItem;
        private BreakPointRequestEventArgs _lastBreakPointRequestArgs;
        private String _lastProjectStringForCompilation;

        #region Properties

        private PublishedProcedureProject _publishedProcedureProject;
        public PublishedProcedureProject PublishedProcedureProject
        {
            get { return _publishedProcedureProject; }
            set { _publishedProcedureProject = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
        }

        private ToolWindows _selectedToolWindow;
        public ToolWindows SelectedToolWindow
        {
            get { return _selectedToolWindow; }
            set { _selectedToolWindow = value; RaisePropertyChangedAuto(); }
        }

        private ProcedureProject _project;
        public ProcedureProject Project
        {
            get { return _project; }
            set { _project = value; RaisePropertyChangedAuto(); OnProjectChanged(); }
        }

        private ProjectRunner _projectRunner;
        public ProjectRunner ProjectRunner
        {
            get { return _projectRunner; }
            set { _projectRunner = value; RaisePropertyChangedAuto(); }
        }

        private List<CompilationError> _compilationErrors;
        public List<CompilationError> CompilationErrors
        {
            get { return _compilationErrors; }
            set { _compilationErrors = value; RaisePropertyChangedAuto(); }
        }

        public ResultsViewVM ResultsViewVM { get; set; }

        public ObservableCollection<Script> OpenScripts { get; set; }

        private Script _selectedScript;
        public Script SelectedScript
        {
            get { return _selectedScript; }
            set
            {
                _selectedScript = value;
                View?.ResetColrization();
                DisplayFindAndReplace = false;
                RaisePropertyChangedAuto();
            }
        }

        public TextController Logger { get; set; }

        private String _status;
        public String Status
        {
            get { return _status; }
            set { _status = value; RaisePropertyChangedAuto(); }
        }

        private TangoProgress<int> _cacheProgress;
        public TangoProgress<int> SymbolsLoadingProgress
        {
            get { return _cacheProgress; }
            set { _cacheProgress = value; RaisePropertyChangedAuto(); }
        }

        private bool _isLoadingSymbols;
        public bool IsLoadingSymbols
        {
            get { return _isLoadingSymbols; }
            set { _isLoadingSymbols = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<Assembly> _loadedAssemblies;
        public ObservableCollection<Assembly> LoadedAssemblies
        {
            get { return _loadedAssemblies; }
            set { _loadedAssemblies = value; RaisePropertyChangedAuto(); }
        }

        private bool _isPublishPanelOpened;
        public bool IsPublishPanelOpened
        {
            get { return _isPublishPanelOpened; }
            set { _isPublishPanelOpened = value; RaisePropertyChangedAuto(); }
        }

        private List<CreateGroup> _createGroups;
        public List<CreateGroup> CreateGroups
        {
            get { return _createGroups; }
            set { _createGroups = value; RaisePropertyChangedAuto(); }
        }

        private bool _displayFindAndReplace;
        public bool DisplayFindAndReplace
        {
            get { return _displayFindAndReplace; }
            set { _displayFindAndReplace = value; RaisePropertyChangedAuto(); }
        }

        private String _findText;
        public String FindText
        {
            get { return _findText; }
            set
            {
                _findText = value;
                RaisePropertyChangedAuto();
                FindNextCommand?.RaiseCanExecuteChanged();
                ReplaceCommand?.RaiseCanExecuteChanged();
                ReplaceAllCommand?.RaiseCanExecuteChanged();
                View?.ColorizeKeyword(value);
            }
        }

        private String _replaceText;
        public String ReplaceText
        {
            get { return _replaceText; }
            set { _replaceText = value; RaisePropertyChangedAuto(); ReplaceCommand?.RaiseCanExecuteChanged(); ReplaceAllCommand?.RaiseCanExecuteChanged(); }
        }

        private double _fontSize;
        public double FontSize
        {
            get { return _fontSize; }
            set
            {
                if (value > 5 && value < 41)
                {
                    _fontSize = value;
                    RaisePropertyChangedAuto();
                }
            }
        }

        private Exception _runtimeException;
        public Exception RuntimeException
        {
            get { return _runtimeException; }
            set { _runtimeException = value; RaisePropertyChangedAuto(); }
        }

        private bool _runtimeErrorFree;
        public bool RuntimeErrorFree
        {
            get { return _runtimeErrorFree; }
            set { _runtimeErrorFree = value; RaisePropertyChangedAuto(); }
        }

        private bool _isBreakPoint;
        public bool IsBreakPoint
        {
            get { return _isBreakPoint; }
            set { _isBreakPoint = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
        }

        private bool _isReadOnly;
        public bool IsReadOnly
        {
            get { return _isReadOnly; }
            set { _isReadOnly = value; RaisePropertyChangedAuto(); }
        }

        #endregion

        #region Commands

        public RelayCommand<Script> OpenScriptCommand { get; set; }
        public RelayCommand<Script> CloseScriptCommand { get; set; }
        public RelayCommand RunProjectCommand { get; set; }
        public RelayCommand StopProjectCommand { get; set; }
        public RelayCommand CompileProjectCommand { get; set; }
        public RelayCommand AddReferenceAssemblyCommand { get; set; }
        public RelayCommand<ReferenceAssembly> RemoveReferenceAssemblyCommand { get; set; }
        public RelayCommand AddProjectInputCommand { get; set; }
        public RelayCommand<ProcedureInput> RemoveProjectInputCommand { get; set; }
        public RelayCommand FormatCodeCommand { get; set; }
        public RelayCommand<CompilationError> HighlightErrorCommand { get; set; }
        public RelayCommand NewProjectCommand { get; set; }
        public RelayCommand SaveProjectCommand { get; set; }
        public RelayCommand SaveAsProjectCommand { get; set; }
        public RelayCommand OpenProjectCommand { get; set; }
        public RelayCommand AddLibraryCommand { get; set; }
        public RelayCommand<Script> DeleteLibraryCommand { get; set; }
        public RelayCommand ClearOutputCommand { get; set; }
        public RelayCommand PublishProjectCommand { get; set; }
        public RelayCommand LoadPublishedProjectCommand { get; set; }
        public RelayCommand UnPublishProjectCommand { get; set; }
        public RelayCommand TogglePublishPanelCommand { get; set; }
        public RelayCommand<Script> RenameLibraryCommand { get; set; }
        public RelayCommand<CreateItem> CreateItemCommand { get; set; }
        public RelayCommand ShowFindAndReplaceCommand { get; set; }
        public RelayCommand HideFindAndReplaceCommand { get; set; }
        public RelayCommand FindNextCommand { get; set; }
        public RelayCommand ReplaceCommand { get; set; }
        public RelayCommand ReplaceAllCommand { get; set; }
        public RelayCommand IncreaseFontSizeCommand { get; set; }
        public RelayCommand DecreaseFontSizeCommand { get; set; }
        public RelayCommand AddDialogCommand { get; set; }
        public RelayCommand<ProcedureDialog> EditDialogCommand { get; set; }
        public RelayCommand<ProcedureDialog> RemoveDialogCommand { get; set; }
        public RelayCommand<ProcedureInput> ConfigureInputSelectionCommand { get; set; }
        public RelayCommand AddResourceCommand { get; set; }
        public RelayCommand<ProcedureResource> RenameResourceCommand { get; set; }
        public RelayCommand<ProcedureResource> RemoveResourceCommand { get; set; }
        public RelayCommand<ProcedureResource> OpenResourceCommand { get; set; }
        public RelayCommand<ProcedureResource> ExportResourceCommand { get; set; }
        public RelayCommand CloseRuntimeErrorCommand { get; set; }
        public RelayCommand ContinueProjectCommand { get; set; }
        public RelayCommand OpenHelpCommand { get; set; }
        public RelayCommand<DebugNode> DisplayDebugNodeValueCommand { get; set; }
        public RelayCommand RunOnProceduresModuleCommand { get; set; }


        #endregion

        #region Constructors

        public ProcedureDesignerViewVM()
        {
            Status = "Ready";

            FontSize = 13;
            ReplaceText = String.Empty;
            RuntimeErrorFree = true;

            LoadedAssemblies = new ObservableCollection<Assembly>();
            ResultsViewVM = new ResultsViewVM();
            CompilationErrors = new List<CompilationError>();
            Logger = new TextController();
            ScriptEditor.LoadingSymbolsProgress += ScriptEditor_LoadingSymbolsProgress;
            ScriptEditor.LoadingSymbolsStarted += ScriptEditor_LoadingSymbolsStarted;
            ScriptEditor.LoadingSymbolsCompleted += ScriptEditor_LoadingSymbolsCompleted;
            ScriptEditor.UsingsLoadingStarted += ScriptEditor_UsingsLoadingStarted;
            ScriptEditor.UsingsLoadingCompleted += ScriptEditor_UsingsLoadingCompleted;
            ScriptEditor.BlockedUsingsCache.Add("Tango");

            OpenScripts = new ObservableCollection<Script>();
            OpenScriptCommand = new RelayCommand<Script>(OpenScript);
            CloseScriptCommand = new RelayCommand<Script>(CloseScript);
            RunProjectCommand = new RelayCommand(RunProject, () => ProjectRunner != null && (ProjectRunner.CanRun || IsBreakPoint));
            RunOnProceduresModuleCommand = new RelayCommand(RunOnProceduresModule, () => ProjectRunner != null && (ProjectRunner.CanRun || IsBreakPoint));
            StopProjectCommand = new RelayCommand(StopProject, () => ProjectRunner != null && ProjectRunner.IsRunning);
            CompileProjectCommand = new RelayCommand(async () => await CompileProject(), () => ProjectRunner != null && ProjectRunner.CanCompile);
            AddReferenceAssemblyCommand = new RelayCommand(AddReferenceAssembly);
            RemoveReferenceAssemblyCommand = new RelayCommand<ReferenceAssembly>(RemoveReferenceAssembly);
            AddProjectInputCommand = new RelayCommand(AddProjectInput);
            RemoveProjectInputCommand = new RelayCommand<ProcedureInput>(RemoveProjectInput);
            FormatCodeCommand = new RelayCommand(FormatCode);
            HighlightErrorCommand = new RelayCommand<CompilationError>(HighlightError);
            NewProjectCommand = new RelayCommand(CreateNewProject, () => ProjectRunner == null || !ProjectRunner.IsRunning);
            SaveProjectCommand = new RelayCommand(SaveProject);
            SaveAsProjectCommand = new RelayCommand(SaveAsProject);
            OpenProjectCommand = new RelayCommand(OpenProject);
            AddLibraryCommand = new RelayCommand(AddNewLibrary);
            ClearOutputCommand = new RelayCommand(Clear);
            DeleteLibraryCommand = new RelayCommand<Script>(DeleteLibrary);
            PublishProjectCommand = new RelayCommand(PublishProject, () => CurrentUser.HasPermission(Permissions.FSE_PublishProcedureProjects));
            LoadPublishedProjectCommand = new RelayCommand(LoadPublishedProject);
            UnPublishProjectCommand = new RelayCommand(UnPublishProject);
            TogglePublishPanelCommand = new RelayCommand(() => IsPublishPanelOpened = !IsPublishPanelOpened, () => CurrentUser.HasPermission(Permissions.FSE_PublishProcedureProjects));
            RenameLibraryCommand = new RelayCommand<Script>(RenameLibrary);
            CreateItemCommand = new RelayCommand<CreateItem>(AutoCreateItem);
            ShowFindAndReplaceCommand = new RelayCommand(() => { DisplayFindAndReplace = true; this.SetFocus(() => FindText); View.ColorizeKeyword(FindText); });
            HideFindAndReplaceCommand = new RelayCommand(() => { DisplayFindAndReplace = false; View.FocusCurrentEditor(); View.ResetColrization(); });
            FindNextCommand = new RelayCommand(FindNext, () => !String.IsNullOrEmpty(FindText));
            ReplaceCommand = new RelayCommand(ReplaceNext, () => !String.IsNullOrEmpty(FindText));
            ReplaceAllCommand = new RelayCommand(ReplaceAll, () => !String.IsNullOrEmpty(FindText));
            IncreaseFontSizeCommand = new RelayCommand(() => FontSize++);
            DecreaseFontSizeCommand = new RelayCommand(() => FontSize--);
            AddDialogCommand = new RelayCommand(AddProcedureDialog);
            EditDialogCommand = new RelayCommand<ProcedureDialog>(EditProcedureDialog);
            RemoveDialogCommand = new RelayCommand<ProcedureDialog>(RemoveProcedureDialog);
            ConfigureInputSelectionCommand = new RelayCommand<ProcedureInput>(ConfigureInputSelection);
            AddResourceCommand = new RelayCommand(AddProcedureResource);
            RenameResourceCommand = new RelayCommand<ProcedureResource>(RenameProcedureResource);
            RemoveResourceCommand = new RelayCommand<ProcedureResource>(RemoveProcedureResource);
            OpenResourceCommand = new RelayCommand<ProcedureResource>(OpenProcedureResource);
            ExportResourceCommand = new RelayCommand<ProcedureResource>(ExportProcedureResource);
            CloseRuntimeErrorCommand = new RelayCommand(CloseRunTimeError);
            ContinueProjectCommand = new RelayCommand(ContinueProject);
            OpenHelpCommand = new RelayCommand(OpenHelpFile);
            DisplayDebugNodeValueCommand = new RelayCommand<DebugNode>(DisplayDebugNodeValue);
        }

        #endregion

        #region Auto Creation

        private void InitCreateGroups()
        {
            var groups = new List<CreateGroup>();

            foreach (var typesGroup in typeof(PMR.Common.MessageContainer).Assembly.GetTypes().Where(x => x.IsClass && !x.IsGenericType && !x.Name.Contains("Reflection") && typeof(IMessage).IsAssignableFrom(x)).GroupBy(x => x.Namespace))
            {
                CreateGroup group = new CreateGroup();
                group.Name = typesGroup.First().Namespace.Split('.').Last();

                foreach (var type in typesGroup)
                {
                    group.Items.Add(new CreateItem()
                    {
                        Name = type.Name,
                        Type = type,
                    });
                }

                groups.Add(group);
            }

            CreateGroups = groups;
        }

        private void AutoCreateItem(CreateItem item)
        {
            if (item != null)
            {
                try
                {
                    String code = String.Empty;
                    FormatProperties(item.Type, ref code);
                    View.InsertCode(code);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, $"Error generating code for {item.Type.Name}.");
                    NotificationProvider.ShowError($"Error generating code for {item.Type.Name}.");
                }
            }
        }

        private void FormatProperties(Type type, ref String code)
        {
            code += Environment.NewLine + String.Format("{0} {1} = new {0}();", type.Name, type.Name.ToCamelCase()) + Environment.NewLine;

            foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                if (prop.PropertyType == typeof(String))
                {
                    code += String.Format("{0}.{1} = {2};", type.Name.ToCamelCase(), prop.Name, "null") + Environment.NewLine;
                }
                else if (prop.PropertyType.IsEnum)
                {
                    code += String.Format("{0}.{1} = {2};", type.Name.ToCamelCase(), prop.Name, Activator.CreateInstance(prop.PropertyType).GetType().FullName + "." + Activator.CreateInstance(prop.PropertyType).ToString()) + Environment.NewLine;
                }
                else if (!prop.PropertyType.IsClass)
                {
                    code += String.Format("{0}.{1} = {2};", type.Name.ToCamelCase(), prop.Name, Activator.CreateInstance(prop.PropertyType).ToString().ToLower()) + Environment.NewLine;
                }
                else if (prop.PropertyType.IsGenericType)
                {
                    Type genericType = prop.PropertyType.GenericTypeArguments[0];
                    FormatProperties(genericType, ref code);
                    code += String.Format("{0}.{1}.Add({2});", type.Name.ToCamelCase(), prop.Name, genericType.Name.ToCamelCase()) + Environment.NewLine;
                }
                else
                {
                    FormatProperties(prop.PropertyType, ref code);
                    code += Environment.NewLine + String.Format("{0}.{1} = {2};", type.Name.ToCamelCase(), prop.Name, prop.Name.ToCamelCase()) + Environment.NewLine;
                }
            }
        }

        #endregion

        #region Override Methods

        public override void OnApplicationStarted()
        {
            _compileTimer = new System.Timers.Timer(2000);
            _compileTimer.Elapsed += _compileTimer_Tick;
            _compileTimer.Start();

            Task.Factory.StartNew(() =>
            {
                try
                {
                    InitCreateGroups();
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error generating procedure designer auto creation groups.");
                }
            });

            FileAssociationProvider.RegisterFileAssociationHandler("designer", HandlerProcedureFileAssociation);
        }

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

            if (Project == null)
            {
                CreateStartupProject();
            }
        }

        public override void OnViewAttached()
        {
            base.OnViewAttached();
            View.FileDropped += View_FileDropped;
        }

        #endregion

        #region Script Editor Symbols Loading Handlers

        private void ScriptEditor_LoadingSymbolsCompleted(object sender, EventArgs e)
        {
            IsLoadingSymbols = false;
        }

        private void ScriptEditor_LoadingSymbolsStarted(object sender, EventArgs e)
        {
            IsLoadingSymbols = true;
        }

        private void ScriptEditor_LoadingSymbolsProgress(object sender, Core.TangoProgressChangedEventArgs<int> e)
        {
            SymbolsLoadingProgress = e.Progress;

            _symbolsTaskItem?.UpdateProgress(e.Progress.Message, e.Progress.Value, e.Progress.Maximum, e.Progress.IsIndeterminate);
        }

        private void ScriptEditor_UsingsLoadingStarted(object sender, EventArgs e)
        {
            _symbolsTaskItem = NotificationProvider.PushTaskItem("Loading symbols...");
        }

        private void ScriptEditor_UsingsLoadingCompleted(object sender, EventArgs e)
        {
            if (_symbolsTaskItem != null)
            {
                NotificationProvider.PopTaskItem(_symbolsTaskItem);
                _symbolsTaskItem = null;
            }
        }

        #endregion

        #region Project Start/Stop Compile

        private void StopProject()
        {
            ProjectRunner.Stop();
            ContinueProject();
            IsReadOnly = false;
        }

        private async Task<bool> CompileProject()
        {
            try
            {
                Mouse.OverrideCursor = Cursors.AppStarting;
                IsFree = false;
                Logger.Clear();
                SelectedToolWindow = ToolWindows.Output;
                Logger.WriteLine("Compiling project...");
                Status = "Compiling project...";
                UIHelper.DoEvents();

                var result = await ProjectRunner.Compile();
                CompilationErrors = result.Errors;

                View.ClearErrors();

                if (CompilationErrors.Count > 0)
                {
                    Logger.WriteLine("Project compiled with errors.");
                    Status = "Project compiled with errors.";
                    SelectedToolWindow = ToolWindows.Errors;

                    foreach (var error in CompilationErrors)
                    {
                        if (error.File == SelectedScript.Name)
                        {
                            View.HighlightError(error.Position, error.Length);
                        }
                    }

                    return false;
                }
                else
                {
                    SelectedToolWindow = ToolWindows.Output;
                    Status = "Project compiled successfully.";
                    Logger.WriteLine("Project compiled successfully.");
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLine($"Error compiling the project.\n{ex.FlattenMessage()}");
                await NotificationProvider.ShowError($"Error compiling the project.\n{ex.FlattenMessage()}");
                return false;
            }
            finally
            {
                Mouse.OverrideCursor = null;
                Status = "Ready";
                IsFree = true;
            }
        }

        private async void RunProject()
        {
            if (IsBreakPoint)
            {
                ContinueProject();
                return;
            }

            try
            {
                if (await CompileProject())
                {
                    IsReadOnly = true;
                    SelectedToolWindow = ToolWindows.Output;
                    ResultsViewVM.Results = new List<Result>();
                    Logger.Clear();
                    Logger.WriteLine("Running project...");

                    var context = new ProcedureContext(Project, this);

                    context.BreakPointRequest += Context_BreakPointRequest;

                    try
                    {
                        Project.BreakPoints = View.GetBreakPoints();
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error initializing break points for project.");
                    }

                    await ProjectRunner.Run(context);

                    ResultsViewVM.Results = context.Results.ToList();

                    if (ResultsViewVM.Results.Count > 0)
                    {
                        Logger.WriteLine("Project ran to completion with procedure results:");
                        Logger.WriteLine(ResultsViewVM.Results.ToJsonString());
                        SelectedToolWindow = ToolWindows.Results;
                    }
                    else
                    {
                        Logger.WriteLine("Project ran to completion with zero results.");
                    }

                    IsReadOnly = false;
                }
            }
            catch (OperationCanceledException)
            {
                IsReadOnly = false;
                Logger.WriteLine("Project terminated by user.");
            }
            catch (Exception ex)
            {
                IsReadOnly = false;
                SelectedToolWindow = ToolWindows.Output;
                Logger.WriteLine("Project terminated with error:");
                Logger.WriteLine(ex.FlattenMessage());

                try
                {
                    int? lineNumber = Helpers.ProcedureExceptionHelper.GetExceptionLineNumber(ex, Project);

                    if (lineNumber != null)
                    {
                        OpenScript(Project.Scripts.FirstOrDefault(x => x.IsEntryPoint));
                        RuntimeException = ex;
                        View.HighlightRuntimeError(lineNumber.Value);
                        RuntimeErrorFree = false;
                        IsReadOnly = true;
                    }
                }
                catch (Exception exx)
                {
                    LogManager.Log(exx, "Error occurred while trying to show procedure runtime error.");
                }
            }
            finally
            {
                Project.BreakPoints.Clear();
            }
        }

        private async void RunOnProceduresModule()
        {
            if (await CompileProject())
            {
                await NavigationManager.NavigateWithObject<ProceduresModule, ProcedureRunnerView, RunProcedureNavigationObject>(new RunProcedureNavigationObject()
                {
                    Project = Project,
                    StartProcedure = true
                });
            }
        }

        #endregion

        #region Open/Close Script Tabs

        private void CloseScript(Script script)
        {
            script.IsSelected = false;
            OpenScripts.Remove(script);
            SelectedScript = OpenScripts.FirstOrDefault();
        }

        internal void OpenScript(Script script)
        {
            if (!OpenScripts.Contains(script))
            {
                OpenScripts.Add(script);
            }

            SelectedScript = script;
        }

        private void ClearOpenedScripts()
        {
            OpenScripts.Clear();
            SelectedScript = null;
        }

        #endregion

        #region Event Handlers

        private void ProjectRunner_StateChanged(object sender, ProjectRunnerState e)
        {
            InvalidateRelayCommands();
        }

        private async void _compileTimer_Tick(object sender, EventArgs e)
        {
            if (!IsVisible) return;

            _compileTimer.Stop();

            if (Project != null && !Project.IsCompiling && !Project.IsRunning)
            {
                String projectString = String.Empty;

                projectString = String.Join(Environment.NewLine, Project.Scripts.ToList().Select(x => x.Code));
                projectString += String.Join(Environment.NewLine, Project.ReferenceAssemblies.ToList().Select(x => x.Name));

                if (_lastProjectStringForCompilation != projectString)
                {
                    _lastProjectStringForCompilation = projectString;

                    CompilationErrors = (await Project.Compile()).Errors;

                    if (CompilationErrors.Count > 0 && (SelectedToolWindow == ToolWindows.Output || SelectedToolWindow == ToolWindows.Results))
                    {
                        SelectedToolWindow = ToolWindows.Errors;
                    }

                    InvokeUI(() =>
                    {
                        View.ClearErrors();

                        foreach (var error in CompilationErrors)
                        {
                            if (error.File == SelectedScript.Name)
                            {
                                View.HighlightError(error.Position, error.Length);
                            }
                        }
                    });
                }

                _lastProjectStringForCompilation = projectString;
            }

            _compileTimer.Start();
        }

        #endregion

        #region Reference Assemblies

        private async void AddReferenceAssembly()
        {
            var vm = await NotificationProvider.ShowDialog<AddReferenceAssemblyViewVM>(new AddReferenceAssemblyViewVM(Project.ReferenceAssemblies.ToList()));
            if (vm.DialogResult)
            {
                Project.ReferenceAssemblies.Clear();

                foreach (var asm in vm.ReferenceAssemblies.SynchedSource)
                {
                    Project.ReferenceAssemblies.Add(asm);
                }

                _isProjectChanged = true;

                LoadReferenceAssemblies();
            }
        }

        private void LoadReferenceAssemblies()
        {
            LoadedAssemblies = Project.LoadReferenceAssemblies().ToObservableCollection();
        }

        private void RemoveReferenceAssembly(ReferenceAssembly assembly)
        {
            Project.ReferenceAssemblies.Remove(assembly);
            _isProjectChanged = true;
            LoadedAssemblies = Project.LoadReferenceAssemblies().ToObservableCollection();
        }

        #endregion

        #region Project Management

        private async void CreateNewProject()
        {
            if (await CheckDiscardProjectChanges())
            {
                var result = await NotificationProvider.ShowInputBox("New Project", "Please specify the project name", PackIconKind.TestTube, "untitled", "Project Name", 100, "CREATE");
                if (result.Confirmed)
                {
                    Project = ProcedureProject.New(result.Input);
                    _projectFile = null;
                    PublishedProcedureProject = null;
                }
            }
        }

        private void CreateStartupProject()
        {
            Project = ProcedureProject.New("untitled");
            _projectFile = null;
        }

        private void SaveProject()
        {
            if (Project != null)
            {
                if (_projectFile == null)
                {
                    SaveAsProject();
                    return;
                }

                try
                {
                    Status = "Saving project...";

                    File.WriteAllText(_projectFile, Project.ToJson());

                    Status = "Project saved.";

                    NotificationProvider.PushSnackbarItem(MessageType.Success, "Procedure project saved", false, $"Project '{Project.Name}' saved successfully.", TimeSpan.FromSeconds(1.5));

                    Project.Scripts.ToList().ForEach(x => x.IsChanged = false);
                    _isProjectChanged = false;
                }
                catch (Exception ex)
                {
                    NotificationProvider.ShowError($"Error saving project\n{ex.FlattenMessage()}");
                    Status = "Ready";
                }
            }
        }

        private async void SaveAsProject()
        {
            if (Project != null)
            {
                var result = await StorageProvider.SaveFile("Save As Project", PROJECT_DIALOG_FILTER, Project.Name, PROJECT_FILE_EXTENSION);

                if (result)
                {
                    try
                    {
                        Status = "Saving project...";

                        File.WriteAllText(result.SelectedItem, Project.ToJson());
                        _projectFile = result.SelectedItem;

                        Status = "Project saved.";

                        Project.Name = Path.GetFileNameWithoutExtension(result.SelectedItem);

                        Project.Scripts.ToList().ForEach(x => x.IsChanged = false);
                        _isProjectChanged = false;

                        NotificationProvider.PushSnackbarItem(MessageType.Success, "Procedure project saved", false, $"Project '{Project.Name}' saved successfully.", TimeSpan.FromSeconds(1.5));
                    }
                    catch (Exception ex)
                    {
                        await NotificationProvider.ShowError($"Error saving project\n{ex.FlattenMessage()}");
                        Status = "Ready";
                    }
                }
            }
        }

        private void OnProjectChanged()
        {
            if (Project != null)
            {
                _isProjectChanged = false;
                LoadReferenceAssemblies();
                ProjectRunner = new ProjectRunner(Project);
                ProjectRunner.StateChanged += ProjectRunner_StateChanged;
                ClearOpenedScripts();

                var programScript = Project.Scripts.SingleOrDefault(x => x.IsEntryPoint);

                if (programScript != null)
                {
                    OpenScript(programScript);
                }
            }
            else
            {
                ProjectRunner = null;
            }

            InvalidateRelayCommands();

            View.InvalidateHighlighting();
        }

        private async void OpenProject()
        {
            if (await CheckDiscardProjectChanges())
            {
                var result = await StorageProvider.OpenFile("Open Procedure Project", PROJECT_DIALOG_FILTER);

                if (result)
                {
                    OpenProject(result.SelectedItem);
                }
            }
        }

        private void OpenProject(String file)
        {
            if (Project != null && Project.IsRunning)
            {
                NotificationProvider.ShowError("Cannot load a project while another project is running.");
                return;
            }

            try
            {
                Project = ProcedureProject.FromJson(File.ReadAllText(file));
                Project.Name = Path.GetFileNameWithoutExtension(file);
                _projectFile = file;
                PublishedProcedureProject = null;
                Status = "Project loaded.";
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error opening procedure project.");
                NotificationProvider.ShowError($"Error occurred while trying to open the project.\n{ex.FlattenMessage()}");
            }
        }

        private async Task<bool> CheckDiscardProjectChanges()
        {
            if (Project != null && (Project.Scripts.Any(x => x.IsChanged) || _isProjectChanged))
            {
                return await NotificationProvider.ShowWarningQuestion("The current project contains unsaved changes. Discard the changes?");
            }
            else
            {
                return true;
            }
        }

        #endregion

        #region Libraries

        private async void AddNewLibrary()
        {
            var result = await NotificationProvider.ShowInputBox("New Library File", "Please specify the library name", PackIconKind.Script, "MyLibrary", "Library Name", 100, "ADD LIBRARY");

            if (result.Confirmed)
            {
                if (Project.Scripts.Any(x => x.Name.ToLower() == result.Input.ToLower() + ".csx"))
                {
                    await NotificationProvider.ShowError($"The project already contains a file named '{result.Input}'.");
                    return;
                }

                try
                {
                    var lib = Script.New(result.Input + ".csx", Encoding.UTF8.GetString(Properties.Resources.lib_template).Replace("@LibraryName", result.Input));
                    Project.Scripts.Add(lib);
                    _isProjectChanged = true;
                    OpenScript(lib);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error adding new library to procedure project.");
                    await NotificationProvider.ShowError($"Could not add a new script to the project.\n{ex.FlattenMessage()}");
                }
            }
        }

        private async void DeleteLibrary(Script script)
        {
            if (script.IsEntryPoint)
            {
                await NotificationProvider.ShowWarning("Entry point file cannot be deleted.");
                return;
            }

            if (await NotificationProvider.ShowWarningQuestion($"Are you sure you want to delete '{script.Name}'?"))
            {
                CloseScript(script);
                Project.Scripts.Remove(script);
                _isProjectChanged = true;
            }
        }

        private async void RenameLibrary(Script script)
        {
            var result = await NotificationProvider.ShowInputBox("Rename Library File", "Please specify a new library name", PackIconKind.Rename, Path.GetFileNameWithoutExtension(script.Name), "Library Name", 100, "RENAME");

            if (result.Confirmed)
            {
                if (Project.Scripts.Any(x => x != script && x.Name.ToLower() == result.Input.ToLower() + ".csx"))
                {
                    await NotificationProvider.ShowError($"The project already contains a file named '{result.Input}'.");
                    return;
                }

                script.Name = result.Input + ".csx";
                script.IsChanged = true;
            }
        }

        #endregion

        #region IProcedureLogger

        public void WriteLine(string text)
        {
            Logger.WriteLine(text);
        }

        public void Write(string text)
        {
            Logger.Write(text);
        }

        public void Clear()
        {
            Logger.Clear();
        }

        #endregion

        #region Inputs

        private void AddProjectInput()
        {
            Project.Inputs.Add(new ProcedureInput()
            {
                Key = "param" + (Project.Inputs.Count + 1),
                Value = "0",
                DisplayName = "Parameter " + (Project.Inputs.Count + 1),
                Description = "Controls the " + (Project.Inputs.Count + 1 + " parameter."),
            });
        }

        private void RemoveProjectInput(ProcedureInput input)
        {
            Project.Inputs.Remove(input);
        }

        #endregion

        #region Publish

        private async void LoadPublishedProject()
        {
            try
            {
                IsFree = false;

                List<PublishedProcedureProject> projects = new List<PublishedProcedureProject>();

                using (NotificationProvider.PushTaskItem("Retrieving published projects..."))
                {
                    projects = await Services.PublishedProcedureProjectsService.GetPublishedProcedureProjects(false);
                }

                if (projects.Count == 0)
                {
                    await NotificationProvider.ShowInfo("No published procedure projects found. Please publish a procedure project before trying to load one.");
                    return;
                }

                IsFree = true;

                var vm = await NotificationProvider.ShowDialog<LoadPublishedProjectViewVM>(new LoadPublishedProjectViewVM(projects));

                IsFree = false;

                if (vm.DialogResult)
                {
                    await LoadPublishedProject(vm.SelectedProject);
                    await NotificationProvider.ShowSuccess("Project loaded successfully.");
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error retrieving published procedure projects.");
                await NotificationProvider.ShowError($"Error occurred while trying to fetch published procedure projects.\n{ex.FlattenMessage()}");
            }
            finally
            {
                IsFree = true;
            }
        }

        private async Task LoadPublishedProject(PublishedProcedureProject project)
        {
            try
            {
                if (project == null)
                {
                    throw new NullReferenceException("No project provided.");
                }

                if (!await CheckDiscardProjectChanges()) return;

                using (NotificationProvider.PushTaskItem("Loading published project..."))
                {
                    await Task.Delay(1000);
                    Project = ProcedureProject.FromJson(project.CurrentVersion.ProjectJsonString);
                    PublishedProcedureProject = project;
                    _projectFile = null;
                    await Task.Delay(1000);
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error loading published procedure project.");
                await NotificationProvider.ShowError($"Error loading published procedure project.\n{ex.FlattenMessage()}");
            }
        }

        private async void PublishProject()
        {
            try
            {
                if (!Project.Name.IsNotNullOrEmpty())
                {
                    await NotificationProvider.ShowError("Please provide a project name.");
                    return;
                }

                if (PublishedProcedureProject == null || !PublishedProcedureProject.IsVisible)
                {
                    if (!await NotificationProvider.ShowWarningQuestion("Publishing this project will make it available for all users via the Procedures module.", "PUBLISH"))
                    {
                        return;
                    }
                }
                else
                {
                    if (!await NotificationProvider.ShowWarningQuestion("This project is already published. Do you want to update the published project?", "UPDATE"))
                    {
                        return;
                    }
                }

                IsFree = false;

                using (NotificationProvider.PushTaskItem("Publishing procedure project..."))
                {
                    PublishedProcedureProject = await Services.PublishedProcedureProjectsService.PublishProcedureProject(
                        PublishedProcedureProject?.Guid,
                        Project.Name,
                        Project.Description,
                        Project.ToJson(),
                        Project.Visibility);

                    RaiseMessage<ProcedureProjectPublishedOrSuppressed>();
                }

                await NotificationProvider.ShowSuccess("Your procedure project is now published!\nNow you can execute this project from the Procedures module.");
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error publishing procedure project.");
                await NotificationProvider.ShowError($"Error publishing procedure project.\n{ex.FlattenMessage()}");
            }
            finally
            {
                IsFree = true;
            }
        }

        private async void UnPublishProject()
        {
            if (!await NotificationProvider.ShowWarningQuestion("Suppressing this project will make it unavailable on the Procedures module.\nAre you sure?", "SUPPRESS"))
            {
                return;
            }

            try
            {
                using (NotificationProvider.PushTaskItem("Suppressing procedure project..."))
                {
                    PublishedProcedureProject = await Services.PublishedProcedureProjectsService.UnPublishProcedureProject(PublishedProcedureProject);
                    RaiseMessage<ProcedureProjectPublishedOrSuppressed>();
                }

                await NotificationProvider.ShowInfo("This procedure project will no longer be available on the Procedures module.");
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error suppressing procedure project.");
                await NotificationProvider.ShowError($"Error occurred while trying to suppress this procedure project.\n{ex.FlattenMessage()}");
            }
        }

        #endregion

        #region View

        private void HighlightError(CompilationError error)
        {
            var errorScript = Project.Scripts.SingleOrDefault(x => x.Name == error.File);

            if (errorScript != null)
            {
                OpenScript(errorScript);
                View.HighlightCode(error.Position, error.Length, error.Line);
            }
        }

        private void FormatCode()
        {
            View.FormatCode();
        }

        #endregion

        #region INavigationObjectReceiver

        public async void OnNavigatedToWithObject(NavigationObject obj)
        {
            await Task.Delay(500);
            await LoadPublishedProject(obj.Project);
        }

        #endregion

        #region Find & Replace

        private void FindNext()
        {
            View.Find(FindText);
        }

        private void ReplaceAll()
        {
            var count = View.ReplaceAll(FindText, ReplaceText);

            if (count > 0)
            {
                NotificationProvider.ShowInfo($"{count} instances of the keyword were replaced.");
            }
        }

        private void ReplaceNext()
        {
            View.ReplaceNext(FindText, ReplaceText);
        }

        #endregion

        #region Dialogs

        private async void AddProcedureDialog()
        {
            var result = await NotificationProvider.ShowInputBox("New Procedure Dialog", "Please specify the dialog name", PackIconKind.WindowRestore, $"Dialog{Project.Dialogs.Count + 1}", "Dialog Name", 30);

            if (result.Confirmed)
            {
                if (Project.Dialogs.Any(x => x.Name.ToLower() == result.Input.ToLower() + ".xaml"))
                {
                    await NotificationProvider.ShowError($"The project already contains a dialog named '{result.Input}'.");
                    return;
                }

                Project.Dialogs.Add(new ProcedureDialog()
                {
                    Name = result.Input + ".xaml",
                    Xaml = Properties.Resources.dialog_template
                });

                _isProjectChanged = true;
            }
        }

        internal async void EditProcedureDialog(ProcedureDialog dialog)
        {
            var vm = await NotificationProvider.ShowDialog(new ProcedureDialogEditorViewVM()
            {
                Name = Path.GetFileNameWithoutExtension(dialog.Name),
                Xaml = dialog.Xaml,
            });

            if (vm.DialogResult)
            {
                if (Project.Dialogs.Any(x => x != dialog && x.Name.ToLower() == vm.Name.ToLower() + ".xaml"))
                {
                    await NotificationProvider.ShowError($"The project already contains a dialog named '{vm.Name}'.");
                    return;
                }

                dialog.Name = vm.Name + ".xaml";
                dialog.Xaml = vm.Xaml;

                _isProjectChanged = true;
            }
        }

        private async void RemoveProcedureDialog(ProcedureDialog dialog)
        {
            if (await NotificationProvider.ShowWarningQuestion($"Are you sure you want to delete '{dialog.Name}'?"))
            {
                Project.Dialogs.Remove(dialog);
                _isProjectChanged = true;
            }
        }

        #endregion

        #region Configure Inputs

        private async void ConfigureInputSelection(ProcedureInput input)
        {
            var vm = await NotificationProvider.ShowDialog(new InputSelectionConfigurationDialogViewVM()
            {
                SelectionInputs = input.SelectionInputs.ToList().ToObservableCollection(),
            });

            if (vm.DialogResult)
            {
                input.SelectionInputs = vm.SelectionInputs.ToObservableCollection();
                _isProjectChanged = true;
            }
        }

        #endregion

        #region Resources

        private async void AddProcedureResource()
        {
            var result = await StorageProvider.OpenFiles("Add procedure resources");

            if (result.Confirmed)
            {
                foreach (var file in result.SelectedItems)
                {
                    String name = Path.GetFileName(file);
                    if (Project.Resources.Any(x => x.Name.ToLower() == name.ToLower()))
                    {
                        await NotificationProvider.ShowError($"The procedure already contains a resource named '{name}'. Cannot add resource.");
                        continue;
                    }

                    long fileSize = (new FileInfo(file).Length / 1024) / 1024;

                    if (fileSize > 2)
                    {
                        await NotificationProvider.ShowError($"Resource '{name}' exceeds the maximum allowed resource size of 2MB. Cannot add resource.");
                        continue;
                    }

                    Project.Resources.Add(new ProcedureResource()
                    {
                        Name = name,
                        Data = File.ReadAllBytes(file)
                    });
                }

                _isProjectChanged = true;
            }
        }

        private async void RemoveProcedureResource(ProcedureResource resource)
        {
            if (await NotificationProvider.ShowWarningQuestion($"Are you sure you want to delete resource '{resource.Name}'?"))
            {
                Project.Resources.Remove(resource);
                _isProjectChanged = true;
            }
        }

        private async void RenameProcedureResource(ProcedureResource resource)
        {
            var result = await NotificationProvider.ShowInputBox("Rename Resource", "Please specify a new resource name", PackIconKind.Rename, resource.Name, "Resource Name", 100, "RENAME");

            if (result.Confirmed)
            {
                if (Project.Resources.Any(x => x != resource && x.Name.ToLower() == result.Input.ToLower()))
                {
                    await NotificationProvider.ShowError($"The project already contains a resource named '{result.Input}'.");
                    return;
                }

                resource.Name = result.Input;
                _isProjectChanged = true;
            }
        }

        public void OpenProcedureResource(ProcedureResource resource)
        {
            var tempFolder = TemporaryManager.CreateFolder();
            var tempFile = Path.Combine(tempFolder, resource.Name);
            File.WriteAllBytes(tempFile, resource.Data);
            Process.Start(tempFile);
        }

        private async void ExportProcedureResource(ProcedureResource resource)
        {
            var result = await StorageProvider.SaveFile("Export Procedure Resource", null, resource.Name, Path.GetExtension(resource.Name));

            if (result.Confirmed)
            {
                File.WriteAllBytes(result.SelectedItem, resource.Data);
            }

            NotificationProvider.PushSnackbarItem(MessageType.Success, "Export Procedure Resource", true, "Procedure resource exported successfully.\nTap to browse the file location.", TimeSpan.FromSeconds(5), null, () =>
            {
                StorageProvider.ShowInExplorer(result.SelectedItem);
            });
        }

        #endregion

        #region Runtime Error

        private void CloseRunTimeError()
        {
            View.CloseRunTimeError();
            RuntimeErrorFree = true;
            IsReadOnly = false;
        }

        #endregion

        #region BreakPoint Request

        private void Context_BreakPointRequest(object sender, BreakPointRequestEventArgs e)
        {
            try
            {
                _lastBreakPointRequestArgs = e;
                OpenScript(e.Script);
                View.HighlightBreakPointRequest(e.LineNumber, e.Symbols);
                IsBreakPoint = true;
            }
            catch (Exception ex)
            {
                e.Release();
                LogManager.Log(ex, "Error initializing runtime debug request.");
            }
        }

        private void ContinueProject()
        {
            if (_lastBreakPointRequestArgs != null)
            {
                View.ResetBreakPointRequest();
                IsBreakPoint = false;
                _lastBreakPointRequestArgs.Release();
                _lastBreakPointRequestArgs = null;
            }
        }

        private async void DisplayDebugNodeValue(DebugNode debugNode)
        {
            if (debugNode != null)
            {
                await NotificationProvider.ShowDialog(new BreakPointValueDialogViewVM(debugNode.Name, debugNode.Value));
            }
        }

        #endregion

        #region Help

        private void OpenHelpFile()
        {
            String word = View.GetCaretWord();

            if (word != null)
            {
                String memberSign = null;
                String typeFullName = null;

                if (typeof(IProcedureContext).GetProperties(BindingFlags.Instance | BindingFlags.Public).Any(x => x.Name == word))
                {
                    memberSign = "P";
                }

                if (memberSign == null)
                {
                    if (typeof(IProcedureContext).GetMethods(BindingFlags.Instance | BindingFlags.Public).Any(x => x.Name == word))
                    {
                        memberSign = "M";
                    }
                }

                if (memberSign == null)
                {
                    var type = typeof(IProcedureContext).Assembly.GetTypes().FirstOrDefault(x => x.Name == word);
                    if (type != null)
                    {
                        memberSign = "T";
                        typeFullName = type.FullName.Replace(".", "_");
                    }
                }

                if (memberSign != null)
                {
                    String url = String.Empty;

                    if (memberSign == "T")
                    {
                        url = $"{typeFullName}.htm";
                    }
                    else
                    {
                        url = $"Tango_FSE_Procedures_IProcedureContext_{word}.htm";
                    }

                    Process pp = new Process();
                    pp.StartInfo.FileName = "hh.exe";
                    pp.StartInfo.Arguments = $"ms-its:{Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "proc-doc.chm")}::/html/{memberSign}_{url}";
                    pp.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
                    pp.Start();
                    return;
                }
            }

            Process p = new Process();
            p.StartInfo.FileName = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "proc-doc.chm");
            p.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
            p.Start();
        }

        #endregion

        #region File Association

        private void HandlerProcedureFileAssociation(FileAssociationPackage package)
        {
            if (!CurrentUser.HasPermission(Permissions.FSE_RunProcedureDesigner))
            {
                NotificationProvider.ShowError("Current user profile does not allow running the procedure designer.");
                return;
            }

            if (File.Exists(package.File))
            {
                try
                {
                    LogManager.Log("Opening procedure project from file association...");
                    NavigationManager.NavigateTo<ProceduresModule>(true, nameof(ProcedureDesignerView));
                    OpenProject(package.File);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error occurred while trying to handle the procedure file association.");
                }
            }
        }

        #endregion

        #region File Drop

        private void View_FileDropped(object sender, string file)
        {
            if (File.Exists(file))
            {
                if (Path.GetExtension(file).ToLower() == ".pproj")
                {
                    OpenProject(file);
                }
            }
        }

        #endregion
    }
}