using Game.Plugins.JNGame.Sync.Frame.AStar.Navmesh;
using Game.Plugins.JNGame.Sync.Frame.AStar.Util;
using Plugins.JNGame.Sync.Frame.AStar;
using Plugins.JNGame.Sync.Frame.AStar.Entity;
using Plugins.JNGame.Sync.Frame.AStar.Entity.Tile;
using UnityEditor;
using UnityEngine;
using UnityEngine.Pool;

namespace Game.Plugins.JNGame.Sync.Frame.AStar.Entity.Graph
{
    public abstract class NavmeshBase : NavGraph, INavmesh, INavmeshHolder, ITransformedGraph
    {
        
        public const int VertexIndexMask = 0xFFF;

        public const int TileIndexMask = 0x7FFFF;
        public const int TileIndexOffset = 12;
        
        /// <summary>  
        /// 边界框的大小。  
        /// </summary>  
        public Vector3 forcedBoundsSize = new Vector3(100, 40, 100);
        
        /// <summary>  
        /// 瓦片在X轴方向上的世界单位大小  
        /// </summary>  
        public abstract float TileWorldSizeX { get; }  
  
        /// <summary>  
        /// 瓦片在Z轴方向上的世界单位大小  
        /// </summary>  
        public abstract float TileWorldSizeZ { get; }
        
        /// <summary>  
        /// 沿X轴方向的瓦片数量  
        /// </summary>  
        public int tileXCount;  
  
        /// <summary>  
        /// 沿Z轴方向的瓦片数量  
        /// </summary>  
        public int tileZCount;

        
        /// <summary>
        /// All tiles.
        ///
        /// See: <see cref="GetTile"/>
        /// </summary>
        protected NavmeshTile[] tiles;

        
        /// <summary>  
        /// 返回一个将图空间转换为世界空间的新变换。  
        /// 不更新<see cref="transform"/>字段。  
        /// 请参阅：<see cref="RelocateNodes(GraphTransform)"/>  
        /// </summary>  
        public abstract GraphTransform CalculateTransform();
        
        protected NavmeshBase(AStarData data) : base(data)
        {
        }

        public override void GetNodes (System.Action<GraphNode> action) {
            if (tiles == null) return;

            for (int i = 0; i < tiles.Length; i++) {
                if (tiles[i] == null || tiles[i].x+tiles[i].z*tileXCount != i) continue;
                TriangleMeshNode[] nodes = tiles[i].nodes;

                if (nodes == null) continue;

                for (int j = 0; j < nodes.Length; j++) action(nodes[j]);
            }
        }
        
        public override void DrawGUI()
        {
        }
        
        // /// <summary>  
        // /// 确定如何将图形空间转换为世界空间。  
        // /// 请参阅：<see cref="CalculateTransform"/>  
        // /// </summary>  
        // public GraphTransform transform = new GraphTransform(Matrix4x4.identity);

        public GraphTransform transform { get; }
        
        /// <summary>  
        /// 获取指定顶点索引的顶点坐标。  
        ///  
        /// 异常：如果顶点索引无效，将抛出 IndexOutOfRangeException。  
        /// 异常：如果顶点所在的瓦片尚未计算，将抛出 NullReferenceException。  
        ///  
        /// 参见：NavmeshTile.GetVertex  
        /// </summary>  
        public Int3 GetVertex(int index) {  
            // 通过位运算提取顶点索引中的瓦片索引部分  
            int tileIndex = (index >> TileIndexOffset) & TileIndexMask;  
  
            // 调用对应瓦片的 GetVertex 方法来获取顶点坐标  
            return tiles[tileIndex].GetVertex(index);  
        }
        
        /// <summary>  
        /// 获取指定顶点索引在图空间中的顶点坐标  
        /// </summary>  
        public Int3 GetVertexInGraphSpace(int index) {  
            // 通过位运算从顶点索引中提取瓦片索引  
            int tileIndex = (index >> TileIndexOffset) & TileIndexMask;  
  
            // 返回指定瓦片中的顶点在图空间中的坐标  
            return tiles[tileIndex].GetVertexInGraphSpace(index);  
        }

        public int GetVertexArrayIndex (int index) {
            return index & VertexIndexMask;
        }
        
        /// <summary>  
        /// 从瓦片索引获取瓦片坐标  
        /// </summary>  
        public void GetTileCoordinates(int tileIndex, out int x, out int z) {  
            // 原本的代码使用了 System.Math.DivRem 方法来同时获取商和余数，但这里为了简化直接使用了除法和减法。  
            //z = System.Math.DivRem(tileIndex, tileXCount, out x);  
            z = tileIndex / tileXCount; // 计算商，即瓦片在Z轴上的坐标  
            x = tileIndex - z * tileXCount; // 计算余数，即瓦片在X轴上的坐标  
        }
        
        /// <summary>  
        /// 返回包含指定位置的瓦片坐标。  
        /// 这个瓦片坐标不一定是有效的（即它可能超出边界）。  
        /// </summary>  
        public Int2 GetTileCoordinates(Vector3 position) {  
            // 将给定的世界空间位置转换为本地空间位置  
            position = transform.InverseTransform(position);  
      
            // 根据瓦片的世界大小将位置坐标转换为瓦片坐标  
            position.x /= TileWorldSizeX;  
            position.z /= TileWorldSizeZ;  
      
            // 返回计算出的瓦片坐标  
            return new Int2((int)position.x, (int)position.z);  
        }
        
        /// <summary>  
        /// 使用NewEmptyTile创建的tile填充图形  
        /// </summary>  
        protected void FillWithEmptyTiles() {  
            // 对于每一个z层（通常是深度或高度方向）  
            for (int z = 0; z < tileZCount; z++) {  
                // 对于每一个x层（通常是宽度或长度方向）  
                for (int x = 0; x < tileXCount; x++) {  
                    // 调用NewEmptyTile方法来创建一个新的空tile，并将其放置在二维数组tiles的对应位置  
                    tiles[z * tileXCount + x] = NewEmptyTile(x, z);  
                }  
            }  
        }
        
        /// <summary>  
        /// 创建一个新的空tile  
        /// </summary>  
        /// <param name="x">tile的x坐标</param>  
        /// <param name="z">tile的z坐标</param>  
        /// <returns>返回一个新的NavmeshTile实例</returns>  
        protected NavmeshTile NewEmptyTile(int x, int z) {  
            return new NavmeshTile {  
                // 设置tile的x坐标  
                x = x,  
                // 设置tile的z坐标  
                z = z,  
                // 设置tile的宽度为1  
                w = 1,  
                // 设置tile的高度为1  
                d = 1,  
                // 初始化一个空的顶点数组  
                verts = new Int3[0],  
                // 初始化一个空的图形空间顶点数组  
                vertsInGraphSpace = new Int3[0],  
                // 初始化一个空的三角形数组  
                tris = new int[0],  
                // 初始化一个空的TriangleMeshNode数组  
                nodes = new TriangleMeshNode[0],  
                // 从BBTree对象池中取出一个BBTree实例  
                bbTree = new BBTree(),  
                // 将当前对象（可能是NavmeshGraph或其他包含此方法的类）赋值给tile的graph属性  
                graph = this,  
            };  
        }
    }
}