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

namespace Plugins.JNGame.Sync.Frame.AStar
{
    /// <summary>  
    /// 基础路径类，用于从A点找到到B点的最短路径。  
    ///  
    /// 这是最基本的路径对象，它将尝试找到两点之间的最短路径。  
    /// 许多其他路径类型都继承自这个类。  
    /// 请参见：Seeker.StartPath  
    /// 请参见：calling-pathfinding（请在线文档查看有效链接）  
    /// 请参见：getstarted（请在线文档查看有效链接）  
    /// </summary>
    public class JNABPath : JNPath
    {
        
        /// <summary>  
        /// 路径的开始节点  
        /// </summary>  
        public GraphNode startNode;
        
        /// <summary>  
        /// 路径的结束节点  
        /// </summary>  
        public GraphNode endNode;

        /// <summary>  
        /// 与路径请求中完全相同的起点  
        /// </summary>  
        public Vector3 originalStartPoint;
        
        /// <summary>  
        /// 路径的起点。  
        /// 这是 <see cref="startNode"/> 上最接近 <see cref="originalStartPoint"/> 的点。  
        /// </summary>  
        public Vector3 startPoint;
        
        /// <summary>  
        /// 与路径请求中完全相同的终点  
        /// </summary>  
        public Vector3 originalEndPoint;
        
        /// <summary>  
        /// 路径的终点。  
        /// 这是 <see cref="endNode"/> 上最接近 <see cref="originalEndPoint"/> 的点。  
        /// </summary>  
        public Vector3 endPoint;
        
        /// <summary>整数坐标下的起点</summary>  
        public Int3 startIntPoint;
        
        /// <summary>  
        /// 为路径请求提供额外的遍历信息。  
        /// 参见：traversal_provider（请在线文档中查看以获取有效链接）。  
        /// </summary>  
        public ITraversalProvider traversalProvider;
        
        /// <summary>  
        /// 如果目标节点无法到达，则计算部分路径。  
        /// 如果目标节点无法到达，将选择最接近（由启发式算法确定）的节点作为目标节点，  
        /// 并返回部分路径。  
        /// 这只在使用启发式算法时有效（这是默认设置）。  
        /// 如果找到了部分路径，则将CompleteState设置为Partial。  
        /// 注意：其他路径类型不需要遵守此设置。  
        ///  
        /// <see cref="endNode"/> 和 <see cref="endPoint"/> 将被修改并设置为最终最接近目标的节点。  
        ///  
        /// 警告：如果您有一个大型图表，使用此功能可能会使路径计算明显变慢。原因是，  
        /// 当目标节点无法到达时，路径必须搜索它可以到达的每一个其他节点，  
        /// 以确定哪一个最接近。这可能是昂贵的，这就是为什么此选项默认被禁用。  
        /// </summary>  
        public bool calculatePartial;
        
        /// <summary>  
        /// 部分路径的当前最佳目标。  
        /// 这是具有最低H分数的节点。  
        /// H分数通常是根据启发式算法计算得出的，用于估计从当前节点到目标节点的代价。  
        /// 在计算部分路径时，算法会不断寻找具有最低H分数的节点作为下一个目标，  
        /// 以确保所选的路径是当前已知的最佳路径。  
        /// 当目标节点不可达时，此属性将存储最接近目标节点的节点，并用于构建部分路径。  
        /// </summary>  
        protected PathNode partialBestTarget;
        
        /// <summary>  
        /// 用于路径查找算法此路径的总成本。  
        ///  
        /// 成本受路径长度以及节点上的任何标签或惩罚的影响。  
        /// 默认情况下，移动1个世界单位的成本是<see cref="Int3.Precision"/>。  
        ///  
        /// 如果路径查找失败，成本将被设置为零。  
        ///  
        /// 请参阅：标签（请在线文档中查看相关链接）  
        /// </summary>  
        public uint cost; // 这是一个无符号整型变量，用于存储路径查找算法中此路径的总成本
        
