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

namespace Plugins.JNGame.Sync.Frame.AStar
{
    //A* 寻路
    public class JNAStarPath
    {
        
        /// <summary>  
        /// 存储所有等待计算的路径并计算它们  
        /// </summary>  
        public PathProcessor pathProcessor;
        
        /// <summary>
        /// 导航数据
        /// </summary>
        public AStarData data;
        
        /// 网格数据
        public NavGraph[] graphs => data.graphs.ToArray();
        
        /// <summary>保存所有已完成的路径，等待返回至请求它们的地方</summary>
        internal readonly PathReturnQueue pathReturnQueue;

        /// <summary>  
        /// 处理导航网格的切割（Navmesh cuts）。  
        /// 参见：<see cref="Pathfinding.NavmeshCut"/>  
        /// </summary>  
        public NavmeshUpdates navmeshUpdates;
        
        // 图形更新处理器  
        GraphUpdateProcessor graphUpdates;
        
        /// <summary>  
        /// 在搜索每个路径之前调用。当使用多线程时要小心，因为这将从另一个线程调用。  
        /// </summary>  
        public OnPathDelegate OnPathPreSearch;
        
        /// <summary>  
        /// 在搜索每个路径之后调用。当使用多线程时要小心，因为这将从不同的线程调用。  
        /// </summary>  
        public static OnPathDelegate OnPathPostSearch;
        
        /// <summary>  
        /// 在开始扫描之前调用  
        /// </summary>
        /// 
        public OnScanDelegate OnPreScan;
        
        /// <summary>  
        /// 扫描后调用。这会在应用链接、洪水填充图形和其他后期处理之前被调用。  
        /// </summary>  
        public static OnScanDelegate OnPostScan;
        
        /// <summary>  
        /// 存储一个层次化图以加速某些查询，例如判断两个节点之间是否存在路径  
        /// </summary>  
        internal readonly HierarchicalGraph hierarchicalGraph = new HierarchicalGraph();

        /// <summary>  
        /// 返回一个新的全局节点索引。  
        /// 警告：此方法不应直接调用。它仅由GraphNode构造函数使用。  
        /// </summary>  
        internal int GetNewNodeIndex() {  
            // 调用pathProcessor对象的GetNewNodeIndex方法，获取一个新的全局节点索引  
            return pathProcessor.GetNewNodeIndex();  
        }
        

        public JNAStarPath(AStarData data)
        {
            
            //寻路数据
            this.data = data;
            pathReturnQueue = new PathReturnQueue(this);;
            navmeshUpdates = new NavmeshUpdates(this);
            graphUpdates = new GraphUpdateProcessor(this);

            //初始化
            Init();

        }

        /// <summary>  
        /// 检查是否有任何工作项需要执行，  
        /// 如果不使用多线程（因为计算会在其他线程中进行），  
        /// 则运行一段时间的路径查找，  
        /// 然后将计算出的路径返回给请求它们的脚本。  
        ///  
        /// 参见：PerformBlockingActions  
        /// 参见：PathProcessor.TickNonMultithreaded  
        /// 参见：PathReturnQueue.ReturnPaths  
        /// </summary>  
        private void Update () {  
  
            // 更新导航网格  
            // navmeshUpdates.Update();  
  
            // // 如果不使用多线程，则计算路径  
            // pathProcessor.TickNonMultithreaded();  
            // // 返回计算出的路径  
            // pathReturnQueue.ReturnPaths(true);  

        }
        
        /// <summary>  
        /// 设置所有必要的变量并扫描图形。  
        /// 调用Initialize，启动ReturnPaths协程并扫描所有图形。  
        /// 如果使用多线程，则启动线程  
        /// 参见：<see cref="OnAwakeSettings"/>  
        /// </summary>  
        protected void Init () {   

            // // 禁用GUILayout以提高性能，因为它在OnGUI调用中未使用  
            // useGUILayout = false;  
  
            // // 确保在扫描之前已经启用了所有图形修饰符（以避免脚本执行顺序问题）  
            // GraphModifier.FindAllModifiers();  
            // RelevantGraphSurface.FindAllGraphSurfaces();  
  
            // 初始化路径处理器  
            InitializePathProcessor();  
            // // 配置内部引用  
            // ConfigureReferencesInternal();  
            // 初始化A*数据  
            InitializeAstarData();  
  
            // // 刷新工作项，这些工作项可能在InitializeAstarData中用于加载图形数据  
            // FlushWorkItems();  
            //
            // // 标记euclideanEmbedding为需要更新  
            // euclideanEmbedding.dirty = true;  
            //
            // 启用导航网格更新  
            navmeshUpdates.OnEnable();  
            
            // 如果在启动时扫描且缓存未启用或缓存文件为空，则执行扫描(因为没有缓存所以直接扫描)
            Scan();
        }
        
