using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace wispro.sp.share.Utility
{
    public class shapeNode
    {
        public int InCount { get; set; }

        public int OutCount { get; set; }
        public double x { get; set; }

        public double y { get; set; }

        public double width { get; set; }

        public double height { get; set; }

        /// <summary>
        /// 形状类型
        /// 0：圆
        /// 1：矩形
        /// 3: 虚拟
        /// </summary>
        public int Type { get; set; }

        public int Level { get; set; } = 1;

        public dynamic NodeObject { get; set; }

        public List<shapeNode> Childrens { get; set; }

        public List<shapeNode> Parents { get; set; }

        public string FillColor { get; set; } = "white";

    }
    public class FlowChartUtility
    {
        public double TitleHeight { get; set; } = 100;
        public double ChartWidth { get; set; } = 1200;
        public double ChartHeight { get; set; } = 900;
        public double rectWidth { get; set; } = 150;
        public double rectHeight { get; set; } = 60;
        public double initRadius { get; set; } = 20;
        public double EndRadius { get; set; } = 20;
        public double hSeparation { get; set; } = 40;
        public string StepShapeColor { get; set; } = "green";
        public string EndShapColor { get; set; } = "gray";
        int rectFontSize { get; set; } = 18;
        public List<entity.workflowDefine.Step> Steps { get; set; }
        public List<entity.workflowDefine.TrasferCondition> Transfers { get; set; }
        public List<entity.workflowDefine.Action> Actions { get; set; }
        public entity.workflowDefine.Workflow workflow { get; set; }

        private List<shapeNode> shapeTrees = null;

        private shapeNode FindNode(int stepId, out int Level, List<shapeNode> lstNodes)
        {

            Level = 1;
            foreach (var sNode in lstNodes)
            {
                if (sNode.NodeObject is entity.workflowDefine.Step && sNode.NodeObject.Id == stepId)
                {
                    return sNode;
                }
                else
                {
                    if (sNode.Childrens != null)
                    {
                        Level += 1;

                        var retObj = FindNode(stepId, out Level, sNode.Childrens);

                        if (retObj != null)
                        {
                            return retObj;
                        }
                    }

                }
            }

            return null;
        }

        private void GetShapeLevelNodes(Dictionary<int, List<shapeNode>> levelNodes, List<shapeNode> TreeNodes)
        {
            if (TreeNodes != null)
            {
                foreach (var sNode in TreeNodes)
                {
                    List<shapeNode> nodes = new List<shapeNode>();
                    if (levelNodes.ContainsKey(sNode.Level))
                    {
                        nodes = levelNodes[sNode.Level];
                        nodes.Add(sNode);
                    }
                    else
                    {
                        nodes.Add(sNode);
                        levelNodes.Add(sNode.Level, nodes);
                    }

                    GetShapeLevelNodes(levelNodes, sNode.Childrens);
                }
            }
        }

        public shapeNode startNode;
        public shapeNode InitShape;
        public shapeNode endNode;
        public Dictionary<int, List<shapeNode>> LevelNodes = new Dictionary<int, List<shapeNode>>();

        private void initShapeTree()
        {
            shapeTrees = new List<shapeNode>();
            LevelNodes = new Dictionary<int, List<shapeNode>>();

            #region 添加开始节点
            startNode = new shapeNode()
            {
                //NodeObject = workflow.InitAction,
                InCount = 0,
                OutCount = 0,
                width = 2 * initRadius,
                height = 2 * initRadius,
                Level = 0
            };

            #endregion

            #region 添加初始化Action节点
            InitShape = new shapeNode()
            {
                NodeObject = workflow.InitAction,
                InCount = 0,
                OutCount = 0,
                Type = 1,
                Level = 1,
                FillColor = StepShapeColor
            };

            startNode.Childrens = new List<shapeNode>();
            startNode.Childrens.Add(InitShape);

            InitShape.Parents = new List<shapeNode>();
            InitShape.Parents.Add(startNode);

            shapeTrees.Add(startNode);
            #endregion

            #region 将步骤对象生成形状Node并添加到列表中


            if (Steps != null)
            {
                foreach (var step in Steps)
                {
                    var temNode = new shapeNode() { NodeObject = step, InCount = 0, OutCount = 0, Type = 1, FillColor = StepShapeColor };

                    if (workflow.EndStepId == step.Id)
                    {
                        endNode = new shapeNode()
                        {
                            NodeObject = step,
                            InCount = 0,
                            OutCount = 0,
                            height = 2 * EndRadius,
                            width = 2 * EndRadius,
                            FillColor = EndShapColor
                        };
                        temNode = endNode;
                    }


                    shapeTrees.Add(temNode);
                }
            }


            #endregion

            #region 遍历转移条件，生成流程树
            if (Transfers != null && Transfers.Count > 0)
            {
                foreach (var transfer in Transfers)
                {
                    var FromNode = InitShape;
                    int FromLevel = 0;

                    if (transfer.StepId != null)
                    {
                        FromNode = FindNode(transfer.StepId.Value, out FromLevel, shapeTrees);
                    }

                    int ToLevel = 0;
                    var ToNode = FindNode(transfer.nextStepId, out ToLevel, shapeTrees);

                    if (FromNode.Childrens == null)
                    {
                        FromNode.Childrens = new List<shapeNode>();
                    }
                    FromNode.Childrens.Add(ToNode);

                    if (ToNode.Parents == null)
                    {
                        ToNode.Parents = new List<shapeNode>();
                    }
                    ToNode.Parents.Add(FromNode);

                    FromNode.OutCount += 1;
                    ToNode.InCount += 1;

                    if (FromNode.Level >= ToLevel)
                    {
                        ToNode.Level = FromNode.Level + 1;
                    }

                    if (shapeTrees.Contains(ToNode))
                    {

                        shapeTrees.Remove(ToNode);
                    }
                }

                if (endNode.Parents != null && endNode.Parents[0].NodeObject is entity.workflowDefine.Step)
                {
                    endNode.Level = endNode.Parents[0].Level + 1;
                }
            }
            else
            {
                InitShape.Childrens = new List<shapeNode>();
                InitShape.Childrens.Add(endNode);
                InitShape.OutCount += 1;
                endNode.Parents = new List<shapeNode>();
                endNode.Parents.Add(InitShape);
                endNode.InCount += 1;
                endNode.Level = InitShape.Level + 1;
                shapeTrees.Remove(endNode);
                //Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(endNode));

            }
            #endregion

            GetShapeLevelNodes(LevelNodes, shapeTrees);

            #region 添加跨层连接的中间层的虚拟节点
            List<dynamic> AddNodes = new List<dynamic>();
            foreach (int level in LevelNodes.Keys)
            {
                foreach (var temNode in LevelNodes[level])
                {
                    if (temNode.Childrens != null)
                    {
                        foreach (var temChildrenNode in temNode.Childrens)
                        {

                            if ((temChildrenNode.Level - temNode.Level) > 1)
                            {
                                dynamic temAddInfo = new ExpandoObject();
                                temAddInfo.Parent = temNode;
                                temAddInfo.Children = temChildrenNode;
                                temAddInfo.Start = temNode.Level;
                                temAddInfo.End = temChildrenNode.Level;
                                AddNodes.Add(temAddInfo);


                            }
                        }
                    }
                }
            }

            foreach (dynamic dynObj in AddNodes)
            {
                shapeNode parentNode = dynObj.Parent;
                shapeNode endNode = dynObj.Children;

                for (int i = dynObj.Start + 1; i < dynObj.End; i++)
                {
                    var xnNode = new shapeNode() { Type = 3, Parents = new List<shapeNode>(), Childrens = new List<shapeNode>(), Level = i };
                    parentNode.Childrens.Add(xnNode);
                    xnNode.Parents.Add(parentNode);

                    parentNode.Childrens.Remove(endNode);
                    parentNode.Childrens.Add(xnNode);

                    endNode.Parents.Remove(parentNode);
                    endNode.Parents.Add(xnNode);
                    parentNode = xnNode;

                    LevelNodes[xnNode.Level].Add(xnNode);
                }

            }
            #endregion

            int MaxNodeLevel = 0;
            int NodeCount = 0;

            foreach (int level in LevelNodes.Keys)
            {
                if (level > 1)
                {
                    int temLevelCount = 0;

                    foreach (var temNode in LevelNodes[level])
                    {
                        int graterInOrOut = (temNode.OutCount > temNode.InCount) ? temNode.OutCount : temNode.InCount;
                        if (graterInOrOut <= 1)
                        {
                            temLevelCount += 1;
                        }
                        else
                        {
                            temLevelCount += graterInOrOut;
                        }
                    }

                    if (temLevelCount > MaxNodeLevel)
                    {
                        MaxNodeLevel = level;
                        NodeCount = temLevelCount;
                    }
                }
            }

            #region 每层节点排序

            for (var i = 1; i < LevelNodes.Count; ++i)
            {
                var top_layer = LevelNodes[i - 1];

                LevelNodes[i] = LevelNodes[i].OrderBy(node =>
                {
                    if (node.Parents == null)
                    {
                        return 0;
                    }

                    var connected_nodes = node.Parents
                        .Where(l => l.Level == (i - 1)).ToList();

                    if (!connected_nodes.Any())
                    {
                        return 0;
                    }
                    var average_index = connected_nodes.Select(cn => { var i = top_layer.IndexOf(cn); return i; }).Average();
                    return average_index;
                }).ToList();
            }

            if (LevelNodes.Count > 1)
            {
                LevelNodes[0] = LevelNodes[0].OrderBy(node =>
                {
                    var connected_nodes = node.Childrens
                        .Where(l => l.Level == 1)
                        .ToList();

                    if (!connected_nodes.Any())
                    {
                        return 0;
                    }
                    var average_index = connected_nodes.Select(cn => { var i = LevelNodes[1].IndexOf(cn); return i; }).Average();
                    return average_index;
                }).ToList();
            }
            #endregion

            ArrangeNodesInRows(LevelNodes);

        }

        private void ArrangeNodesInRows(Dictionary<int, List<shapeNode>> LevelNodes)
        {
            double preBotton = TitleHeight;
            for (int iLevel = 0; iLevel < LevelNodes.Count; iLevel++)
            {
                int iCount = 0;
                foreach (var node in LevelNodes[iLevel])
                {
                    int max = (node.InCount > node.OutCount) ? node.InCount : node.OutCount;
                    if (max == 0)
                    {
                        max = 1;
                    }
                    iCount += max;
                }

                double onNodeWidth = ChartWidth / (iCount + 1);

                iCount = 0;
                double maxHeight = 0;
                foreach (var node in LevelNodes[iLevel])
                {


                    int max = (node.InCount > node.OutCount) ? node.InCount : node.OutCount;
                    if (max == 0)
                    {
                        max = 1;
                    }

                    node.x = onNodeWidth * (iCount + 1) + (max - 1) * onNodeWidth / 2;
                    if (node.Type == 0)
                    {
                        node.width = 2 * initRadius;
                        node.height = 2 * initRadius;
                    }
                    else
                    {
                        node.width = rectWidth;
                        node.height = rectHeight;
                    }
                    iCount += max;

                    if (node.height > maxHeight)
                    {
                        maxHeight = node.height;
                    }
                }

                foreach (var node in LevelNodes[iLevel])
                {
                    node.y = preBotton + hSeparation + maxHeight / 2;
                }

                preBotton = preBotton + hSeparation + maxHeight;
            }


        }

        public dynamic GetLineParater(entity.workflowDefine.TrasferCondition trasferCondition)
        {
            int level = 0;
            var startNode = InitShape;
            if (trasferCondition.StepId != null)
            {
                startNode = FindNode(trasferCondition.StepId.Value, out level, shapeTrees);
            }
            var endNode = FindNode(trasferCondition.nextStepId, out level, shapeTrees);

            dynamic ret = new ExpandoObject();
            ret.x1 = startNode.x;
            ret.y1 = startNode.y + startNode.height / 2;

            ret.x2 = endNode.x;
            ret.y2 = endNode.y - endNode.height / 2;

            double x3 = 0.0;
            double y3 = 0.0;
            GetArrorEndPoint(ret, out x3, out y3);

            ret.x2 = x3;
            ret.y2 = y3;

            return ret;
        }

        public void GetArrorEndPoint(dynamic ret, out double x3, out double y3)
        {
            if (ret.x1 == ret.x2)
            {
                x3 = ret.x1;
                if (ret.y2 > ret.y1)
                {
                    y3 = ret.y2 - 8;
                }
                else
                {
                    y3 = ret.y2 + 8;
                }
            }
            else
            {
                if (ret.y1 == ret.y2)
                {
                    y3 = ret.y1;

                    if (ret.x1 < ret.x2)
                    {
                        x3 = ret.x2 - 8;
                    }
                    else
                    {
                        x3 = ret.x2 + 8;
                    }
                }
                else
                {
                    double k = (ret.y1 - ret.y2) / (ret.x1 - ret.x2);
                    double b = ret.y2 - k * ret.x2;

                    double a = k * k + 1;
                    double B = 2 * k * (b - ret.y2) - 2 * ret.x2;
                    double c = (b - ret.y2) * (b - ret.y2) + ret.x2 * ret.x2 - 100;


                    double x3_1 = (Math.Sqrt(B * B - 4 * a * c) - B) / (2 * a);
                    double x3_2 = ((Math.Sqrt(B * B - 4 * a * c) + B) / (2 * a)) * -1;

                    if (ret.x1 < ret.x2)
                    {
                        x3 = (x3_1 < x3_2) ? x3_1 : x3_2;
                    }
                    else
                    {
                        x3 = (x3_1 < x3_2) ? x3_2 : x3_1;
                    }

                    y3 = k * x3 + b;
                }
            }
        }

        public dynamic GetEndStepLine()
        {
            var startNode = InitShape;
            if (endNode.Parents != null)
            {
                startNode = endNode.Parents[0];
            }
            dynamic ret = new ExpandoObject();
            ret.x1 = startNode.x;
            ret.y1 = startNode.y + startNode.height / 2;

            ret.x2 = endNode.x;
            ret.y2 = endNode.y - endNode.height / 2;

            double x3 = 0.0;
            double y3 = 0.0;
            GetArrorEndPoint(ret, out x3, out y3);

            ret.x2 = x3;
            ret.y2 = y3;

            return ret;
        }

        public dynamic GetStartInitLine()
        {
            dynamic ret = new ExpandoObject();
            ret.x1 = this.startNode.x;
            ret.y1 = this.startNode.y + startNode.height / 2;

            ret.x2 = InitShape.x;
            ret.y2 = InitShape.y - InitShape.height / 2;

            double x3 = 0.0;
            double y3 = 0.0;
            GetArrorEndPoint(ret, out x3, out y3);

            ret.x2 = x3;
            ret.y2 = y3;

            return ret;

        }


        public void Refresh()
        {
            //System.Text.Json.JsonSerializer.Serialize(Actions);
            initShapeTree();
        }




        void InitAction()
        {

        }

        #region 单击/双击事件
        public EventCallback<entity.workflowDefine.Step> OnClickStep { get; set; }

        public EventCallback<entity.workflowDefine.Action> OnClickAction { get; set; }

        public EventCallback<entity.workflowDefine.TrasferCondition> OnClickTransfer { get; set; }

        public EventCallback<entity.workflowDefine.Step> OnDoubleClickStep { get; set; }

        [Parameter]
        public EventCallback<entity.workflowDefine.Action> OnDoubleClickAction { get; set; }

        [Parameter]
        public EventCallback<entity.workflowDefine.TrasferCondition> OnDoubleClickTransfer { get; set; }

        public shapeNode SelectedShape { get; set; }

        void ClickNode(shapeNode node)
        {
            SelectedShape = node;

            if (node.NodeObject is entity.workflowDefine.Step)
            {
                if (OnClickStep.HasDelegate)
                {
                    OnClickStep.InvokeAsync((entity.workflowDefine.Step)node.NodeObject);
                }
            }

            if (node.NodeObject is entity.workflowDefine.Action)
            {
                if (OnClickAction.HasDelegate)
                {
                    OnClickAction.InvokeAsync((entity.workflowDefine.Action)node.NodeObject);
                }
            }
        }

        void DoubleClickNode(shapeNode node)
        {
            SelectedShape = node;

            if (node.NodeObject is entity.workflowDefine.Step)
            {
                if (OnDoubleClickStep.HasDelegate)
                {
                    OnDoubleClickStep.InvokeAsync((entity.workflowDefine.Step)node.NodeObject);
                }
            }

            if (node.NodeObject is entity.workflowDefine.Action)
            {
                if (OnDoubleClickAction.HasDelegate)
                {
                    OnDoubleClickAction.InvokeAsync((entity.workflowDefine.Action)node.NodeObject);
                }
            }
        }

        void ClickTrasfer(entity.workflowDefine.TrasferCondition trasferCondition)
        {
            SelectedShape = null;

            if (OnClickTransfer.HasDelegate)
            {
                OnClickTransfer.InvokeAsync(trasferCondition);
            }
        }

        void DoubleClickTrasfer(entity.workflowDefine.TrasferCondition trasferCondition)
        {
            SelectedShape = null;

            if (OnDoubleClickTransfer.HasDelegate)
            {
                OnDoubleClickTransfer.InvokeAsync(trasferCondition);
            }
        }
        #endregion

        public string GetSvgString()
        {
            initShapeTree();
            string strReturn = $"<svg width =\"{ChartWidth}\" height = \"{ChartHeight}\" id = \"FlowChartContainer\" xmlns=\"http://www.w3.org/2000/svg\"  version=\"1.2\">";
            strReturn = strReturn +
                "<defs>\r\n" +
                    "\t<marker id =\"idArrow\" viewBox=\"0 0 20 20\" refX = \"0\" refY = \"10\" markerUnits = \"strokeWidth\" markerWidth = \"5\" markerHeight = \"10\" orient=\"auto\">\r\n" +
                        "\t\t<path d =\"M 0 0 L 20 10 L 0 20 z\" fill=\"black\" stroke = \"black\" />\r\n" +
                    "</marker>\r\n" +
                "</defs>\r\n";

            if (shapeTrees != null)
            {
                strReturn += $"<text x=\"{ChartWidth / 2}\" y=\"{TitleHeight / 2}\" fill=\"black\" alignment-baseline=\"middle\" text-anchor=\"middle\" font-size=\"{30}\" font-weight=\"800\">{workflow.Name}</text>\r\n";
                dynamic startLine = GetStartInitLine();
                strReturn += $"<line x1=\"{startLine.x1}\" y1=\"{startLine.y1}\" x2 =\"{startLine.x2}\" y2=\"{startLine.y2}\" stroke=\"black\" stroke-width=\"2\" marker-end=\"url(#idArrow)\"/>\r\n";

                for (int iLevel = 0; iLevel < LevelNodes.Count; iLevel++)
                {
                    foreach (var node in LevelNodes[iLevel])
                    {
                        if (node.Type == 0)
                        {
                            strReturn += $"<circle cx =\"{node.x}\" cy=\"{node.y}\" r=\"{node.width / 2}\" stroke=\"black\" stroke-width=\"2\" fill=\"{node.FillColor}\"/>\r\n";
                            strReturn += "<a>" +
                                  $"<text x =\"{node.x}\" y=\"{node.y}\" fill=\"black\" alignment-baseline=\"middle\" text-anchor=\"middle\" font-size=\"14\">\r\n";
                            if (node == startNode)
                            {
                                strReturn += "开始";
                            }
                            else
                            {
                                if (node == endNode)
                                {
                                    strReturn += "结束";
                                }
                                else
                                {
                                    if (node.NodeObject is entity.workflowDefine.Step)
                                    {
                                        strReturn += ((entity.workflowDefine.Step)node.NodeObject).Name;
                                    }
                                }
                            }
                            strReturn += "</text>\r\n</a>\r\n";
                        }

                        if (node.Type == 1)
                        {
                            strReturn += $"<rect x =\"{(node.x-node.width/2)}\" y=\"{(node.y-node.height/2)}\" rx=\"10\" ry=\"10\" width=\"{node.width}\" height=\"{node.height}\" style=\"fill:{node.FillColor};stroke:black;stroke-width:3;opacity:0.5\"/>\r\n";
                            strReturn += $"<a ondblclick =\"() => DoubleClickNode(node)\" onclick=\"()=>ClickNode(node)\">\r\n";
                            strReturn += $"<text x =\"{node.x}\" y=\"{node.y}\" fill=\"black\" alignment-baseline=\"middle\" text-anchor=\"middle\" font-size=\"{rectFontSize}\" font-weight=\"700\">\r\n";
                            
                            if (node.NodeObject is entity.workflowDefine.Action)
                            {
                                strReturn += ((workflow.InitAction == null || string.IsNullOrEmpty(workflow.InitAction.Name)) ? $"启动{workflow.Name}" : workflow.InitAction.Name) + "\r\n";
                            }
                            else
                            {
                                strReturn += (((entity.workflowDefine.Step)node.NodeObject).Name) + "\r\n";
                            }

                            strReturn += "</text>\r\n</a>\r\n";

                            if (SelectedShape == node)
                            {
                                strReturn += $"<circle cx =\"{(node.x - node.width / 2)}\" cy=\"{(node.y)}\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"white\"/>\r\n";
                                strReturn += $"<circle cx =\"{(node.x + node.width / 2)}\"cy=\"{(node.y)}\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"white\"/>\r\n";
                                strReturn += $"<circle cx =\"{(node.x)}\" cy=\"{(node.y - node.height / 2)}\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"white\"/>\r\n";
                                strReturn += $"<circle cx =\"{(node.x)}\" cy=\"{(node.y + node.height / 2)}\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"white\"/>\r\n";
                            }
                        }
                    }
                }

                if (Transfers.Count > 0)
                {
                    foreach (var t in Transfers)
                    {
                        dynamic ret = GetLineParater(t);

                        strReturn += $"<line x1 =\"{ret.x1}\" y1=\"{ret.y1}\" x2=\"{ret.x2}\" y2=\"{ret.y2}\" stroke=\"black\" stroke-width=\"2\" marker-end=\"url(#idArrow)\"/>\r\n";
                        strReturn += $"<circle cx =\"{((ret.x1 + ret.x2) / 2)}\" cy=\"{((ret.y1 + ret.y2) / 2)}\" r=\"7\" stroke=\"black\" stroke-width=\"1.5\" fill=\"white\"/>\r\n";
                        strReturn += $"<a ondblclick =\"() => DoubleClickTrasfer(t)\" onclick=\"()=>ClickTrasfer(t)\">\r\n";
                        strReturn += $"<text x =\"{((ret.x1 + ret.x2) / 2)}\" y=\"{((ret.y1 + ret.y2) / 2)}\" fill=\"black\" alignment-baseline=\"middle\" text-anchor=\"middle\" font-size=\"12\">c</text>\r\n";
                        strReturn += "</a>\r\n";
                    }
                }
                else
                {
                    dynamic ret = GetEndStepLine();
                    strReturn += $"<line x1 =\"{ret.x1}\" y1=\"{ret.y1}\" x2=\"{ret.x2}\" y2=\"{ret.y2}\" stroke=\"black\" stroke-width=\"2\" marker-end=\"url(#idArrow)\"/>\r\n";

                }
                //dynamic endLine = GetEndStepLine();
                //strReturn += $"<line x1 =\"{endLine.x1}\" y1=\"{endLine.y1}\" x2=\"{endLine.x2}\" y2=\"{endLine.y2}\" stroke=\"black\" stroke-width=\"2\" marker-end=\"url(#idArrow)\"/>\r\n";
            }

            return strReturn + "</svg>";
        }
    }
}
