using System;
using System.Collections.Generic;
using Game.Plugins.JNGame.Sync.Frame.AStar.Util;
using Plugins.JNGame.Sync.Frame.AStar.Entity;
using UnityEngine;
using NotImplementedException = System.NotImplementedException;
using Object = System.Object;

namespace Plugins.JNGame.Sync.Frame.AStar
{
    /// <summary>  
    /// 为路径请求提供额外的遍历信息。  
    /// 请在线文档中查看 traversal_provider（以获取有效的链接）。  
    /// </summary>  
    public interface ITraversalProvider {  
        // bool CanTraverse(JNAStarPath path, GraphNode node);  
        // uint GetTraversalCost(JNAStarPath path, GraphNode node);  
    }
    
    /// <summary>  
    /// 用于隐藏Path类的内部方法  
    /// </summary>  
    internal interface IPathInternals  
    {  
        // /// <summary>  
        // /// 获取路径处理器  
        // /// </summary>  
        // PathHandler PathHandler { get; }  
  
        /// <summary>  
        /// 获取或设置路径是否已被池化（即是否被复用）  
        /// </summary>  
        bool Pooled { get; set; }  
  
        /// <summary>  
        /// 将路径状态向前推进到指定的状态  
        /// </summary>  
        /// <param name="s">目标路径状态</param>  
        void AdvanceState(PathState s);  
  
        /// <summary>  
        /// 当路径被加入池化时调用  
        /// </summary>  
        void OnEnterPool();  
  
        /// <summary>  
        /// 重置路径的状态和属性  
        /// </summary>  
        void Reset();  
  
        /// <summary>  
        /// 将路径返回给路径池  
        /// </summary>  
        void ReturnPath();  
  
        /// <summary>  
        /// 为基础路径处理器做准备  
        /// </summary>  
        /// <param name="handler">路径处理器</param>  
        void PrepareBase(PathHandler handler);  
  
        /// <summary>  
        /// 准备路径以供使用  
        /// </summary>  
        void Prepare();  
  
        /// <summary>  
        /// 初始化路径  
        /// </summary>  
        void Initialize();  
  
        /// <summary>  
        /// 清理路径资源和状态  
        /// </summary>  
        void Cleanup();  
  
        /// <summary>  
        /// 根据目标时间戳计算路径的下一步  
        /// </summary>  
        void CalculateStep();  
  
        // /// <summary>  
        // /// 获取用于调试的路径日志字符串  
        // /// </summary>  
        // /// <param name="logMode">日志模式</param>  
        // /// <returns>调试字符串</returns>  
        // string DebugString(PathLog logMode);  
    }
    
    public abstract class JNPath : IPathInternals
    {
        /// <summary>  
        /// 此路径的ID。用于区分不同的路径  
        /// </summary>  
        public ushort pathID { get; private set; }

        /// <summary>  
        /// 用于指定如何搜索节点的约束条件  
        /// </summary>  
        public NNConstraint nnConstraint = PathNNConstraint.Walkable;
        
        /// <summary>  
        /// 当路径计算完成时调用的回调。  
        /// 这个回调通常被发送给 Seeker 组件，该组件对路径进行后处理，然后调用请求路径的脚本的回调。  
        /// </summary>  
        public OnPathDelegate callback;
        
        /// <summary>用于H分数计算的目标点。参见：Pathfinding.Node.H</summary>  
        protected Int3 hTarget;
        
        /// <summary>  
        /// 可遍历的图标签。  
        /// 这是一个位掩码，因此-1表示所有位都设置为可遍历的所有标签。); </code>  
        ///  
        /// 搜索器(Seeker)有一个弹出字段，您可以在其中设置要使用的标签。  
        /// 注意：如果您正在使用搜索器(Seeker)。在StartPath时，搜索器(Seeker)会将此值设置为检查器字段中设置的值。  
        /// 因此，如果您想通过脚本更改它，需要通过脚本更改搜索器(Seeker)的值，而不是设置此值。  
        ///  
        /// 参考：CanTraverse  
        /// </summary>  
        public int enabledTags = -1;
        
        /// <summary>  
        /// 由其他脚本设置的标签惩罚值  
        /// 参见：tagPenalties  
        /// </summary>  
        protected int[] manualTagPenalties;
        
        /// <summary>  
        /// 实际使用的标签惩罚值。  
        /// 如果 manualTagPenalties 为 null，那么这将是 ZeroTagPenalties。  
        /// 参见：tagPenalties  
        /// </summary>  
        protected int[] internalTagPenalties;
        
        /// <summary>用作默认标签惩罚值的零值列表</summary>  
        static readonly int[] ZeroTagPenalties = new int[32];
        
