using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using static VisualScript.Form1;

namespace VisualScript
{
    public partial class Form1 : Form
    {

        #region Member

        private static Panel canvasPanel;
        private static PropertyGrid propertyGrid;
        private List<Node> nodes;
        private static List<Connector> connectors;
        private Node selectedNode;
        private Port selectedPort;
        private Point draggingStartPoint;

        private Connector selectedConnector;

        //private static SplitContainer splitContainer;
        private static MenuStrip menuStrip;

        #endregion


        #region Initializer

        public Form1()
        {

            InitializeComponent();
            InitializeComponents();
        }

        private void InitializeComponents()
        {

            // Menüleiste erstellen
            menuStrip = new MenuStrip();

            ToolStripMenuItem fileMenu = new ToolStripMenuItem("Node");

            ToolStripMenuItem menuItemNewVariable = new ToolStripMenuItem("Variable");
            menuItemNewVariable.Click += MenuItemNewVariable_Click;

            ToolStripMenuItem menuItemNewAddNode = new ToolStripMenuItem("AddNode");
            menuItemNewAddNode.Click += MenuItemNewAddNode_Click;

            ToolStripMenuItem menuItemNewSubNode = new ToolStripMenuItem("SubNode");
            menuItemNewSubNode.Click += MenuItemNewSubNode_Click;

            ToolStripMenuItem menuItemNewMulNode = new ToolStripMenuItem("MulNode");
            menuItemNewMulNode.Click += MenuItemNewMulNode_Click;

            ToolStripMenuItem menuItemNewDivNode = new ToolStripMenuItem("DivNode");
            menuItemNewDivNode.Click += MenuItemNewDivNode_Click;

            // Menüpunkte hinzufügen
            fileMenu.DropDownItems.Add(menuItemNewVariable);
            fileMenu.DropDownItems.Add(menuItemNewAddNode);
            fileMenu.DropDownItems.Add(menuItemNewSubNode);
            fileMenu.DropDownItems.Add(menuItemNewMulNode);
            fileMenu.DropDownItems.Add(menuItemNewDivNode);

            // Menü zur Menüleiste hinzufügen
            menuStrip.Items.Add(fileMenu);
            this.Controls.Add(menuStrip);

            // Canvas Panel
            canvasPanel = new Panel();
            canvasPanel.Dock = DockStyle.Fill;
            canvasPanel.Paint += canvasPanel_Paint;
            canvasPanel.MouseClick += canvasPanel_MouseClick; // Wenn Maus angeklickt und losgelassen wurde
            canvasPanel.MouseDown += canvasPanel_MouseDown; // Wenn Maus angeklickt wurde
            canvasPanel.MouseMove += canvasPanel_MouseMove;
            canvasPanel.MouseUp += canvasPanel_MouseUp;
            //this.Controls.Add(canvasPanel);

            // Property Grid
            propertyGrid = new PropertyGrid();
            propertyGrid.Dock = DockStyle.Fill;
            //this.Controls.Add(propertyGrid);


            splitContainer1.Panel1.Controls.Add(canvasPanel);
            splitContainer1.Panel2.Controls.Add(propertyGrid);
            //this.Controls.Add(splitContainer);

            // Liste für Nodes und Connectors initialisieren
            nodes = new List<Node>();
            connectors = new List<Connector>();

        }

        #endregion


        #region MenuItems Click Events

        private void MenuItemNewVariable_Click(object sender, EventArgs e)
        {
            nodes.Add(new VariableNode());
            canvasPanel.Invalidate();
        }
        private void MenuItemNewAddNode_Click(object sender, EventArgs e)
        {
            nodes.Add(new AddNode());
            canvasPanel.Invalidate();
        }
        private void MenuItemNewSubNode_Click(object sender, EventArgs e)
        {
            nodes.Add(new SubNode());
            canvasPanel.Invalidate();
        }
        private void MenuItemNewMulNode_Click(object sender, EventArgs e)
        {
            nodes.Add(new MulNode());
            canvasPanel.Invalidate();
        }
        private void MenuItemNewDivNode_Click(object sender, EventArgs e)
        {
            nodes.Add(new DivNode());
            canvasPanel.Invalidate();
        }

