using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WpfApp1.Structure;
using WpfApp1.Tools;
using static System.Formats.Asn1.AsnWriter;
using System.Xml.Linq;
using Emgu.CV.Structure;
using Emgu.CV;
using Emgu.CV.Util;
using Emgu.CV.CvEnum;
using System.Drawing;
using Microsoft.Win32;
using System.IO;
using System.Text.Json;
using System.Windows.Media.Media3D;
using System.Diagnostics;
using System.Security.Cryptography.Xml;
using System.Windows.Controls.Primitives;
using static Emgu.Util.Platform;
using static System.Net.Mime.MediaTypeNames;
using System.Xml.Serialization;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private void SymbolMoved(Symbol symbol, double movedDistanceX, double movedDistanceY)
        {
            symbol.Moved();
            if (symbol is Node)
            {
                //Node node = (Node)symbol;
                //node.center[0] += movedDistanceX;
                //node.center[1] += movedDistanceY;
                MoveEdgesByNode(symbol as Node, movedDistanceX, movedDistanceY);
                RelayoutOverlappingNode(symbol as Node);
            }
            else
            {

            }
        }

        private void SymbolResized(Symbol symbol, Rect oldBounds, Rect bounds)
        {
            double oldCenterX = symbol.center[0];
            double oldCenterY = symbol.center[1];
            symbol.Resized();
            if (symbol is Node)
            {
                (symbol as Node).area = bounds.Width * bounds.Height;
                foreach (Edge edge in (symbol as Node).edges_in)
                {
                    double movedDistanceX = (edge.strokes[0].StylusPoints.Last().X - oldBounds.Left) / oldBounds.Width * bounds.Width + bounds.Left - edge.strokes[0].StylusPoints.Last().X;
                    double movedDistanceY = (edge.strokes[0].StylusPoints.Last().Y - oldBounds.Top) / oldBounds.Height * bounds.Height + bounds.Top - edge.strokes[0].StylusPoints.Last().Y;
                    MoveEdgeTail(edge, movedDistanceX, movedDistanceY);
                }
                foreach (Edge edge in (symbol as Node).edges_out)
                {
                    double movedDistanceX = (edge.strokes[0].StylusPoints[0].X - oldBounds.Left) / oldBounds.Width * bounds.Width + bounds.Left - edge.strokes[0].StylusPoints[0].X;
                    double movedDistanceY = (edge.strokes[0].StylusPoints[0].Y - oldBounds.Top) / oldBounds.Height * bounds.Height + bounds.Top - edge.strokes[0].StylusPoints[0].Y;
                    MoveEdgeHead(edge, movedDistanceX, movedDistanceY);
                }
            }
        }

        //基础操作
        private void MoveEdgeHead(Edge edge, double movedDistanceX, double movedDistanceY)
        {
            GetShadowStrokes(edge);
            Stroke stroke = edge.strokes[0];
            double startX = stroke.StylusPoints[0].X;
            double startY = stroke.StylusPoints[0].Y;
            double endX = stroke.StylusPoints.Last().X;
            double endY = stroke.StylusPoints.Last().Y;
            double targetX = startX + movedDistanceX;
            double targetY = startY + movedDistanceY;
            if (stroke.StylusPoints.Count == 2)
            {
                stroke.StylusPoints[0] = new StylusPoint(targetX, targetY);
            }
            else
            {
                // 计算缩放比例
                double scaleX = startX == endX ? 1 : (targetX - endX) / (startX - endX);
                double scaleY = startY == endY ? 1 : (targetY - endY) / (startY - endY);
                // 创建缩放和平移变换
                Matrix matrix = new Matrix();
                matrix.ScaleAt(scaleX, scaleY, endX, endY);
                stroke.Transform(matrix, false);
            }
            edge.ResetArrow();
            SymbolMoved(edge, 0, 0);
            PushHistory(new History("MoveEdgeHead", new Dictionary<string, object> { { "edge", edge }, { "movedDistanceX", movedDistanceX }, { "movedDistanceY", movedDistanceY } }));
        }

        private void MoveEdgeTail(Edge edge, double movedDistanceX, double movedDistanceY)
        {
            GetShadowStrokes(edge);
            Stroke stroke = edge.strokes[0];
            double startX = stroke.StylusPoints[0].X;
            double startY = stroke.StylusPoints[0].Y;
            double endX = stroke.StylusPoints.Last().X;
            double endY = stroke.StylusPoints.Last().Y;
            double targetX = endX + movedDistanceX;
            double targetY = endY + movedDistanceY;
            if (stroke.StylusPoints.Count == 2)
            {
                stroke.StylusPoints[1] = new StylusPoint(targetX, targetY);
            }
            else
            {
                // 计算缩放比例
                double scaleX = endX == startX ? 1 : (targetX - startX) / (endX - startX);
                double scaleY = endY == startY ? 1 : (targetY - startY) / (endY - startY);
                // 创建缩放和平移变换
                Matrix matrix = new Matrix();
                matrix.ScaleAt(scaleX, scaleY, startX, startY);
                stroke.Transform(matrix, false);
            }
            edge.ResetArrow();
            SymbolMoved(edge, 0, 0);
            PushHistory(new History("MoveEdgeTail", new Dictionary<string, object> { { "edge", edge }, { "movedDistanceX", movedDistanceX }, { "movedDistanceY", movedDistanceY } }));
        }

        private Node AddNode(string text, string category, string color, StrokeCollection strokes)
        {
            GetShadowStrokes(null);
            Node node = new Node(++Symbol_id, video_text);
            if (color == "")
            {
                //使用默认颜色
                color = SymbolColor.d_CategoryColor[category];
            }
            node.category = category;
            Rect boundingBox = strokes.GetBounds();
            node.center[0] = boundingBox.Left + boundingBox.Width / 2;
            node.center[1] = boundingBox.Top + boundingBox.Height / 2;
            node.SetStrokes(strokes);
            node.SetText(text);
            node.SetColor(color);

            d_idSymbol.Add(Symbol_id, node);
            foreach (Stroke stroke in node.strokes)
            {
                d_idStroke.Add(++Stroke_id, stroke);
                d_StrokeSymbol.Add(stroke, node);
                video_sketch.Strokes.Add(stroke);
            }

            currentSymbols.Add(node);
            PushHistory(new History("AddNode_s", new Dictionary<string, object> { { "node", node } }));
            RelayoutOverlappingNode(node);
            return node;
        }

        //仅使用模板笔画
        private Node AddNode(string text, string category, string color, double centerX, double centerY, double area)
        {
            GetShadowStrokes(null);
            Node node = new Node(++Symbol_id, video_text);
            node.center[0] = centerX;
            node.center[1] = centerY;
            node.area = area;
            if (color == "")
            {
                //使用默认颜色
                color = SymbolColor.d_CategoryColor[category];
            }
            node.SetParam(text, category, color);

            d_idSymbol.Add(Symbol_id, node);
            foreach (Stroke stroke in node.strokes)
            {
                d_idStroke.Add(++Stroke_id, stroke);
                d_StrokeSymbol.Add(stroke, node);
                video_sketch.Strokes.Add(stroke);
            }

            currentSymbols.Add(node);
            PushHistory(new History("AddNode", new Dictionary<string, object> { { "node", node } }));
            RelayoutOverlappingNode(node);
            return node;
        }

        private Edge AddEdge(string text, string category, string color, double weight, double startX, double startY, double endX, double endY)
        {
            GetShadowStrokes(null);
            Edge edge = new Edge(++Symbol_id, video_text);
            edge.weight = weight;
            d_idSymbol.Add(Symbol_id, edge);
            StrokeCollection strokes = new StrokeCollection();
            StylusPointCollection stylusPoints = new StylusPointCollection
            {
                new StylusPoint(startX, startY),
                new StylusPoint(endX, endY)
            };
            strokes.Add(new Stroke(stylusPoints));
            edge.SetStrokes(strokes);
            if (color == "")
            {
                //使用默认颜色
                //color = SymbolColor.d_CategoryColor[category];
                color = SymbolColor.d_CategoryColor["Arrow"];
            }
            edge.SetParam(text, category, color);
            foreach (Stroke stroke in edge.strokes)
            {
                d_idStroke.Add(++Stroke_id, stroke);
                d_StrokeSymbol.Add(stroke, edge);
                video_sketch.Strokes.Add(stroke);
            }

            currentSymbols.Add(edge);
            PushHistory(new History("AddEdge", new Dictionary<string, object> { { "edge", edge } }));
            return edge;
        }

        private void DeleteSymbol(Symbol symbol, bool recursion=true)
        {
            //GetShadowStrokes(symbol);
            if (!currentSymbols.Contains(symbol))
            {
                return;
            }
            symbol.Disconnect();
            symbol.Deleted();
            currentSymbols.Remove(symbol);
            foreach (Stroke stroke in symbol.strokes)
            {
                if (video_sketch.Strokes.Contains(stroke))
                {
                    video_sketch.Strokes.Remove(stroke);
                }
            }
            if (recursion)
            {
                if (symbol is Node)
                {
                    foreach (Edge edge in (symbol as Node).edges_in)
                    {
                        DeleteSymbol(edge, false);
                    }
                    foreach (Edge edge in (symbol as Node).edges_out)
                    {
                        DeleteSymbol(edge, false);
                    }
                }
                //else if (symbol is Edge)
                //{
                //    foreach (Node node in (symbol as Edge).nodes_from)
                //    {
                //        DeleteSymbol(node, false);
                //    }
                //    foreach (Node node in (symbol as Edge).nodes_to)
                //    {
                //        DeleteSymbol(node, false);
                //    }
                //}
            }
            PushHistory(new History("DeleteSymbol", new Dictionary<string, object> { { "symbol", symbol } }));
        }

        private void MoveSymbol(Symbol symbol, double movedDistanceX, double movedDistanceY)
        {
            GetShadowStrokes(symbol);
            Matrix matrix = new Matrix();
            matrix.Translate(movedDistanceX, movedDistanceY);
            foreach (Stroke stroke in symbol.strokes)
            {
                stroke.Transform(matrix, false);
            }
            PushHistory(new History("MoveSymbol", new Dictionary<string, object> { { "symbol", symbol }, { "movedDistanceX", movedDistanceX }, { "movedDistanceY", movedDistanceY } }));
            SymbolMoved(symbol, movedDistanceX, movedDistanceY);
        }

        private void ResizeSymbol(Symbol symbol, Rect bounds)
        {
            GetShadowStrokes(symbol);
            Rect currentBounds = symbol.strokes.GetBounds();
            Matrix matrix = new Matrix();
            matrix.Translate(bounds.Left - currentBounds.Left, bounds.Top - currentBounds.Top);
            matrix.ScaleAt(bounds.Width / currentBounds.Width, bounds.Height / currentBounds.Height, bounds.Left, bounds.Top);
            foreach (Stroke stroke in symbol.strokes)
            {
                stroke.Transform(matrix, false);
            }
            PushHistory(new History("ResizeSymbol", new Dictionary<string, object> { { "symbol", symbol }, { "oldBounds", currentBounds }, { "bounds", bounds } }));
            SymbolResized(symbol, currentBounds, bounds);
        }

        private void ReverseEdge(Edge edge)
        {
            edge.Reverse();
        }

        private void SetParam(Symbol symbol, string param, object value)
        {
            switch (param)
            {
                case "text":
                    SetText(symbol, value as string);
                    break;
                case "class":
                case "category":
                    SetCategory(symbol, value as string);
                    break;
                case "color":
                    SetColor(symbol, value as string);
                    break;
                case "area":
                    SetArea(symbol, (double)value);
                    break;
                case "weight":
                    SetWeight(symbol, (double)value);
                    break;
                case "center":
                    SetCenter(symbol, ((double[])value)[0], ((double[])value)[1]);
                    break;
                default:
                    break;
            }
        }

        private void SetText(Symbol symbol, string text)
        {
            symbol.SetText(text);
        }

        private void SetCategory(Symbol symbol, string category)
        {
            Rect oldBounds = symbol.strokes.GetBounds();
            video_sketch.Strokes.Remove(symbol.strokes);
            foreach (Stroke stroke in symbol.strokes)
            {
                d_StrokeSymbol.Remove(stroke);
            }
            (symbol as Node).SetCategory(category);
            Rect bounds = symbol.strokes.GetBounds();
            video_sketch.Strokes.Add(symbol.strokes);
            foreach (Stroke stroke in symbol.strokes)
            {
                d_StrokeSymbol.Add(stroke, symbol);
            }
            SymbolResized(symbol, oldBounds, bounds);
        }

        private void SetColor(Symbol symbol, string color)
        {
            symbol.SetColor(color);
        }

        private void SetArea(Symbol symbol, double area)
        {
            if (symbol is Node)
            {
                (symbol as Node).SetArea(area);
            }
        }
        private void SetWeight(Symbol symbol, double weight)
        {
            if (symbol is Edge)
            {
                (symbol as Edge).SetWeight(weight);
            }
        }

        private void SetCenter(Symbol symbol, double centerX, double centerY)
        {
            double movedDistanceX = centerX - symbol.center[0];
            double movedDistanceY = centerY - symbol.center[1];
            MoveSymbol(symbol, movedDistanceX, movedDistanceY);
        }

        //复合操作
        private void MoveEdgesByNode(Node node, double movedDistanceX, double movedDistanceY)
        {
            foreach (Edge edge in node.edges_in)
            {
                MoveEdgeTail(edge, movedDistanceX, movedDistanceY);
            }
            foreach (Edge edge in node.edges_out)
            {
                MoveEdgeHead(edge, movedDistanceX, movedDistanceY);
            }
        }

        private Node AddNodeOfNode(Node preNode, string category)
        {
            double centerX = preNode.center[0] + 250;
            double centerY = preNode.center[1] - 100;
            Node node = AddNode("", category, "", centerX, centerY, 160 * 80);
            AddEdgeBetweenNodes(preNode, node);
            return node;
        }

        private Edge AddEdgeBetweenNodes(Node nodeHead, Node nodeTail)
        {
            double[] points = GetLinkPoint(nodeHead, nodeTail);
            //添加连线
            Edge edge = AddEdge("", "Arrow", "", 1, points[0], points[1], points[2], points[3]);
            edge.nodes_from.Add(nodeHead);
            edge.nodes_to.Add(nodeTail);
            nodeHead.edges_out.Add(edge);
            nodeTail.edges_in.Add(edge);
            return edge;
        }

        private Node AddNodeOnEdge(Edge edge)
        {
            double length = 0;
            StylusPointCollection stylusPoints = edge.strokes[0].StylusPoints;
            for (int i = 1; i < stylusPoints.Count; i++)
            {
                length += EulerDistance(stylusPoints[i - 1].X, stylusPoints[i].X, stylusPoints[i - 1].Y, stylusPoints[i].Y);
            }
            length /= 2;
            //查找中点
            double centerX = -1, centerY = -1;
            double currentLength = 0;
            for (int i = 1; i < stylusPoints.Count; i++)
            {
                double pathLength = EulerDistance(stylusPoints[i - 1].X, stylusPoints[i].X, stylusPoints[i - 1].Y, stylusPoints[i].Y);
                currentLength += pathLength;
                if (currentLength > length)
                {
                    double scale = 1 - (currentLength - length) / pathLength;
                    centerX = stylusPoints[i - 1].X + (stylusPoints[i].X - stylusPoints[i - 1].X) * scale;
                    centerY = stylusPoints[i - 1].Y + (stylusPoints[i].Y - stylusPoints[i - 1].Y) * scale;
                    break;
                }
            }
            //添加节点
            Node node = AddNode("", "Process", "", centerX, centerY, 160 * 80);
            //修改连线
            DeleteSymbol(edge);
            foreach (Node nodeHead in edge.nodes_from)
            {
                AddEdgeBetweenNodes(nodeHead, node);
            }
            foreach (Node nodeTail in edge.nodes_to)
            {
                AddEdgeBetweenNodes(node, nodeTail);
            }
            return node;
        }

        private void ChangeLink(Edge edge, Node nodeHead, Node nodeTail)
        {
            double[] points = GetLinkPoint(nodeHead, nodeTail);
            MoveEdgeHead(edge, points[0] - edge.strokes[0].StylusPoints[0].X, points[1] - edge.strokes[0].StylusPoints[0].Y);
            MoveEdgeTail(edge, points[2] - edge.strokes[0].StylusPoints.Last().X, points[1] - edge.strokes[3].StylusPoints.Last().Y);
        }
    }
}