using System.Collections.Generic;
using Plugins.JNGame.Sync.Frame.AStar;
using Plugins.JNGame.Sync.Frame.AStar.Modifier;
using UnityEngine;
using UnityEngine.Profiling;

namespace Game.Plugins.JNGame.Sync.Frame.AstarPath.AI
{
    public class JNAISeeker
    {

        public JNAISeeker(JNAStarPath Atar)
        {
            this.Atar = Atar;
        }

        //A*系统
        public JNAStarPath Atar;
        
        /// <summary>  
        /// 使用Gizmos绘制最后计算出的路径。  
        /// 路径将以绿色显示。  
        ///  
        /// 参见：OnDrawGizmos  
        /// </summary>  
        public bool drawGizmos = true;
        
        /// <summary>  
        /// 启用使用Gizmos绘制非后处理路径。  
        /// 路径将以橙色显示。  
        ///  
        /// 要求 <see cref="drawGizmos"/> 为 true。  
        ///  
        /// 这将显示在应用任何后处理（如平滑）之前的路径。  
        ///  
        /// 参见：drawGizmos  
        /// 参见：OnDrawGizmos  
        /// </summary>  
        public bool detailedGizmos;
        
        // /// <summary>路径修改器，用于微调路径的起点和终点</summary>  
        // [HideInInspector] // 在Inspector面板中隐藏此属性  
        // public StartEndModifier startEndModifier = new StartEndModifier(); // 创建一个StartEndModifier实例，用于修改路径的起点和终点  
  
        /// <summary>  
        /// Seeker可以遍历的标签。  
        ///  
        /// 注意：此字段是一个位掩码。  
        /// 请参见：位掩码（查看在线文档以获取工作链接）  
        /// </summary>  
        public int traversableTags = -1; // 表示Seeker可以遍历的所有标签。初始值为-1，可能意味着没有限制。  
  
        /// <summary>  
        /// 每个标签的惩罚值。  
        /// 默认标签（即标签0）将有一个惩罚值，该值由tagPenalties[0]给出。  
        /// 这些值应该是正数，因为A*算法无法处理负惩罚值。  
        ///  
        /// 注意：此数组的长度应始终为32，否则系统将忽略它。  
        ///  
        /// 请参见：Pathfinding.Path.tagPenalties
        /// </summary>  
        public int[] tagPenalties = new int[32]; // 创建一个长度为32的整数数组，用于存储每个标签的惩罚值
        
        /// <summary>  
        /// 自定义遍历提供者，用于计算哪些节点可遍历以及它们的惩罚值。  
        ///  
        /// 这可以用于覆盖内置的寻路逻辑。（请在线文档中查看工作链接）  
        /// </summary>  
        public ITraversalProvider traversalProvider;
        
        /// <summary>  
        /// 该搜索器可以使用的图形。  
        /// 此字段决定了在搜索路径的起始和结束节点时应该考虑哪些图形。  
        /// 在许多情况下都很有用，例如，如果你想为小型单位创建一个图形，为大型单位创建另一个图形。  
        ///   
        /// 这是一个位掩码，因此，例如，如果你只想让代理仅使用图形索引3，你可以这样设置：  
        /// <code> seeker.graphMask = 1 << 3; </code>  
        ///   
        /// 请参阅：位掩码（查看在线文档以获取工作链接）  
        ///   
        /// 请注意，此字段仅存储允许的图形索引。这意味着如果图形的顺序发生变化，  
        /// 则此掩码可能不再正确。 GraphMask mask1 = GraphMask.FromGraphName("My Grid Graph");  
        /// GraphMask mask2 = GraphMask.FromGraphName("My Other Grid Graph");  
        ///   
        /// NNConstraint nn = NNConstraint.Walkable;  
        ///   
        /// nn.graphMask = mask1 | mask2;  
        ///   
        /// // 查找与somePoint最近的节点，该节点位于'My Grid Graph'或'My Other Grid Graph'中  
        /// var info = AstarPath.active.GetNearest(somePoint, nn);  
        /// </code>  
        ///   
        /// <see cref="StartPath"/>方法的一些重载接受一个graphMask参数。如果使用这些重载，  
        /// 它们将覆盖该路径请求的图形掩码。  
        ///   
        /// [打开在线文档以查看图像]  
        ///   
        /// 请参阅：多种代理类型（查看在线文档以获取工作链接）  
        /// </summary>  
        public GraphMask graphMask = GraphMask.everything; // 默认情况下，允许所有图形
        
        /// <summary>当前路径</summary>  
        protected JNPath path;
        
        /// <summary>  
        /// 上次查询的路径的ID  
        /// </summary>  
        protected uint lastPathID;
        
        /// <summary>  
        /// 仅对当前路径调用的临时回调函数。该值由StartPath函数设置  
        /// </summary>  
        private OnPathDelegate tmpPathCallback;
        
        /// <summary>  
        /// 缓存的委托，用于避免每次启动路径时都分配一个新的委托实例  
        /// </summary>  
        private readonly OnPathDelegate onPathDelegate;  
  
        /// <summary>  
        /// 缓存的委托，用于避免每次启动部分路径时都分配一个新的委托实例  
        /// </summary>  
        private readonly OnPathDelegate onPartialPathDelegate;
        
        /// <summary>在路径查找开始之前调用</summary>  
        public OnPathDelegate preProcessPath;
        
        /// <summary>  
        /// 所有修饰符的内部列表  
        /// </summary>  
        readonly List<IPathModifier> modifiers = new List<IPathModifier>();
        