        /// <summary>  
        /// 扫描所有指定的图。  
        ///  
        /// 调用此方法将重新计算所有指定的图，或者如果graphsToScan参数为null，则重新计算所有图。  
        /// 此方法的速度相对较慢（当然取决于图的类型和复杂性），因此建议在可能的情况下使用更小的图更新。 var graphToScan = AstarPath.active.data.gridGraph;  
        /// AstarPath.active.Scan(graphToScan);  
        ///  
        /// // 仅重新计算第一个和第三个图  
        /// var graphsToScan = new [] { AstarPath.active.data.graphs[0], AstarPath.active.data.graphs[2] };  
        /// AstarPath.active.Scan(graphsToScan);  
        /// </code>  
        ///  
        /// 请参阅：graph-updates（请在线文档中查看工作链接）  
        /// 请参阅：ScanAsync  
        /// </summary>  
        /// <param name="graphsToScan">要扫描的图。如果此参数为null，则将扫描所有图</param>  
        public void Scan (NavGraph[] graphsToScan = null) {
            
            Debug.Log("初始化导航图");
            if (graphsToScan == null) graphsToScan = graphs;

            if (graphsToScan == null || graphsToScan.Length <= 0)
            {
                Debug.LogError("初始化失败 没有导航图");
                return;
            }
            
            //开始扫描
            if (OnPreScan != null) {
                OnPreScan(this);
            }
            Physics2D.SyncTransforms();
            // 销毁之前的节点  
            for (int i = 0; i < graphsToScan.Length; i++) {  
                // 遍历graphsToScan数组中的每一个元素  
                if (graphsToScan[i] != null) {  
                    // 如果当前元素不为null，即存在图形对象  
                    ((IGraphInternals)graphsToScan[i]).DestroyAllNodes();  
                    // 将当前图形对象强制转换为IGraphInternals接口，并调用其DestroyAllNodes方法，销毁所有节点  
                }  
            }
            
            // 遍历所有的图并逐个进行扫描  
            for (int i = 0; i < graphsToScan.Length; i++) {  
                // 跳过为null的图  
                if (graphsToScan[i] == null) continue;
                NavGraph graph = graphsToScan[i];

                //直接扫描图形
                ((IGraphInternals)graph).ScanInternal();

                // foreach (var p in ((IGraphInternals)graph).ScanInternal()) {
                //     yield return p.MapTo(0, 0.95f);
                // }

                // // 仅用于进度信息  
                // // 这个图将从minp推进到maxp的进度条  
                // float minp = Mathf.Lerp(0.1F, 0.8F, (float)(i)/(graphsToScan.Length));  
                // float maxp = Mathf.Lerp(0.1F, 0.8F, (float)(i+0.95F)/(graphsToScan.Length));  

                // 进度描述的前缀  
                // var progressDescriptionPrefix = "Scanning graph " + (i+1) + " of " + graphsToScan.Length + " - ";  

                // // 类似于foreach循环，但由于异常处理（在try-except块中不能使用yield），所以稍微复杂一些  
                // var coroutine = ScanGraph(graphsToScan[i]).GetEnumerator();  
                //
                // while (true) {  
                //     try {  
                //         if (!coroutine.MoveNext()) break;  
                //     } catch {  
                //         // 如果在扫描过程中发生异常，则停止扫描，解锁图结构，并释放图更新的锁  
                //         isScanning = false;  
                //         data.UnlockGraphStructure();  
                //         graphUpdateLock.Release();  
                //         throw; // 重新抛出捕获的异常  
                //     }  
                //     // 返回当前扫描的进度和描述  
                //     yield return coroutine.Current.MapTo(minp, maxp, progressDescriptionPrefix);  
                // } 

            }

        }
        
        /// <summary>  
        /// 初始化AstarData类。  
        /// 搜索图类型，对<see cref="data"/>和所有图调用Awake方法。  
        ///  
        /// 请参阅：AstarData.FindGraphTypes  
        /// </summary>  
        void InitializeAstarData () {
            
            // // // 调用AstarData类的FindGraphTypes方法，该方法负责搜索并识别可用的图类型。  
            // // data.FindGraphTypes();  
            //
            // // 调用AstarData类的Awake方法，该方法可能用于唤醒或初始化类的内部状态或资源。  
            // data.Awake();  
            //
            // // 调用AstarData类的UpdateShortcuts方法，该方法可能用于更新或创建图的快捷方式或优化路径。  
            // data.UpdateShortcuts();  
        }
        