        #endregion


        #region Form Event Handler

        private void canvasPanel_Paint(object sender, PaintEventArgs e)
        {
            // Draw Connectors
            foreach (var connector in connectors)
            {
                e.Graphics.DrawLine(Pens.Black, connector.StartPort.Location, connector.EndPort.Location);
            }

            // Draw nodes
            foreach (var node in nodes)
                node.Paint(sender, e);
        }

        private void canvasPanel_MouseClick(object sender, MouseEventArgs e)
        {

            if(e.Button == MouseButtons.Left)
            {

                // Check if a Node is selected
                foreach (var node in nodes)
                {
                    if (node.Bounds.Contains(e.Location))
                    {
                        selectedNode = node;
                        selectedPort = null;
                        propertyGrid.SelectedObject = selectedNode;
                        canvasPanel.Invalidate();
                        return;
                    }
                }

                // Check if a Port is selected
                foreach (var node in nodes)
                {
                    var clickedPort = node.InputPorts.Concat<Port>(node.OutputPorts).FirstOrDefault(port => port.Bounds.Contains(e.Location));
                    if (clickedPort != null)
                    {
                        if (selectedPort == null)
                        {
                            // If no port was selected, select the clicked port
                            selectedPort = clickedPort;
                            propertyGrid.SelectedObject = selectedPort;
                        }
                        else
                        {
                            // If a port was already selected, start the connection
                            ConnectPorts(selectedPort, clickedPort);
                            selectedPort = null;
                            canvasPanel.Invalidate();
                        }
                        return;
                    }
                }

                // Überprüfen, ob auf einen Connector geklickt wurde
                foreach (var connector in connectors)
                {
                    //var connectorBounds = GetConnectorBounds(connector);
                    //if (connectorBounds.Contains(e.Location))
                    if (IsPointOnConnector(e.Location, connector))
                    {
                        // Connector auswählen
                        selectedConnector = connector;
                        selectedNode = null;
                        selectedPort = null;

                        // Aktualisieren Sie das PropertyGrid für den ausgewählten Connector
                        propertyGrid.SelectedObject = selectedConnector;
                        //selectedConnector = null;
                        canvasPanel.Invalidate();
                        return;
                    }
                }


            }
            else if (e.Button == MouseButtons.Right)
            {

                // Überprüfen, ob auf einen Connector geklickt wurde
                foreach (var connector in connectors)
                {
                    if (IsPointOnConnector(e.Location, connector))
                    {
                        // Connector löschen
                        connectors.Remove(connector);
                        connector.StartPort.Connected = false;
                        connector.EndPort.Connected = false;
                        selectedNode = null;
                        UpdateConnectors();
                        propertyGrid.Refresh();
                        canvasPanel.Invalidate();
                        return;
                    }
                }

                List<Node> nodesToRemove = new List<Node>();
                foreach (Node n in nodes)
                {
                    //if (IsPointOnNode(e.Location, n))
                    if (n.Bounds.Contains(e.Location))
                    {
                        nodesToRemove.Add(n);
                        break;
                    }
                }

                foreach (Node n in nodesToRemove)
                {
                    nodes.Remove(n);
                }

                canvasPanel.Invalidate();
                return;

            }

            if (e.Button == MouseButtons.Left && Control.ModifierKeys == Keys.Control)
            {

                var newNode = new VariableNode { X = e.X, Y = e.Y };
                nodes.Add(newNode);
                //selectedNode = newNode;
                selectedPort = null;
                propertyGrid.SelectedObject = selectedNode;
                canvasPanel.Invalidate();
                return;

            }

            //UpdateConnectors();
            // ??
            selectedNode = null;
            //selectedPort = null;
            propertyGrid.SelectedObject = null;

        }