        /// <summary>  
        /// 返回路径查找管道中路径的状态  
        /// </summary>  
        public PathState PipelineState { get; private set; }
        
        /// <summary>  
        /// 支持<see cref="CompleteState"/>属性的后备字段  
        /// </summary>  
        protected PathCompleteState completeState;
        
        /// <summary>  
        /// 包含引用对象的此路径上的声明列表  
        /// </summary>  
        private List<Object> claimed = new();
        
        /// <summary>  
        /// 如果路径已经通过非静默调用被释放，则为True。  
        ///  
        /// 参见：Release  
        /// 参见：Claim  
        /// </summary>  
        private bool releasedNotSilent;
        
        /// <summary>  
        /// 如果路径失败，此属性为 true。  
        /// 参见：<see cref="errorLog"/>  
        /// 参见：这相当于检查 path.CompleteState == PathCompleteState.Error  
        /// </summary>  
        public bool error { get { return CompleteState == PathCompleteState.Error; } }
        
        /// <summary>  
        /// 存储（可能经过后处理）的路径作为 Vector3 列表。  
        ///  
        /// 该列表可能会被路径修饰器修改，与路径查找算法生成的原始路径相比，可能会更平滑或更简单。  
        ///  
        /// 参见：修饰器（请查阅在线文档以获取有效链接）  
        /// 参见：<see cref="path"/>  
        /// </summary>  
        public List<Vector3> vectorPath;
        
        /// <summary>计算此路径的线程的数据</summary>  
        protected PathHandler pathHandler;
        
        /// <summary>  
        /// 该路径已搜索的节点数量  
        /// </summary>  
        public int searchedNodes { get; protected set; }
        
        /// <summary>
        /// 记入错误信息
        /// See: <see cref="AstarPath.logPathResults"/>
        /// </summary>
        public string errorLog { get; private set; }
        
        /// <summary>  
        /// 以<see cref="GraphNode"/>列表的形式保存路径。  
        ///  
        /// 这些节点是路径查找算法计算得出的路径上经过的所有节点。  
        /// 这可能与后处理路径遍历的节点不同。  
        ///  
        /// 参见：<see cref="vectorPath"/>  
        /// </summary>  
        public List<GraphNode> path;
        
        /// <summary>  
        /// 当前正在处理的节点  
        /// </summary>  
        protected PathNode currentR;
        
        /// <summary>  
        /// 当路径完成时立即调用的回调函数。  
        /// 警告：这个函数可能在一个单独的线程中被调用。通常不建议使用此函数。  
        ///  
        /// 参见：callback  
        /// </summary>  
        public OnPathDelegate immediateCallback;
        
        /// <summary>  
        /// 路径的当前状态。  
        /// Bug：当前可能在路径完全计算之前就将此属性设置为Complete。特别是，vectorPath和path列表可能尚未完全构建。  
        /// 在使用多线程时，这可能导致竞态条件。尽量避免使用此方法检查路径是否已计算完成，而应使用<see cref="IsDone"/>。  
        /// </summary>  
        public PathCompleteState CompleteState {  
            get { return completeState; }  
            protected set {  
                // // 使用锁定来避免多线程竞态条件，  
                // // 在这种情况下，主线程将错误状态设置为取消路径，然后寻路线程将路径标记为完成，  
                // // 这将替换错误状态（如果没有使用锁定和检查）。  
                // lock (stateLock) {  
                    // 一旦路径进入错误状态，就不能将其设置为任何其他状态  
                    if (completeState != PathCompleteState.Error) completeState = value;  
                // }  
            }  
        }
        
        /// <summary>  
        /// 每个标签的惩罚值。  
        /// 默认标签（即标签0）将添加tagPenalties[0]的惩罚值。  
        /// 这些值应为正数，因为A*算法无法处理负惩罚值。  
        ///  
        /// 当为这个属性分配数组时，它必须长度为32。  
        ///  
        /// 注意：将此属性设置为null，或者尝试分配一个长度不为32的数组，将使得所有标签的惩罚值被视为零。  
        ///  
        /// 注意：如果你正在使用Seeker。当你调用seeker.StartPath时，Seeker会将此值设置为检查器字段中设置的值。  
        /// 因此，你需要通过脚本来更改Seeker的值，而不是设置这个值。  
        ///  
        /// 参见：Seeker.tagPenalties  
        /// </summary>  
        public int[] tagPenalties {  
            get {  
                // 获取器(getter)，返回当前的手动设置的标签惩罚值数组  
                return manualTagPenalties;  
            }  
            set {  
                // 设置器(setter)，允许外部设置标签惩罚值数组  
                if (value == null || value.Length != 32) {  
                    // 如果传入的数组为null或长度不为32，则将手动设置的标签惩罚值数组设为null  
                    // 同时将内部使用的标签惩罚值数组设为默认零惩罚值数组  
                    manualTagPenalties = null;  
                    internalTagPenalties = ZeroTagPenalties;  
                } else {  
                    // 如果传入的数组有效（非null且长度为32），则更新手动设置的标签惩罚值数组  
                    // 同时更新内部使用的标签惩罚值数组  
                    manualTagPenalties = value;  
                    internalTagPenalties = value;  
                }  
            }  
        }
        