        /// <summary>  
        /// 在路径计算完成后、修饰符执行之前调用。  
        /// </summary>  
        public OnPathDelegate postProcessPath;
        
        
        public enum ModifierPass {  
            PreProcess,  
            PostProcess = 2,  
        }

        
        /// <summary>  
        /// 调用此函数开始计算路径。  
        ///  
        /// 当路径计算完成（可能是在未来几个帧中）时，将调用回调。  
        /// 如果路径被取消（例如，在之前的路径完成之前请求新的路径），则不会调用回调。  
        /// </summary>  
        /// <param name="start">路径的起点</param>  
        /// <param name="end">路径的终点</param>  
        /// <param name="callback">路径计算完成后要调用的函数</param>  
        public JNPath StartPath(Vector3 start, Vector3 end, OnPathDelegate callback) {  
            return StartPath(JNABPath.Construct(start, end, null), callback);  
        }
        
        /// <summary>  
        /// 调用此函数以开始计算路径。  
        ///  
        /// 当路径计算完成后（可能是在未来的多个帧中），将调用回调函数。  
        /// 如果在新的路径请求计算完成之前开始了一个新的路径请求，则不会调用回调函数。  
        ///  
        /// 版本：从3.8.3版本开始，如果使用了MultiTargetPath，则此方法将正常工作。  
        /// 它的行为现在与StartMultiTargetPath(MultiTargetPath)方法完全相同。  
        ///  
        /// 版本：从4.1.x版本开始，除非明确将其作为参数传递（请参阅此方法的其他重载），否则此方法将不再覆盖路径上的graphMask。  
        /// </summary>  
        /// <param name="p">要开始计算的路径</param>  
        /// <param name="callback">路径计算完成时要调用的函数</param>  
        public JNABPath StartPath(JNABPath p, OnPathDelegate callback = null) {  
            // 仅当用户未从默认值更改它时才设置图掩码。  
            // 这不是完美的，因为用户可能希望它精确为-1，  
            // 但是这是我能做的最佳检测。  
            // 非默认检查主要是为了兼容性原因，以避免破坏人们现有的代码。  
            // 应使用具有显式graphMask字段的StartPath重载来设置graphMask。  
            if (p.nnConstraint.graphMask == -1)  
            {  
                p.nnConstraint.graphMask = graphMask;  
            }  
            StartPathInternal(p, callback);  
            return p;  
        }
        
        /// <summary>  
        /// 在路径上运行修改器  
        /// </summary>  
        /// <param name="pass">修改器传递类型（预处理或后处理）</param>  
        /// <param name="path">要修改的路径</param>  
        public void RunModifiers(ModifierPass pass, JNPath path) {  
            // 如果传递类型是预处理  
            if (pass == ModifierPass.PreProcess) {  
                // 如果预处理路径委托存在，则调用它  
                if (preProcessPath != null) preProcessPath(path);  
  
                // 遍历所有修改器，并对其进行预处理  
                for (int i = 0; i < modifiers.Count; i++)   
                    modifiers[i].PreProcess(path);  
            }   
            // 如果传递类型是后处理  
            else if (pass == ModifierPass.PostProcess) {  
                // 开始性能分析样本，记录运行路径修改器的时间  
                Profiler.BeginSample("Running Path Modifiers");  
  
                // 如果后处理路径委托存在，则调用它  
                if (postProcessPath != null) postProcessPath(path);  
  
                // 遍历所有修改器，并应用后处理  
                for (int i = 0; i < modifiers.Count; i++)   
                    modifiers[i].Apply(path);  
  
                // 结束性能分析样本  
                Profiler.EndSample();  
            }  
        }
        
        /// <summary>内部方法，用于启动一个路径并将其标记为当前活动路径</summary>  
        void StartPathInternal (JNABPath p, OnPathDelegate callback) {  
            var mtp = p as JNMultiTargetPath;  
            if (mtp != null) {  
                // // TODO: Allocation, cache  
                // var callbacks = new OnPathDelegate[mtp.targetPoints.Length];  
                //
                // for (int i = 0; i < callbacks.Length; i++) {  
                //     callbacks[i] = onPartialPathDelegate;  
                // }  
                //
                // mtp.callbacks = callbacks;  
                // p.callback += OnMultiPathComplete;
            } else {  
                p.callback += onPathDelegate;  
            }
  
            p.enabledTags = traversableTags;  
            p.tagPenalties = tagPenalties;  
            if (traversalProvider != null) p.traversalProvider = traversalProvider;  
  
            // 如果之前请求的路径尚未处理，并且确保它没有被回收并在其他地方使用，则取消它  
            if (path != null && path.PipelineState <= PathState.Processing && path.CompleteState != PathCompleteState.Error && lastPathID == path.pathID) {  
                // path.FailWithError("Canceled path because a new one was requested.\n" +  
                //                    "This happens when a new path is requested from the seeker when one was already being calculated.\n" +  
                //                    "For example if a unit got a new order, you might request a new path directly instead of waiting for the now" +  
                //                    " invalid path to be calculated. Which is probably what you want.\n" +  
                //                    "If you are getting this a lot, you might want to consider how you are scheduling path requests.");  
                // 取消的路径将不会发送回调  
            }  
  
            // 将p设置为活动路径  
            path = p;  
            tmpPathCallback = callback;  
  
            // 保存路径ID，以便我们可以确保如果取消路径（如上所述），它尚未被回收  
            lastPathID = path.pathID;  
  
            // 预处理路径  
            RunModifiers(ModifierPass.PreProcess, path);  
  
            // 将请求发送到寻路器  
            Atar.StartPath(path);
        }
        
    }
}