        private void canvasPanel_MouseDown(object sender, MouseEventArgs e)
        {

            if (e.Button == MouseButtons.Left)
            {

                bool test = false;
                foreach (Node node in nodes)
                {
                    if (node.Bounds.Contains(e.Location))
                    {
                        test = true;
                    }
                }
                if (!test)
                {
                    selectedNode = null;
                    //return;
                }

                // Prüfen, ob ein existierender Node ausgewählt wurde
                if (selectedNode != null && selectedNode.Bounds.Contains(e.Location))
                {
                    draggingStartPoint = e.Location;
                    return;
                }

                // Wenn ein Port ausgewählt wurde, starten Sie die Verbindung
                if (selectedPort != null && selectedNode != null)
                {
                    var clickedPort = selectedNode.InputPorts.Concat<Port>(selectedNode.OutputPorts).FirstOrDefault(port => port.Bounds.Contains(e.Location));
                    if (clickedPort != null && clickedPort != selectedPort)
                    {
                        // Hier wird der Verbindungscode aufgerufen, wenn ein Port ausgewählt ist
                        ConnectPorts(selectedPort, clickedPort);
                        selectedPort = null;
                        canvasPanel.Invalidate();
                        return;
                    }
                }

                // Hier können Sie den Code für das Verschieben eines Nodes implementieren
                foreach (var node in nodes)
                {
                    if (node.Bounds.Contains(e.Location))
                    {
                        selectedNode = node;
                        selectedPort = null;
                        propertyGrid.SelectedObject = selectedNode;
                        draggingStartPoint = e.Location;
                        return;
                    }
                }
            }
        }

        private void canvasPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (selectedNode != null && e.Button == MouseButtons.Left)
            {
                // Bewegen Sie den ausgewählten Node während des Drag-and-Drop
                selectedNode.X += e.X - draggingStartPoint.X;
                selectedNode.Y += e.Y - draggingStartPoint.Y;
                draggingStartPoint = e.Location;
                canvasPanel.Invalidate();
            }
        }

        private void canvasPanel_MouseUp(object sender, MouseEventArgs e)
        {
            if (selectedNode != null && e.Button == MouseButtons.Left)
            {
                // Wenn ein existierender Node verschoben wurde, aktualisieren Sie die Verbindungen
                UpdateConnectors();
            }
        }

        #endregion


        private void ConnectPorts(Port startPort, Port endPort)
        {
            connectors.Add(new Connector { StartPort = startPort, EndPort = endPort });
            UpdateConnectors();
            canvasPanel.Invalidate();
        }

        private void UpdateConnectors()
        {
            foreach (var node in nodes)
            {
                foreach (var outputPort in node.OutputPorts)
                {
                    foreach (var inputPort in node.InputPorts)
                    {
                        if (outputPort.Connected && inputPort.Connected)
                        {
                            connectors.Add(new Connector { StartPort = outputPort, EndPort = inputPort });
                        }
                    }
                }
            }

            foreach(Node n in nodes)
            {
                if(n is AddNode || n is SubNode || n is MulNode || n is DivNode)
                    n.UpdateValue();
            }

            canvasPanel.Invalidate();
        }


        #region Nodes

        public abstract class Node
        {

            [Browsable(false)] 
            public int X { get; set; }

            [Browsable(false)] 
            public int Y { get; set; }

            [Browsable(true)] 
            public virtual string Name { get; set; }

            [Browsable(true)]
            public virtual string Type { get; set; }

            [Browsable(true)]
            public virtual string Value { get; set; } = "";

            [Browsable(false)] 
            public abstract int Width { get; }

            [Browsable(false)] 
            public abstract int Height { get; }

            [Browsable(false)]
            public Rectangle Bounds => new Rectangle(X, Y, Width, Height);