        /// <summary>  
        /// 使用起始点和终点构造一个路径。  
        /// 当路径计算完成后，将调用指定的委托。  
        /// 不要将其与 Seeker 的回调混淆，因为它们在不同的时间点发送。  
        /// 如果你使用 Seeker 来启动路径，你可以将回调设置为 null。  
        ///  
        /// 返回值：构造的路径对象  
        /// </summary>  
        public static JNABPath Construct(Vector3 start, Vector3 end, OnPathDelegate callback = null) {  
            // // 从路径池中获取一个ABPath对象。路径池是一种优化技术，用于重用和减少内存分配。  
            // var p = PathPool.GetPath<ABPath>();  
            //前期直接 New 因为不确定对象池是否可以帧同步
            var p = new JNABPath();
  
            // 设置路径的起始点、终点和回调委托。  
            p.Setup(start, end, callback);  
  
            // 返回构造好的路径对象。  
            return p;  
        }
        
        protected void Setup (Vector3 start, Vector3 end, OnPathDelegate callbackDelegate) {
            callback = callbackDelegate;
            UpdateStartEnd(start, end);
        }
        
        /// <summary>  
        /// 设置起始点和终点。  
        /// 设置 <see cref="originalStartPoint"/>, <see cref="originalEndPoint"/>, <see cref="startPoint"/>, <see cref="endPoint"/>, <see cref="startIntPoint"/> 和 <see cref="hTarget"/>（至终点）  
        /// </summary>  
        protected void UpdateStartEnd(Vector3 start, Vector3 end) {  
            // 将传入的起始点赋值给 originalStartPoint 和 startPoint 变量  
            originalStartPoint = start;  
            startPoint = start;  
  
            // 将传入的终点赋值给 originalEndPoint, endPoint 和 hTarget 变量  
            originalEndPoint = end;  
            endPoint = end;  
            hTarget = (Int3)end; // 注意这里将 Vector3 类型的 end 强制转换为 Int3 类型  
  
            // 将起始点 start 转换为 Int3 类型并赋值给 startIntPoint 变量  
            startIntPoint = (Int3)start;  
        }
        
        /// <summary>  
        /// 完成部分路径计算，并将结果存储在路径处理器中。  
        /// </summary>  
        /// <param name="node">部分路径的最后一个节点。</param>  
        void CompletePartial(PathNode node) {  
            // 我们将改变结束节点，因此需要清理之前的结束节点，以避免留下过时数据。  
            var pathEndNode = pathHandler.GetPathNode(endNode);  
            pathEndNode.flag1 = false;  // 清除结束节点的flag1标志  
            pathEndNode.flag2 = false;  // 清除结束节点的flag2标志  
  
            // 设置完成状态为部分完成  
            CompleteState = PathCompleteState.Partial;  
  
            // 更新结束节点为传入的节点  
            endNode = node.node;  
  
            // 计算结束点，即传入节点上距离原始结束点最近的点  
            endPoint = endNode.ClosestPointOnNode(originalEndPoint);  
  
            // 更新路径成本为结束节点的G值（从开始节点到结束节点的总成本）  
            cost = pathEndNode.G;  
  
            // 追踪路径，可能是为了调试或可视化目的  
            Trace(node);  
        }
        
