using System.Collections.Generic;
using UnityEngine;
namespace XCharts.Runtime
{
    /// 
    /// the data struct of graph.
    /// ||数据结构-图。
    /// 
    public class Graph
    {
        public bool directed;
        public List nodes = new List();
        public List edges = new List();
        public Dictionary nodeMap = new Dictionary();
        public Dictionary edgeMap = new Dictionary();
        public Graph(bool directed)
        {
            this.directed = directed;
        }
        public void Clear()
        {
            nodes.Clear();
            edges.Clear();
            nodeMap.Clear();
            edgeMap.Clear();
        }
        public void Refresh()
        {
            foreach (var node in nodes)
            {
                node.depth = GetNodeDepth(node);
            }
        }
        public static double GetNodesTotalValue(List nodes)
        {
            double totalValue = 0;
            foreach (var node in nodes)
            {
                totalValue += node.totalValues;
            }
            return totalValue;
        }
        public List> GetDepthNodes()
        {
            List> depthNodes = new List>();
            var maxDepth = GetMaxDepth();
            for (int i = 0; i <= maxDepth; i++)
            {
                depthNodes.Add(new List());
            }
            foreach (var node in nodes)
            {
                if (node.inDegree == 0)
                {
                    depthNodes[0].Add(node);
                }
                else
                {
                    int deep = GetNodeDepth(node);
                    depthNodes[maxDepth - deep].Add(node);
                }
            }
            return depthNodes;
        }
        public List GetRootNodes()
        {
            List rootNodes = new List();
            foreach (var node in nodes)
            {
                if (node.inDegree == 0)
                {
                    rootNodes.Add(node);
                }
            }
            return rootNodes;
        }
        public int GetMaxDepth()
        {
            int maxDepth = 0;
            foreach (var node in nodes)
            {
                int deep = GetNodeDepth(node);
                if (deep > maxDepth)
                {
                    maxDepth = deep;
                }
            }
            return maxDepth;
        }
        // public int GetNodeDepth(GraphNode node)
        // {
        //     int depth = 0;
        //     GetNodeDepth(node, ref depth);
        //     return depth;
        // }
        // public void GetNodeDepth(GraphNode node, ref int depth, int recursiveCount = 0)
        // {
        //     if (recursiveCount > 50)
        //     {
        //         XLog.Error("Graph.GetNodeDeep(): recursiveCount > 50, maybe graph is ring");
        //         return;
        //     }
        //     if (node.inDegree == 0)
        //     {
        //         return;
        //     }
        //     else
        //     {
        //         depth += 1;
        //         foreach (var edge in node.inEdges)
        //         {
        //             GetNodeDepth(edge.node1, ref depth, recursiveCount + 1);
        //         }
        //     }
        // }
        public int GetNodeDepth(GraphNode node, int recursiveCount = 0)
        {
            if (recursiveCount > 50)
            {
                XLog.Error("Graph.GetNodeDeep(): recursiveCount > 50, maybe graph is ring");
                return 0;
            }
            int depth = 0;
            if (node.outDegree == 0)
            {
                return depth;
            }
            else
            {
                foreach (var edge in node.outEdges)
                {
                    int otherDeep = GetNodeDepth(edge.node2, recursiveCount + 1);
                    if (otherDeep > depth)
                    {
                        depth = otherDeep;
                    }
                }
                return depth + 1;
            }
        }
        public GraphNode GetNode(string nodeId)
        {
            if (nodeMap.ContainsKey(nodeId))
            {
                return nodeMap[nodeId];
            }
            else
            {
                return null;
            }
        }
        public GraphEdge GetEdge(string nodeId1, string nodeId2)
        {
            if (directed)
            {
                return edgeMap[nodeId1 + "_" + nodeId2];
            }
            else
            {
                var key = nodeId1 + "_" + nodeId2;
                if (edgeMap.ContainsKey(key))
                {
                    return edgeMap[key];
                }
                else
                {
                    key = nodeId2 + "_" + nodeId1;
                    if (edgeMap.ContainsKey(key))
                    {
                        return edgeMap[key];
                    }
                    else
                    {
                        return null;
                    }
                }
            }
        }
        public GraphNode AddNode(string nodeId, string nodeName, int dataIndex)
        {
            if (nodeMap.ContainsKey(nodeId))
            {
                return nodeMap[nodeId];
            }
            else
            {
                GraphNode node = new GraphNode(nodeId, nodeName, dataIndex);
                node.hostGraph = this;
                nodeMap.Add(nodeId, node);
                nodes.Add(node);
                return node;
            }
        }
        public GraphEdge AddEdge(string nodeId1, string nodeId2, double value)
        {
            GraphNode node1, node2;
            if (!nodeMap.TryGetValue(nodeId1, out node1))
            {
                XLog.Warning("Graph.AddEdge(): " + nodeId1 + " not exist");
                return null;
            }
            if (!nodeMap.TryGetValue(nodeId2, out node2))
            {
                XLog.Warning("Graph.AddEdge(): " + nodeId2 + " not exist");
                return null;
            }
            if (node1 == null)
            {
                XLog.Warning("Graph.AddEdge(): node1 is null");
                return null;
            }
            if (node2 == null)
            {
                XLog.Warning("Graph.AddEdge(): node2 is null");
                return null;
            }
            if (node1 == node2)
            {
                XLog.Warning("Graph.AddEdge(): node1 == node2");
                return null;
            }
            string edgeKey = nodeId1 + "_" + nodeId2;
            if (edgeMap.ContainsKey(edgeKey))
            {
                return edgeMap[edgeKey];
            }
            else
            {
                GraphEdge edge = new GraphEdge(node1, node2, value);
                edge.key = edgeKey;
                edge.hostGraph = this;
                if (directed)
                {
                    node1.outEdges.Add(edge);
                    node2.inEdges.Add(edge);
                }
                node1.edges.Add(edge);
                if (node1 != node2)
                {
                    node2.edges.Add(edge);
                }
                edgeMap.Add(edgeKey, edge);
                edges.Add(edge);
                return edge;
            }
        }
        public void EachNode(System.Action onEach)
        {
            if (onEach == null) return;
            foreach (var node in nodes)
            {
                onEach(node);
            }
        }
        public void BreadthFirstTraverse(GraphNode startNode, System.Action onTraverse)
        {
            if (startNode == null) return;
            foreach (var node in nodes)
            {
                node.visited = false;
            }
            onTraverse(startNode);
            startNode.visited = true;
            Queue queue = new Queue();
            queue.Enqueue(startNode);
            while (queue.Count > 0)
            {
                var currentNode = queue.Dequeue();
                foreach (var edge in currentNode.edges)
                {
                    var otherNode = edge.node1 == currentNode ? edge.node2 : edge.node1;
                    if (!otherNode.visited)
                    {
                        onTraverse(otherNode);
                        otherNode.visited = true;
                        queue.Enqueue(otherNode);
                    }
                }
            }
        }
        public void DeepFirstTraverse(GraphNode startNode, System.Action onTraverse)
        {
            if (startNode == null) return;
            foreach (var node in nodes)
            {
                node.visited = false;
            }
            Stack stack = new Stack();
            stack.Push(startNode);
            while (stack.Count > 0)
            {
                var currentNode = stack.Pop();
                if (!currentNode.visited)
                {
                    onTraverse(currentNode);
                    currentNode.visited = true;
                }
                foreach (var edge in currentNode.edges)
                {
                    var otherNode = edge.node1 == currentNode ? edge.node2 : edge.node1;
                    if (!otherNode.visited)
                    {
                        stack.Push(otherNode);
                    }
                }
            }
        }
    }
    /// 
    /// The node of graph.
    /// ||图的节点。
    /// 
    public class GraphNode
    {
        public string id;
        public string name;
        public List edges = new List();
        public List inEdges = new List();
        public List outEdges = new List();
        public Graph hostGraph;
        public int dataIndex;
        public bool visited;
        public int depth = -1;
        public GraphNode(string id, string name, int dataIndex)
        {
            this.id = id;
            this.name = name;
            this.dataIndex = dataIndex;
        }
        public int degree { get { return edges.Count; } }
        public int inDegree { get { return inEdges.Count; } }
        public int outDegree { get { return outEdges.Count; } }
        public double totalValues
        {
            get
            {
                double totalValue = 0;
                if (inEdges.Count == 0)
                {
                    foreach (var edge in outEdges)
                    {
                        totalValue += edge.value;
                    }
                }
                else
                {
                    foreach (var edge in inEdges)
                    {
                        totalValue += edge.value;
                    }
                }
                return totalValue;
            }
        }
        public override string ToString()
        {
            return name;
        }
    }
    /// 
    /// The edge of graph.
    /// ||图的边。
    /// 
    public class GraphEdge
    {
        public string key;
        public GraphNode node1;
        public GraphNode node2;
        public double value;
        public Graph hostGraph;
        public List points = new List();
        public float width;
        public bool highlight;
        public GraphEdge(GraphNode node1, GraphNode node2, double value)
        {
            this.node1 = node1;
            this.node2 = node2;
            this.value = value;
        }
    }
}