using System;
using Game.Plugins.JNGame.Sync.Frame.AStar.Util;

namespace Plugins.JNGame.Sync.Frame.AStar.Entity
{
    /// <summary>  
    /// 存储单个路径查找请求的临时节点数据。  
    /// 每个节点在每个使用的线程中都有一个PathNode实例。  
    /// 它存储例如G分数、H分数和其他用于路径计算的临时变量，  
    /// 这些变量不是图形结构的一部分。
    /// 
    ///  该类用于存储单个路径查找请求中的临时节点数据。这个类似乎与路径查找算法（如 A* 搜索算法）相关，用于在图形或网格中查找从起点到终点的最佳路径
    /// 
    /// 参见：Pathfinding.PathHandler  
    /// 参见：https://en.wikipedia.org/wiki/A*_search_algorithm  
    /// </summary>  
    public class PathNode
    {
        
        /// <summary>搜索树中的父节点</summary>  
        public PathNode parent;
        
        /// <summary>上次使用此节点的路径请求（如果使用了多线程，则指当前线程中的请求）</summary>  
        public ushort pathID;
        
        /// <summary>用于存储G分数的后备字段</summary>  
        private uint g; // 定义一个私有字段g，类型为无符号整数（uint），用于存储G分数  
  
        /// <summary>用于存储H分数的后备字段</summary>  
        private uint h; // 定义一个私有字段h，类型为无符号整数（uint），用于存储H分数  
  
        /// <summary>G分数，表示到达此节点的成本</summary>  
        public uint G { get { return g; } set { g = value; } }
  
        /// <summary>H分数，表示估计到达目标的成本</summary>  
        public uint H { get { return h; } set { h = value; } }
        
        /// <summary>F分数，等于H分数与G分数的和</summary>  
        public uint F { get { return g + h; } }
        
        
        /// <summary>  
        /// 位打包变量，用于存储多个字段  
        /// </summary>  
        private uint flags; // 定义一个无符号整数变量flags，用于存储多个标志位和成本信息  
  
        /// <summary>  
        /// 成本使用前28位  
        /// </summary>  
        private const uint CostMask = (1U << 28) - 1U; // 定义一个常量CostMask，用于提取flags变量中的成本信息，它占据前28位  
  
        /// <summary>  
        /// 标志1位于第28位  
        /// </summary>  
        private const int Flag1Offset = 28; // 定义一个常量Flag1Offset，表示标志1在flags变量中的位偏移量  
        private const uint Flag1Mask = (uint)(1 << Flag1Offset); // 定义一个常量Flag1Mask，用于标记和提取flags变量中的标志1，通过位运算将第28位设置为1，其余位为0  
  
        /// <summary>  
        /// 标志2位于第29位  
        /// </summary>  
        private const int Flag2Offset = 29; // 定义一个常量Flag2Offset，表示标志2在flags变量中的位偏移量  
        private const uint Flag2Mask = (uint)(1 << Flag2Offset); // 定义一个常量Flag2Mask，用于标记和提取flags变量中的标志2，通过位运算将第29位设置为1，其余位为0

        public uint cost {
            get {
                return flags & CostMask;
            }
            set {
                flags = (flags & ~CostMask) | value;
            }
        }
        
        /// <summary>  
        /// 在路径查找过程中用作临时标志。  
        /// 仅路径查找器（Pathfinders）在路径查找过程中可以使用此标志来标记节点。  
        /// 当完成路径查找后，应将此标志恢复为其默认状态（false），以避免干扰其他路径查找请求。  
        /// </summary>  
        public bool flag1 {  
            get {  
                // 获取flag1的值。使用位运算检查flags变量中的Flag1Mask位是否被设置。  
                return (flags & Flag1Mask) != 0;  
            }  
            set {  
                // 设置flag1的值。使用位运算来设置或清除Flags变量中的Flag1Mask位。  
                flags = (flags & ~Flag1Mask) | (value ? Flag1Mask : 0U);  
            }  
        }  
  