        /// <summary>  
        /// 从终点节点回溯到起点节点，追踪计算出的路径。  
        /// 这将构建一个包含此路径经过的节点的数组(<see cref="path"/>)，并将<see cref="vectorPath"/>数组设置为<see cref="path"/>数组的位置。  
        /// 假设<see cref="vectorPath"/>和<see cref="path"/>为空且不为null（对于正确初始化的路径来说，这是正确的）。  
        /// </summary>  
        protected virtual void Trace(PathNode from) {  
            // 当前正在处理的节点  
            PathNode c = from;  
            int count = 0;  
  
            // 从终点节点回溯到起点节点，直到c为null  
            while (c != null) {  
                c = c.parent; // 移动到父节点  
                count++; // 计算路径中的节点数量  
          
                // 防止无限循环，如果节点数量超过16384，则输出警告并退出循环  
                if (count > 16384) {  
                    Debug.LogWarning("Infinite loop? >16384 node path. Remove this message if you really have that long paths (Path.cs, Trace method)");  
                    break;  
                }  
            }  
  
            // 确保列表的容量足够  
            if (path.Capacity < count) path.Capacity = count; // 调整path列表的容量  
            if (vectorPath.Capacity < count) vectorPath.Capacity = count; // 调整vectorPath列表的容量  
  
            c = from; // 重置c为起点节点  
  
            // 从起点节点到终点节点遍历，并将节点添加到path列表中  
            for (int i = 0; i < count; i++) {  
                path.Add(c.node); // 添加节点到path列表  
                c = c.parent; // 移动到父节点  
            }  
  
            // 反转path列表，因为我们是从终点回溯到起点的，所以节点顺序是反的  
            int half = count / 2;  
            for (int i = 0; i < half; i++) {  
                var tmp = path[i]; // 临时变量用于交换  
                path[i] = path[count - i - 1]; // 交换节点位置  
                path[count - i - 1] = tmp;  
            }  
  
            // 将path列表中的节点位置添加到vectorPath列表中  
            for (int i = 0; i < count; i++) {  
                vectorPath.Add((Vector3)path[i].position); // 强制转换节点位置为Vector3并添加到vectorPath列表  
            }  
        }
        
        /// <summary>  
        /// 计算路径直到完成或直到时间超过目标tick。  
        /// 通常，只有在每500个节点并且时间超过目标tick时才会进行检查。  
        /// 时间/Ticks是从System.DateTime.UtcNow.Ticks获取的 帧同步不能使用本地时间 所以默认不能超过2000节点。  
        ///   
        /// 对于标准路径（Pathfinding.ABPath）该函数的基本概述。  
        /// </summary>  
        protected override void CalculateStep()  
        {  
            // 计数器，用于每500个节点检查一次时间  
            int counter = 0;  
          
            // 继续搜索，直到遇到错误或找到目标  
            while (CompleteState == PathCompleteState.NotCalculated)  
            {  
                // 增加已搜索节点的数量  
                searchedNodes++;  
          
                // 如果没有要搜索的节点了  
                if (pathHandler.heap.isEmpty)  
                {  
                    // 如果需要计算部分路径，并且有一个部分最佳目标  
                    if (calculatePartial && partialBestTarget != null)  
                    {  
                        // 完成部分路径的计算  
                        CompletePartial(partialBestTarget);  
                    }  
                    else  
                    {  
                        // 报告错误，所有可达节点都已搜索，但找不到目标  
                        FailWithError("Searched all reachable nodes, but could not find target. This can happen if you have nodes with a different tag blocking the way to the goal. You can enable path.calculatePartial to handle that case workaround (though this comes with a performance cost).");  
                    }  
                    // 退出方法  
                    return;  
                }  
          
                // 选择F分数最低的节点，并从开放列表中移除它  
                currentR = pathHandler.heap.Remove();  
          
                // 每500个节点检查一次时间，通常大约是每0.5毫秒  
                if (counter > 2000)  
                {  
                    // // 如果当前时间已经超过了目标tick  
                    // if (System.DateTime.UtcNow.Ticks >= targetTick)  
                    // {  
                    //     // 返回而不是yield，一个单独的函数处理yield（CalculatePaths）  
                    //     return;  
                    // }  
                    // // 重置计数器  
                    // counter = 0;
                    
                    return;
          
                    // // 主要用于开发阶段  
                    // if (searchedNodes > 1000000)  
                    // {  
                    //     // 抛出异常，可能是无限循环，搜索了超过1,000,000个节点  
                    //     throw new Exception("Probable infinite loop. Over 1,000,000 nodes searched");  
                    // }  
                }  
          
                // 增加计数器  
                counter++;  
            }  
          
            // 如果路径计算完成  
            if (CompleteState == PathCompleteState.Complete)  
            {  
                // 记录或输出当前节点信息  
                Trace(currentR);  
            }  
            // 如果需要计算部分路径，并且有一个部分最佳目标  
            else if (calculatePartial && partialBestTarget != null)  
            {  
                // 完成部分路径的计算  
                CompletePartial(partialBestTarget);  
            }  
        }
        
    }
}