        /// <summary>  
        /// 初始化 <see cref="pathProcessor"/> 字段  
        /// </summary>  
        void InitializePathProcessor() {  
  
            // // 确保线程数至少为1  
            // int numProcessors = Mathf.Max(numThreads, 1);  
            // // 判断是否使用多线程  
            // bool multithreaded = numThreads > 0;  
  
            // 创建 PathProcessor 实例，并传入相关的参数  
            pathProcessor = new PathProcessor(this, pathReturnQueue);  
  
            // 为 PathProcessor 的 OnPathPreSearch 事件添加事件处理器  
            pathProcessor.OnPathPreSearch += path => {  
                var tmp = OnPathPreSearch;  
                if (tmp != null) tmp(path);  
            };  
  
            // 为 PathProcessor 的 OnPathPostSearch 事件添加事件处理器  
            pathProcessor.OnPathPostSearch += path => {  
                LogPathResults(path);  
                var tmp = OnPathPostSearch;  
                if (tmp != null) tmp(path);  
            };  
  
            // // 为 PathProcessor 的 OnQueueUnblocked 事件添加事件处理器  
            // // 当路径队列被解锁时，此事件将被触发  
            // pathProcessor.OnQueueUnblocked += () => {  
            //     if (euclideanEmbedding.dirty) {  
            //         euclideanEmbedding.RecalculateCosts();  
            //     }  
            // };  
            //
            // // 如果使用多线程  
            // if (multithreaded) {  
            //     // 启用图形更新的多线程支持  
            //     graphUpdates.EnableMultithreading();  
            // }  
        }
        
        /// <summary>  
        /// 将路径结果打印到日志中。打印的内容可以通过 <see cref="logPathResults"/> 进行控制。  
        /// 参见： <see cref="logPathResults"/>  
        /// 参见： PathLog  
        /// 参见： Pathfinding.Path.DebugString  
        /// </summary>  
        private void LogPathResults(JNPath path) {  
            // // 如果 logPathResults 不为 PathLog.None，并且路径存在错误或者 logPathResults 不只为错误打印  
            // if (logPathResults != PathLog.None && (path.error || logPathResults != PathLog.OnlyErrors)) {  
            //     // 获取路径的调试字符串，具体取决于 logPathResults 的值  
            //     string debug = (path as IPathInternals).DebugString(logPathResults);  
            //
            //     // 如果 logPathResults 为 PathLog.InGame  
            //     if (logPathResults == PathLog.InGame) {  
            //         // 将调试字符串赋值给 inGameDebugPath  
            //         inGameDebugPath = debug;  
            //         // 如果路径存在错误  
            //     } else if (path.error) {  
            //         // 打印警告级别的调试信息  
            //         Debug.LogWarning(debug);  
            //         // 如果路径没有错误  
            //     } else {  
            //         // 打印普通的调试信息  
            //         Debug.Log(debug);  
            //     }  
            // }  
            if (path.error)
                Debug.Log(path.errorLog);
        }
        
        /// <summary>  
        /// 将路径添加到队列中，以便尽快进行计算。  
        /// 当路径计算完成时，将调用在构造路径时指定的回调。  
        /// 通常，你应该使用Seeker组件而不是直接调用此函数。  
        /// </summary>  
        /// <param name="path">应该入队的路径。</param>  
        /// <param name="pushToFront">如果为true，则将路径推送到队列的前面，绕过所有等待的路径，使其成为下一个要计算的路径。  
        /// 如果你有一个想要优先于其他所有路径的路径，这可能会很有用。但是，请注意不要过度使用它。  
        /// 如果经常将太多路径放在队列的前面，这可能会导致正常路径需要等待很长时间才能进行计算。</param>  
        public void StartPath(JNPath path, bool pushToFront = false)  
        {  
  
            // 如果路径的状态不是已创建，则抛出异常  
            if (path.PipelineState != PathState.Created)  
            {  
                Debug.LogError("路径的状态无效。预期值为 " + PathState.Created + "，找到的值为 " + path.PipelineState + "\n" +  
                                           "确保你没有请求同一个路径两次");  
            }  
  
            // 如果没有在场景中的图形，则记录错误并使路径失败  
            if (this.graphs == null || this.graphs.Length == 0)  
            {  
                Debug.LogError("场景中没有图形");  
                return;  
            }  
  
            // 声明该路径属于AStarPath对象  
            path.Claim(this);  
  
            // 将路径的状态从已创建更改为在路径队列中  
            ((IPathInternals)path).AdvanceState(PathState.PathQueue);  
  
            // 如果指定了pushToFront参数为true，则将路径推送到队列的前面  
            // 否则，将路径推送到队列的末尾  
            if (pushToFront){  
                this.pathProcessor.queue.AddFirst(path);  
            }else{  
                this.pathProcessor.queue.AddLast(path);  
            }  
            
            // // 在播放模式之外，所有路径请求都是同步的
            // if (!Application.isPlaying) {
            //     BlockUntilCalculated(path);
            // }
        }
        
        /// <summary>  
        /// 初始化节点的临时路径数据。  
        /// 警告：此方法不应直接调用。它仅由GraphNode构造函数使用。  
        /// </summary>  
        /// <param name="node">要初始化路径数据的节点</param>  
        internal void InitializeNode(GraphNode node) {  
            // 调用pathProcessor的InitializeNode方法，将node作为参数传递，  
            // 以初始化节点的临时路径数据  
            pathProcessor.InitializeNode(node);  
        }
        
    }
}