        /// <summary>  
        /// 将此路径的引用计数增加1（用于池化）。  
        /// 对路径的声明将确保它不会被池化。  
        /// 如果您正在使用路径，则应在首次获取它时声明它，然后在不再使用它时释放它。当路径上没有声明时，它将被重置并放入池中。  
        ///  
        /// 这本质上是引用计数。  
        ///  
        /// 传递给此方法的对象仅用于更容易地检测池化是否未正确完成。  
        /// 它可以是任何对象，当从移动脚本中使用时，您可以只传递“this”。如果尝试使用相同的对象对同一路径进行两次声明（这通常不是您想要的）  
        /// 或者如果尝试使用未在该路径的Claim调用中使用的对象进行释放，则此类将引发异常。  
        /// 传递给Claim方法的对象需要与您传递给此方法的对象相同。  
        ///  
        /// 参见：Release  
        /// 参见：Pool  
        /// 参见：pooling（请在线文档中查看工作链接）  
        /// 参见：https://en.wikipedia.org/wiki/Reference_counting  
        /// </summary>  
        public void Claim(Object o)  
        {  
            // 如果传递的对象为空，则抛出空引用异常  
            if (Object.ReferenceEquals(o, null)) throw new ArgumentNullException("o");  
  
            // 遍历已声明的对象列表  
            for (int i = 0; i < claimed.Count; i++)  
            {  
                // 需要使用ReferenceEquals，因为它可能从另一个线程调用  
                // 如果已声明的列表中存在相同的对象，则抛出参数异常  
                if (Object.ReferenceEquals(claimed[i], o))  
                    throw new ArgumentException("您已经使用该对象 (" + o + ") 声明了路径。您是否尝试使用相同的对象两次来声明路径?");  
            }  
  
            // 将对象添加到已声明的列表中  
            claimed.Add(o);  
            
            // // 如果定义了ASTAR_POOL_DEBUG，则记录声明信息，包括对象的字符串表示和堆栈跟踪  
            // claimInfo.Add(o.ToString() + "\n\nClaimed from:\n" + System.Environment.StackTrace);  
        }
        
        /// <summary>  
        /// 线程安全地增加状态  
        /// </summary>  
        void IPathInternals.AdvanceState(PathState s) {  
            // lock (stateLock) {  
            //     // 使用lock关键字确保在修改PipelineState时的线程安全  
            //     // 将PipelineState更新为PipelineState和s中较大的那个值  
            //     PipelineState = (PathState)System.Math.Max((int)PipelineState, (int)s);  
            // }  
            PipelineState = (PathState)Math.Max((int)PipelineState, (int)s);  
        }
        
        
        /// <summary>  
        /// 减少路径的引用计数（对象池技术）。  
        /// 移除指定对象对路径的声明。  
        /// 当引用计数达到零时，路径将被放回对象池，所有变量将被清除，以便再次使用。  
        /// 这对于性能提升很有帮助，因为减少了对象的分配。  
        ///  
        /// 如果silent参数为true，则此方法将仅移除指定对象的声明，  
        /// 但如果声明计数达到零，路径也不会被放回对象池，除非之前已经有一个非silent的Release调用。  
        /// 这被内部路径查找组件（如Seeker和AstarPath）所使用，以便它们不会导致路径被放回对象池，  
        /// 从而避免在用户跳过声明/释放调用时产生奇怪的bug。  
        ///  
        /// 参见：Claim  
        /// 参见：PathPool  
        /// </summary>  
        /// <param name="o">要释放路径声明的对象。</param>  
        /// <param name="silent">是否静默释放，默认为false。</param>  
        public void Release (Object o, bool silent = false) {  
            // 检查传入的对象是否为null，如果是则抛出异常  
            if (o == null) throw new ArgumentNullException("o");  
  
            // 遍历当前所有已声明的对象  
            for (int i = 0; i < claimed.Count; i++) {  
                // 使用ReferenceEquals是因为这个方法可能会从另一个线程被调用  
                // 如果当前遍历到的对象与传入的对象是同一个对象（引用相同）  
                if (ReferenceEquals(claimed[i], o)) {  
                    
                    // 从已声明列表中移除该对象  
                    claimed.RemoveAt(i);  
  
                    // 如果不是静默释放，则标记为非静默已释放  
                    if (!silent) {  
                        releasedNotSilent = true;  
                    }  
  
                    // 如果已声明列表为空且之前有过非静默释放，则将当前路径对象放回对象池  
                    if (claimed.Count == 0 && releasedNotSilent) {  
                        // PathPool.Pool(this);  
                    }  

                    // 找到并处理完目标对象后，提前返回  
                    return;  
                }  
            }  
        }
        