        /// <summary>  
        /// 在路径查找过程中用作临时标志。  
        /// 仅路径查找器（Pathfinders）在路径查找过程中可以使用此标志来标记节点。  
        /// 当完成路径查找后，应将此标志恢复为其默认状态（false），以避免干扰其他路径查找请求。  
        /// </summary>  
        public bool flag2 {  
            get {  
                // 获取flag2的值。使用位运算检查Flags变量中的Flag2Mask位是否被设置。  
                return (flags & Flag2Mask) != 0;  
            }  
            set {  
                // 设置flag2的值。使用位运算来设置或清除Flags变量中的Flag2Mask位。  
                flags = (flags & ~Flag2Mask) | (value ? Flag2Mask : 0U);  
            }  
        }
        
        /// <summary>  
        /// 对实际图节点的引用  
        /// </summary>  
        public GraphNode node; // 这是一个对图节点的引用，用于指向实际存储节点信息的对象。通过这个属性，可以访问和操作这个节点。
        
    }

    public class PathHandler
    {
        
        private ushort pathID;
        /// <summary>正在计算或最近一次计算过的路径的ID</summary>  
        public ushort PathID { get { return pathID; } }
        
        /// <summary>  
        /// 用于追踪“开放列表”上的节点的二叉堆。  
        /// 参见：https://en.wikipedia.org/wiki/A*_search_algorithm  
        /// </summary>  
        public readonly BinaryHeap heap = new BinaryHeap(128);
        
        /// <summary>所有PathNode的数组</summary>  
        public PathNode[] nodes = new PathNode[0];
        
        
        /// <summary>  
        /// 将所有节点的pathID设置为0。  
        /// 参见：Pathfinding.PathNode.pathID  
        /// </summary>  
        public void ClearPathIDs () {  
            // 遍历nodes数组中的每个元素  
            for (int i = 0; i < nodes.Length; i++) {  
                // 如果当前节点不为null  
                if (nodes[i] != null)   
                    // 将该节点的pathID设置为0  
                    nodes[i].pathID = 0;  
            }  
        }
        
        
        /// <summary>
        /// 初始路线数据
        /// </summary>
        /// <param name="p"></param>
        public void InitializeForPath (JNPath p) {
            pathID = p.pathID;
            heap.Clear();
        }
        
        /// <summary>  
        /// 根据指定的节点返回对应的PathNode。  
        /// 由于在多线程启用的情况下可能会同时使用多个PathHandler，  
        /// 因此这个PathNode是特定于这个PathHandler的。  
        /// </summary>  
        /// <param name="node">要获取其PathNode的GraphNode。</param>  
        /// <returns>与指定节点对应的PathNode。</returns>  
        public PathNode GetPathNode(GraphNode node) {  
            // 使用节点索引从nodes数组中检索对应的PathNode  
            return nodes[node.NodeIndex];  
        }
        
        /// <summary>  
        /// 内部方法，用于初始化节点数据  
        /// </summary>  
        public void InitializeNode(GraphNode node)  
        {  
            // 获取节点的索引  
            //Get the index of the node  
            int ind = node.NodeIndex;  
  
            // 如果索引大于或等于当前nodes数组的长度  
            if (ind >= nodes.Length)  
            {  
                // 则扩大数组的长度，扩大倍数为2  
                // Grow by a factor of 2  
                PathNode[] newNodes = new PathNode[Math.Max(128, nodes.Length * 2)];  
  
                // 将旧的nodes数组中的数据复制到新的newNodes数组中  
                nodes.CopyTo(newNodes, 0);  
  
                // 初始化newNodes数组中新增的部分  
                // 这样做很重要，我们不应该留下NULL的入口然后延迟初始化它们  
                // 通过一次性分配所有PathNode实例，我们更有可能将它们分配在内存中的相邻位置  
                // （大多数系统使用某种形式的bump-allocator），这可以提高缓存局部性并减少错误的共享  
                // （如果我们将不同线程的PathNodes分配在相邻位置，就会发生这种情况）  
                // 据分析，这样做可以提高大约4%的总体路径查找性能  
                // Initialize all PathNode instances at once  
                for (int i = nodes.Length; i < newNodes.Length; i++)  
                {  
                    newNodes[i] = new PathNode();  
                }  
  
                // 更新nodes数组引用，使其指向新的newNodes数组  
                nodes = newNodes;  
            }  
  
            // 将传入的node赋值给nodes数组中对应索引位置的node属性  
            nodes[ind].node = node;  
        }
        
    }
}