            [Browsable(false)] 
            public List<InputPort> InputPorts { get; } = new List<InputPort>();

            [Browsable(false)] 
            public List<OutputPort> OutputPorts { get; } = new List<OutputPort>();

            public abstract void Paint(object sender, PaintEventArgs e);

            public abstract void UpdateValue();

        }

        public class VariableNode : Node
        {

            public override int Width => 100;
            public override int Height => 30;

            public override string Name { get; set; } = "Variable";

            public override string Type { get; set; } = "";
            public override string Value { get; set; } = "";

            public VariableNode()
            {
                OutputPorts.Add(new OutputPort(this, "Output 1"));
            }

            public override void UpdateValue()
            {

            }

            public override void Paint(object sender, PaintEventArgs e)
            {

                e.Graphics.DrawRectangle(Pens.Black, Bounds);
                e.Graphics.DrawString(Name, DefaultFont, Brushes.Black, Bounds.Location);

                
                Rectangle newLocation = Bounds;
                newLocation.Y += 15;
                e.Graphics.DrawString(Type.ToString() + ": " + Value, DefaultFont, Brushes.Black, newLocation);
                
                // Draw Input Ports
                foreach (var inputPort in InputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Blue, inputPort.Bounds);
                }

                // Draw Output Ports
                foreach (var outputPort in OutputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Red, outputPort.Bounds);
                }
            }

        }

        public class AddNode : Node
        {

            public override int Width => 100;
            public override int Height => 30;

            public override string Name { get; set; } = "Addition";

            public override string Type { get; set; }
            public override string Value { get; set; }

            public override void UpdateValue()
            {

                int i = 0;
                int counter = 0;

                foreach(Connector c in connectors)
                {

                    // Nicht wenn es ein StartPort ist (Input)
                    /*
                    if(c.StartPort.OwnerNode == this)
                    {
                        if (!string.IsNullOrEmpty(c.EndPort.OwnerNode.Value))
                        if(counter == 0)
                            i = int.Parse(c.EndPort.OwnerNode.Value);
                        else
                            i += int.Parse(c.EndPort.OwnerNode.Value);
                    }
                    */
                        
                    if (c.EndPort.OwnerNode == this)
                    {
                        if(!string.IsNullOrEmpty(c.StartPort.OwnerNode.Value))
                        if(counter == 0)
                            i = int.Parse(c.StartPort.OwnerNode.Value);
                        else
                            i += int.Parse(c.StartPort.OwnerNode.Value);
                    }

                    counter++;
 
                }

                Value = i.ToString();

            }

            public AddNode()
            {
                InputPorts.Add(new InputPort(this, "Input 1"));
                OutputPorts.Add(new OutputPort(this, "Output 1"));
            }


            public override void Paint(object sender, PaintEventArgs e)
            {

                e.Graphics.DrawRectangle(Pens.Black, Bounds);
                e.Graphics.DrawString(Name, DefaultFont, Brushes.Black, Bounds.Location);

                Rectangle newLocation = Bounds;
                newLocation.Y += 15;
                e.Graphics.DrawString("Ergebnis: " + Value, DefaultFont, Brushes.Black, newLocation);

                // Draw Output Ports
                foreach (var inputPort in InputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Red, inputPort.Bounds);
                }

                // Draw Output Ports
                foreach (var outputPort in OutputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Red, outputPort.Bounds);
                }
            }

        }

        public class SubNode : Node
        {

            public override int Width => 100;
            public override int Height => 30;

            public override string Name { get; set; } = "Subtraction";

            public override string Type { get; set; }
            public override string Value { get; set; }

            public override void UpdateValue()
            {

                int i = 0;
                int counter = 0;

                foreach (Connector c in connectors)
                {
                    
                    if (c.EndPort.OwnerNode == this)
                    {
                        if (!string.IsNullOrEmpty(c.StartPort.OwnerNode.Value))
                            if (counter == 0)
                                i = int.Parse(c.StartPort.OwnerNode.Value);
                            else
                                i = i - int.Parse(c.StartPort.OwnerNode.Value);

                        counter++;
                    }
                    
                }

                Value = i.ToString();

            }

            public SubNode()
            {
                InputPorts.Add(new InputPort(this, "Input 1"));
                OutputPorts.Add(new OutputPort(this, "Output 1"));
            }

            public override void Paint(object sender, PaintEventArgs e)
            {

                e.Graphics.DrawRectangle(Pens.Black, Bounds);
                e.Graphics.DrawString(Name, DefaultFont, Brushes.Black, Bounds.Location);

                Rectangle newLocation = Bounds;
                newLocation.Y += 15;
                e.Graphics.DrawString("Ergebnis: " + Value, DefaultFont, Brushes.Black, newLocation);

                // Draw Output Ports
                foreach (var inputPort in InputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Red, inputPort.Bounds);
                }

                // Draw Output Ports
                foreach (var outputPort in OutputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Red, outputPort.Bounds);
                }
            }

        }

        public class MulNode : Node
        {

            public override int Width => 100;
            public override int Height => 30;

            public override string Name { get; set; } = "Multiplication";

            public override string Type { get; set; }
            public override string Value { get; set; }

            public override void UpdateValue()
            {

                int i = 0;
                int counter = 0;

                foreach (Connector c in connectors)
                {

                    if (c.EndPort.OwnerNode == this)
                    {
                        if (!string.IsNullOrEmpty(c.StartPort.OwnerNode.Value))
                            if (counter == 0)
                                i = int.Parse(c.StartPort.OwnerNode.Value);
                            else
                                i *= int.Parse(c.StartPort.OwnerNode.Value);

                        counter++;
                    }
     
                }

                Value = i.ToString();

            }

            public MulNode() 
            {
                InputPorts.Add(new InputPort(this, "Input 1"));
                OutputPorts.Add(new OutputPort(this, "Output 1"));
            }

            public override void Paint(object sender, PaintEventArgs e)
            {

                e.Graphics.DrawRectangle(Pens.Black, Bounds);
                e.Graphics.DrawString(Name, DefaultFont, Brushes.Black, Bounds.Location);

                Rectangle newLocation = Bounds;
                newLocation.Y += 15;
                e.Graphics.DrawString("Ergebnis: " + Value, DefaultFont, Brushes.Black, newLocation);

                // Draw Output Ports
                foreach (var inputPort in InputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Red, inputPort.Bounds);
                }

                // Draw Output Ports
                foreach (var outputPort in OutputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Red, outputPort.Bounds);
                }
            }

        }

        public class DivNode : Node
        {

            public override int Width => 100;
            public override int Height => 30;

            public override string Name { get; set; } = "Division";

            public override string Type { get; set; }
            public override string Value { get; set; }

            public override void UpdateValue()
            {

                int i = 0;
                int counter = 0;

                foreach (Connector c in connectors)
                {

                    if (c.EndPort.OwnerNode == this)
                    {
                        if (!string.IsNullOrEmpty(c.StartPort.OwnerNode.Value))
                            if (counter == 0)
                                i = int.Parse(c.StartPort.OwnerNode.Value);
                            else
                                i = i / int.Parse(c.StartPort.OwnerNode.Value);

                        counter++;
                    }

                }

                Value = i.ToString();

            }

            public DivNode()
            {
                InputPorts.Add(new InputPort(this, "Input 1"));
                OutputPorts.Add(new OutputPort(this, "Output 1"));
            }

            public override void Paint(object sender, PaintEventArgs e)
            {

                e.Graphics.DrawRectangle(Pens.Black, Bounds);
                e.Graphics.DrawString(Name, DefaultFont, Brushes.Black, Bounds.Location);

                Rectangle newLocation = Bounds;
                newLocation.Y += 15;
                e.Graphics.DrawString("Ergebnis: " + Value, DefaultFont, Brushes.Black, newLocation);

                // Draw Output Ports
                foreach (var inputPort in InputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Red, inputPort.Bounds);
                }

                // Draw Output Ports
                foreach (var outputPort in OutputPorts)
                {
                    e.Graphics.FillEllipse(Brushes.Red, outputPort.Bounds);
                }
            }

        }

        #endregion


        #region Ports

        public abstract class Port
        {
            public Node OwnerNode { get; }
            public string Name { get; set; }
            public bool Connected { get; set; }
            protected Port(Node ownerNode, string name)
            {
                OwnerNode = ownerNode;
                Name = name;
            }
            public abstract Point Location { get; }
            public Rectangle Bounds => new Rectangle(Location, new Size(10, 10));
            [Browsable(false)]
            public int portSpacing = 10;
        }

        public class InputPort : Port
        {
            public InputPort(Node ownerNode, string name) : base(ownerNode, name)
            {
            }

            public override Point Location => new Point(
               OwnerNode.X - 10,
               OwnerNode.Y + (OwnerNode.InputPorts.IndexOf(this) ) * portSpacing
           );
        }

        public class OutputPort : Port
        {
            public OutputPort(Node ownerNode, string name) : base(ownerNode, name)
            {
            }
            public override Point Location => new Point(
                OwnerNode.X + OwnerNode.Width,
                OwnerNode.Y + (OwnerNode.OutputPorts.IndexOf(this) + 1) * portSpacing
            );
        }

        #endregion


        public class Connector
        {
            [Browsable(false)]
            public Port StartPort { get; set; }
            [Browsable(false)]
            public Port EndPort { get; set; }

            [Browsable(true)]
            [Category("Connector")]
            public string Test
            {
                get { return StartPort.OwnerNode.Type + "::" + StartPort.OwnerNode.Value +  " -> " + EndPort.OwnerNode.Type + "::" + EndPort.OwnerNode.Value; }
            }
        }


        #region Helper Functions

        private Rectangle GetConnectorBounds(Connector connector)
        {
            var startPoint = connector.StartPort.Location;
            var endPoint = connector.EndPort.Location;

            int x = Math.Min(startPoint.X, endPoint.X);
            int y = Math.Min(startPoint.Y, endPoint.Y);
            int width = Math.Abs(startPoint.X - endPoint.X);
            int height = Math.Abs(startPoint.Y - endPoint.Y);

            return new Rectangle(x, y, width, height);
        }

        private bool IsPointOnConnector(Point clickPoint, Connector connector)
        {
            var startPoint = connector.StartPort.Location;
            var endPoint = connector.EndPort.Location;

            // Überprüfen, ob der Klickpunkt in der Nähe der Linie liegt
            float distance = DistancePointToLine(clickPoint, startPoint, endPoint);
            return distance < 5; // Ändern Sie den Schwellenwert nach Bedarf
        }

        private float DistancePointToLine(Point point, Point lineStart, Point lineEnd)
        {
            float a = point.X - lineStart.X;
            float b = point.Y - lineStart.Y;
            float c = lineEnd.X - lineStart.X;
            float d = lineEnd.Y - lineStart.Y;

            float dot = a * c + b * d;
            float len_sq = c * c + d * d;
            float param = dot / len_sq;

            float xx, yy;

            if (param < 0)
            {
                xx = lineStart.X;
                yy = lineStart.Y;
            }
            else if (param > 1)
            {
                xx = lineEnd.X;
                yy = lineEnd.Y;
            }
            else
            {
                xx = lineStart.X + param * c;
                yy = lineStart.Y + param * d;
            }

            float dx = point.X - xx;
            float dy = point.Y - yy;
            return (float)Math.Sqrt(dx * dx + dy * dy);
        }

        #endregion


    }
}