private static void AddEdge(NeuronViewModelBase selectedNeuron, string id, string source, string target, MsGraph graph, string strength = "1", bool inhibitoryEndpoint = false)
        {
            if (graph.Edges.SingleOrDefault(e => e.Attr.Id == id) == null)
            {
                Edge e = null;
                if (strength != "1")
                {
                    e = graph.AddEdge(source, strength, target);
                    e.Label.FontSize = e.Label.FontSize * 0.8;
                    e.Attr.AddStyle(Microsoft.Msagl.Drawing.Style.Dashed);
                }
                else
                {
                    e = graph.AddEdge(source, target);
                }

                e.Attr.Id = id;
                if (selectedNeuron.TerminalId == id)
                {
                    e.Attr.Color = NeuronGraphView.GetHighlightColor();
                }
                else if (inhibitoryEndpoint)
                {
                    e.Attr.Color = Color.IndianRed;
                }
            }
        }
        private static void AddNeuronAndChildren(NeuronViewModelBase root, NeuronViewModelBase selectedNeuron, NeuronViewModelBase value, MsGraph graph)
        {
            NeuronGraphView.AddSingleNeuron(root, selectedNeuron, value, graph);

            if (value.Parent.HasValue)
            {
                if (graph.FindNode(value.Parent.Value.NeuronId) == null)
                {
                    NeuronGraphView.AddSingleNeuron(root, selectedNeuron, value.Parent.Value, graph);
                }

                switch (value.Neuron.Type)
                {
                case RelativeType.Postsynaptic:
                    NeuronGraphView.AddEdge(selectedNeuron, value.TerminalId, value.Parent.Value.NeuronId, value.NeuronId, graph, value.Neuron.Terminal.Strength, value.Neuron.Terminal.Effect == "-1");
                    break;

                case RelativeType.Presynaptic:
                    NeuronGraphView.AddEdge(selectedNeuron, value.TerminalId, value.NeuronId, value.Parent.Value.NeuronId, graph, value.Neuron.Terminal.Strength, value.Neuron.Terminal.Effect == "-1");
                    break;

                case RelativeType.NotSet:
                    NeuronGraphView.AddEdge(selectedNeuron, value.NeuronId, value.NeuronId, value.Parent.Value.NeuronId, graph);
                    break;
                }
            }

            if (value.Children != null)
            {
                foreach (var c in value.Children)
                {
                    NeuronGraphView.AddNeuronAndChildren(root, selectedNeuron, c, graph);
                }
            }
        }
        private static void AddSingleNeuron(NeuronViewModelBase root, NeuronViewModelBase selectedNeuron, NeuronViewModelBase value, MsGraph graph)
        {
            var n = graph.AddNode(value.NeuronId);

            n.LabelText = value.Tag;
            if (selectedNeuron == value)
            {
                var mtc = SystemColors.HighlightTextColor;
                n.Attr.FillColor  = NeuronGraphView.GetHighlightColor();
                n.Label.FontColor = new Color(mtc.A, mtc.R, mtc.G, mtc.B);
            }
            else if (root == value)
            {
                n.Attr.Color      = NeuronGraphView.GetHighlightColor();
                n.Attr.LineWidth *= 1.5;
            }
        }
        public NeuronGraphView()
        {
            InitializeComponent();

            this.graphViewer = new GraphViewer();
            this.graphViewer.BindToPanel(this.ContentPanel);

            this.WhenActivated(d =>
            {
                this.WhenAnyValue(x => x.DataContext)
                .Where(x => x != null)
                .Subscribe(x => this.ViewModel = (NeuronGraphViewModel)x);

                d(this.OneWayBind(this.ViewModel, vm => vm.LayoutOptions, v => v.Layout.ItemsSource, vmp => vmp.Select(s => new MenuItem()
                {
                    Header = s, IsCheckable = true, Style = Resources["MenuItemStyle1"] as System.Windows.Style
                })));

                Observable.FromEventPattern <RoutedEventHandler, RoutedEventArgs>(
                    ev => this.Layout.Click += ev,
                    ev => this.Layout.Click -= ev
                    ).Subscribe(ep =>
                {
                    this.ViewModel.Layout = this.Layout.Items.IndexOf(ep.EventArgs.OriginalSource);
                    this.Layout.Items.Cast <MenuItem>().ToList().ForEach(mi => mi.IsChecked = false);
                    ((MenuItem)ep.EventArgs.OriginalSource).IsChecked = true;
                });

                Observable.FromEventPattern <EventHandler <MsaglMouseEventArgs>, MsaglMouseEventArgs>(
                    ev => this.graphViewer.MouseDown += ev,
                    ev => this.graphViewer.MouseDown -= ev
                    ).Subscribe(ep =>
                {
                    var gv = ep.Sender as GraphViewer;
                    if (
                        gv != null &&
                        gv.ObjectUnderMouseCursor != null &&
                        (
                            gv.ObjectUnderMouseCursor.DrawingObject is Node ||
                            gv.ObjectUnderMouseCursor.DrawingObject is Edge
                        )
                        )
                    {
                        Node node = null;
                        if (gv.ObjectUnderMouseCursor.DrawingObject is Node)
                        {
                            node = (Node)gv.ObjectUnderMouseCursor.DrawingObject;
                            this.ViewModel.InternallySelectNeuronAndTerminal(node.Id, null);
                        }
                        else
                        {
                            var terminalId = ((Edge)gv.ObjectUnderMouseCursor.DrawingObject).Attr.Id;
                            var neuronId   = this.ViewModel.GetNeuronId(terminalId);
                            this.ViewModel.InternallySelectNeuronAndTerminal(neuronId, terminalId);
                            node = gv.Graph.FindNode(neuronId);
                        }

                        // reset highlight for all unselected neuron(s) in graph
                        gv.Graph.Nodes.ToList().ForEach(n => NeuronGraphView.FillIfNotExternallySelected(
                                                            n,
                                                            this.ViewModel.ExternallySelectedNeuron.NeuronId,
                                                            NeuronGraphView.ConvertColorToMsaglColor(SystemColors.WindowColor),
                                                            1
                                                            ));

                        // highlight selected neuron in graph
                        NeuronGraphView.FillIfNotExternallySelected(
                            node,
                            this.ViewModel.ExternallySelectedNeuron.NeuronId,
                            NeuronGraphView.ConvertColorToMsaglColor(Color.Yellow, 80),
                            1
                            );

                        // add/remove highlight for selected/unselected presynaptic/postsynaptic
                        var postsynaptic = NeuronGraphView.ConvertColorToMsaglColor(Color.LightGreen, 90);
                        var presynaptic  = NeuronGraphView.ConvertColorToMsaglColor(Color.PowderBlue, 150);
                        node.Edges.ToList().ForEach(e =>
                        {
                            if (e.SourceNode == node)
                            {
                                NeuronGraphView.FillIfNotExternallySelected(
                                    e.TargetNode,
                                    this.ViewModel.ExternallySelectedNeuron.NeuronId,
                                    postsynaptic,
                                    2
                                    );
                            }
                            else
                            {
                                NeuronGraphView.FillIfNotExternallySelected(
                                    e.SourceNode,
                                    this.ViewModel.ExternallySelectedNeuron.NeuronId,
                                    presynaptic,
                                    2
                                    );
                            }
                        });
                    }

                    this.ViewModel.SelectCommand.Execute().Subscribe();
                });

                Observable.FromEventPattern <PropertyChangedEventHandler, PropertyChangedEventArgs>(
                    ev => this.ViewModel.PropertyChanged += ev,
                    ev => this.ViewModel.PropertyChanged -= ev
                    ).Subscribe(ep =>
                {
                    if (ep.EventArgs.PropertyName == nameof(NeuronGraphViewModel.ExternallySelectedNeuron))
                    {
                        this.graphViewer.Graph    = null;
                        MsGraph graph             = new MsGraph();
                        graph.Attr.LayerDirection = (LayerDirection)this.ViewModel.Layout;

                        NeuronViewModelBase root = this.ViewModel.ExternallySelectedNeuron;

                        while (root.Parent.HasValue)
                        {
                            root = root.Parent.Value;
                        }

                        NeuronGraphView.AddNeuronAndChildren(root, this.ViewModel.ExternallySelectedNeuron, root, graph);
                        this.graphViewer.Graph = graph;
                    }
                });
            });
        }