        /// <summary>  
        /// 为计算准备低级别的路径变量。  
        /// 在路径搜索之前调用。  
        /// 总是在Prepare、Initialize和CalculateStep函数之前调用。  
        /// </summary>  
        /// <param name="pathHandler">路径处理器对象，用于处理路径相关的操作。</param>  
        public void PrepareBase(PathHandler pathHandler) {  
            // 如果路径处理器的PathID大于当前的pathID，则表明路径ID已经超过了65K，需要进行清理。  
            // 由于pathIDs是顺序分配的，我们可以这样做。  
            if (pathHandler.PathID > pathID) {  
                pathHandler.ClearPathIDs(); // 调用ClearPathIDs方法来清理pathIDs。  
            }  
  
            // 确保当前对象（可能是一个路径对象）对pathHandler有引用。  
            this.pathHandler = pathHandler;  
  
            // 将相关的路径数据分配给pathHandler，以便后续使用。  
            pathHandler.InitializeForPath(this); // 调用InitializeForPath方法，并传递当前对象为参数，进行初始化。  
  
            // 确保internalTagPenalties是一个长度为32的数组。  
            // 如果internalTagPenalties为空或者其长度不为32，则将其设置为ZeroTagPenalties。  
            if (internalTagPenalties == null || internalTagPenalties.Length != 32) {  
                internalTagPenalties = ZeroTagPenalties; // 将internalTagPenalties设置为默认的或零值的数组。  
            }  
  
            try {  
                ErrorCheck(); // 调用ErrorCheck方法进行错误检查。  
            } catch (Exception e) {  
                // 如果ErrorCheck方法抛出异常，则捕获该异常，并使用异常的消息调用FailWithError方法。  
                // 这通常用于记录错误或通知调用者出现了问题。  
                FailWithError(e.Message);  
            }  
        }
        
        /// <summary>  
        /// 由于发生错误而中止路径。  
        /// 将 <see cref="error"/> 设置为 true。  
        /// 当发生错误时调用此函数（例如，找不到有效的路径）。  
        /// 请参见：<see cref="FailWithError"/>  
        /// </summary>  
        public void Error() {  
            CompleteState = PathCompleteState.Error;  
        }  
  
        /// <summary>  
        /// 使路径处理失败，并将错误消息msg设置到<see cref="errorLog"/>中。  
        /// </summary>  
        /// <param name="msg">要记录的错误消息</param>  
        public void FailWithError(string msg) {  
            Error(); // 调用Error方法，标记路径处理为错误状态  
      
            // 检查errorLog是否为空字符串  
            if (errorLog != "") {  
                // 如果不为空，则在errorLog后添加换行符和新的错误消息msg  
                errorLog += "\n" + msg;  
            } else {  
                // 如果errorLog为空字符串，则将errorLog设置为错误消息msg  
                errorLog = msg;  
            }  
        }
        
        /// <summary>  
        /// 执行一些错误检查。  
        /// 确保用户没有使用旧的代码路径，并且没有犯下重大错误。  
        ///  
        /// 如果发现任何错误，则使路径失败。  
        /// </summary>
        private void ErrorCheck () {  
            // if (!hasBeenReset) FailWithError("请使用静态的Construct函数创建路径，不要使用正常的构造函数。");  
            if (((IPathInternals)this).Pooled) FailWithError("该路径当前在路径池中。您是否将路径发送了两次以进行计算？");  
            if (pathHandler == null) FailWithError("pathHandler字段未设置。请报告此错误。");  
            if (PipelineState > PathState.Processing) FailWithError("此路径已被处理。不要使用相同的路径对象两次请求路径。");  
        }
        
        public bool Pooled { get; set; }
        public void OnEnterPool()
        {
            throw new NotImplementedException();
        }

        public void Reset()
        {
            throw new NotImplementedException();
        }

        public void ReturnPath()
        {
            throw new NotImplementedException();
        }

        public void Prepare()
        {
            throw new NotImplementedException();
        }

        public void Initialize()
        {
            throw new NotImplementedException();
        }

        public void Cleanup()
        {
            throw new NotImplementedException();
        }

        /// <summary>计算，直到它完成或时间超过targetTick</summary>
        protected abstract void CalculateStep();
        void IPathInternals.CalculateStep () { CalculateStep(); }
    }
}