using System;
using System.Collections.Generic;
using Game.Plugins.JNGame.Sync.Frame.AStar.Navmesh;
using Game.Plugins.JNGame.Sync.Frame.AStar.Util;
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 class RecastGraph : NavmeshBase
    {
	    
	    /// <summary>  
	    /// 边界框的中心点。  
	    /// 扫描将仅在边界框内部进行。  
	    /// </summary>  
	    public Vector3 forcedBoundsCenter;
	    
	    /// <summary>  
	    /// 图形的旋转角度（以度为单位）  
	    /// </summary>  
	    public Vector3 rotation;
	    
	    /// <summary>  
	    /// 如果为true，扫描图形将生成一个完全空的图形。  
	    /// 例如，如果你想用自定义的导航网格替换图形，这将非常有用。  
	    /// </summary>  
	    public bool scanEmptyGraph;
	    
	    /// <summary>  
	    /// 体素样本大小（x，z）。  
	    /// 当生成重铸图形时，会将世界体素化。  
	    /// 可以将其视为使用许多盒子构建的世界的近似值。  
	    /// 如果您玩过《我的世界》，那么它看起来非常相似（但盒子更小）。  
	    /// [打开在线文档以查看图像]  
	    /// 单元格大小是这些盒子的宽度和深度。盒子的高度通常要小得多，并且是自动计算的。请参阅<see cref="CellHeight"/>。  
	    ///  
	    /// 较低的值将产生更高质量的导航网格，但扫描图形将更慢。  
	    ///  
	    /// [打开在线文档以查看图像]  
	    /// </summary>  
	    public float cellSize = 0.5F;
	    
	    /// <summary>  
	    /// 如果为true，则将图形分割成tile；否则，使用单个tile覆盖整个图形。  
	    ///  
	    /// 使用tile具有多种用途，但也存在一些缺点。  
	    /// - 使用tile允许您一次只更新图形的一部分。在重铸图形上进行图形更新时，它将始终重新计算整个tile（或如果没有tile，则重新计算整个图形）。  
	    ///    <see cref="NavmeshCut"/>组件也按tile进行处理。  
	    /// - 使用tile允许您使用<see cref="NavmeshPrefab"/>s。  
	    /// - 使用tile可以分解非常大的三角形，这在某些情况下可以提高路径质量，并使导航网格更紧密地跟随地面的y坐标。  
	    /// - 使用tile可以大大加快生成导航网格的速度，因为每个tile都可以并行计算。  
	    ///    但是，如果tile制作得太小，则许多tile的开销可能会使其比拥有较少的tile更慢。  
	    /// - 使用小tile在某些情况下可能会使路径质量变差，但是将<see cref="FunnelModifier"/>s的质量设置设置为高（或使用<see cref="RichAI.funnelSimplification"/>）将大多数情况下会缓解这个问题。  
	    ///  
	    /// 请参阅：<see cref="editorTileSize"/>  
	    ///  
	    /// 自4.1版本起，默认值为true。  
	    /// </summary>  
	    public bool useTiles = true;
	    
	    /// <summary>  
	    /// 沿X轴的一个tile在体素中的大小。  
	    /// \copydetails editorTileSize  
	    ///  
	    /// 警告：不要修改，它会在扫描时从<see cref="editorTileSize"/>设置  
	    ///  
	    /// 请参阅：<see cref="tileSizeZ"/>  
	    /// </summary>  
	    public int tileSizeX = 128;  
  
	    /// <summary>  
	    /// 沿Z轴的一个tile在体素中的大小。  
	    /// \copydetails editorTileSize  
	    ///  
	    /// 警告：不要修改，它会在扫描时从<see cref="editorTileSize"/>设置  
	    ///  
	    /// 请参阅：<see cref="tileSizeX"/>  
	    /// </summary>  
	    public int tileSizeZ = 128;
	    
	    /// <summary>  
	    /// 单个tile在体素中的大小。  
	    /// 这是tile的宽度。  
	    ///  
	    /// [打开在线文档以查看图像]  
	    ///  
	    /// 较大的tile大小在初始扫描
	    /// 时可能更快（但如果在大世界中使用过大的tile大小，请小心内存溢出问题）  
	    /// 较小的tile大小在更新时（快得多）。  
	    ///  
	    /// 不同的tile大小会影响路径的质量。通常最好将巨大的开放区域拆分为多个tile以获得更好的路径质量，  
	    /// 但过小的tile也可能导致看起来像无形障碍物的效果。  
	    /// 要了解更多关于此的信息，请参阅navmeshnotes（在在线文档中查看工作链接）。  
	    /// 通常最好进行实验，看看哪种方法最适合您的游戏。  
	    ///  
	    /// 当扫描重铸图形时，可以并行计算单个tile，这可以大大加快扫描大型世界的速度。  
	    /// 当您要重新计算重铸图形的一部分时，这只能按tile逐个进行，这意味着如果您经常尝试更新重铸图形中远小于tile大小的区域，  
	    /// 则将进行大量不必要的计算。然而，另一方面，如果您更新比tile大小大得多的重铸图形区域，则可能会比必要的速度慢，因为使用大量的tile会有一些开销  
	    /// 而不是几个较大的tile（尽管不是很多）。  
	    ///  
	    /// 推荐的值介于64和256之间，但这些是非常软的限制。可以使用较大和较小的值。  
	    /// </summary>  
	    public int editorTileSize = 128;
	    
        public RecastGraph(AStarData data) : base(data)
        {
        }

        public override void DrawGUI()
        {

        }

        public override void GetNodes(Action<GraphNode> action)
        {
            throw new NotImplementedException();
        }

        public override void ScanInternal()
        {
            TriangleMeshNode.SetNavmeshHolder(this.data.GetGraphIndex(this), this);
            // RelevantGraphSurface.UpdateAllPositions();
            // ScanAllTiles();
        }

        public override float TileWorldSizeX { get; }
        public override float TileWorldSizeZ { get; }

        /// <summary>  
        /// 计算图变换。  
        /// </summary>  
        /// <returns>返回表示图变换的 GraphTransform 对象。</returns>  
        public override GraphTransform CalculateTransform() {  
	        // 创建一个新的 GraphTransform 对象，该对象基于给定的矩阵变换。  
	        // 矩阵变换首先应用 forcedBoundsSize*0.5f 的负值作为平移，然后应用旋转和缩放。  
	        return new GraphTransform(  
		        Matrix4x4.TRS(  
			        forcedBoundsCenter, // 平移中心  
			        Quaternion.Euler(rotation), // 旋转值，通过欧拉角表示  
			        Vector3.one // 缩放因子，所有维度都缩放为1  
		        ) * // 矩阵乘法  
		        Matrix4x4.TRS(  
			        -forcedBoundsSize * 0.5f, // 平移量，是 forcedBoundsSize 的一半的负值  
			        Quaternion.identity, // 不旋转  
			        Vector3.one // 缩放因子，所有维度都缩放为1  
		        )  
	        );  
        }
        
        void InitializeTileInfo () {  
	        // 计算体素网格的尺寸  
	        // Voxel grid size  
	        int totalVoxelWidth = (int)(forcedBoundsSize.x / cellSize + 0.5f); // 总体素宽度  
	        int totalVoxelDepth = (int)(forcedBoundsSize.z / cellSize + 0.5f); // 总体素深度  
  
	        // 根据是否使用tiles来设置tile的尺寸  
	        if (!useTiles) {  
		        // 如果不使用tiles，则tile的尺寸等于总体素的尺寸  
		        tileSizeX = totalVoxelWidth;  
		        tileSizeZ = totalVoxelDepth;  
	        } else {  
		        // 如果使用tiles，则tile的尺寸等于编辑器设置的尺寸  
		        tileSizeX = editorTileSize;  
		        tileSizeZ = editorTileSize;  
	        }  
  
	        // 计算tiles的数量  
	        // Number of tiles  
	        tileXCount = (totalVoxelWidth + tileSizeX - 1) / tileSizeX; // X轴上的tile数量  
	        tileZCount = (totalVoxelDepth + tileSizeZ - 1) / tileSizeZ; // Z轴上的tile数量  
  
	        // 检查tiles的数量是否超过了最大限制  
	        if (tileXCount * tileZCount > TileIndexMask + 1) {  
		        // 如果超过了最大限制，则抛出异常  
		        throw new System.Exception("Tiles数量过多 (" + (tileXCount * tileZCount) + ")，最大限制是 " + (TileIndexMask + 1) +  
		                                   "\n尝试在A*检查器的'Optimizations'选项卡下禁用ASTAR_RECAST_LARGER_TILES选项。");  
	        }  
  
	        // 初始化tiles数组  
	        tiles = new NavmeshTile[tileXCount * tileZCount];  
        }
        
  //       protected void ScanAllTiles () {
	 //        
		// 	transform = CalculateTransform();
		// 	InitializeTileInfo();
  //
		// 	// If this is true, just fill the graph with empty tiles
		// 	if (scanEmptyGraph) {
		// 		FillWithEmptyTiles();
		// 		yield break;
		// 	}
  //
		// 	// A walkableClimb higher than walkableHeight can cause issues when generating the navmesh since then it can in some cases
		// 	// Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with navmesh without links)
		// 	// The editor scripts also enforce this but we enforce it here too just to be sure
		// 	walkableClimb = Mathf.Min(walkableClimb, walkableHeight);
  //
		// 	yield return new Progress(0, "Finding Meshes");
		// 	var bounds = transform.Transform(new Bounds(forcedBoundsSize*0.5f, forcedBoundsSize));
		// 	var meshes = CollectMeshes(bounds);
		// 	var buckets = PutMeshesIntoTileBuckets(meshes);
  //
		// 	Queue<Int2> tileQueue = new Queue<Int2>();
  //
		// 	// Put all tiles in the queue
		// 	for (int z = 0; z < tileZCount; z++) {
		// 		for (int x = 0; x < tileXCount; x++) {
		// 			tileQueue.Enqueue(new Int2(x, z));
		// 		}
		// 	}
  //
		// 	var workQueue = new ParallelWorkQueue<Int2>(tileQueue);
		// 	// Create the voxelizers and set all settings (one for each thread)
		// 	var voxelizers = new Voxelize[workQueue.threadCount];
		// 	for (int i = 0; i < voxelizers.Length; i++) voxelizers[i] = new Voxelize(CellHeight, cellSize, walkableClimb, walkableHeight, maxSlope, maxEdgeLength);
		// 	workQueue.action = (tile, threadIndex) => {
		// 		voxelizers[threadIndex].inputMeshes = buckets[tile.x + tile.y*tileXCount];
		// 		tiles[tile.x + tile.y*tileXCount] = BuildTileMesh(voxelizers[threadIndex], tile.x, tile.y, threadIndex);
		// 	};
  //
		// 	// Prioritize responsiveness while playing
		// 	// but when not playing prioritize throughput
		// 	// (the Unity progress bar is also pretty slow to update)
		// 	int timeoutMillis = Application.isPlaying ? 1 : 200;
  //
		// 	// Scan all tiles in parallel
		// 	foreach (var done in workQueue.Run(timeoutMillis)) {
		// 		yield return new Progress(Mathf.Lerp(0.1f, 0.9f, done / (float)tiles.Length), "Calculated Tiles: " + done + "/" + tiles.Length);
		// 	}
  //
		// 	yield return new Progress(0.9f, "Assigning Graph Indices");
  //
		// 	// Assign graph index to nodes
		// 	uint graphIndex = (uint)AstarPath.active.data.GetGraphIndex(this);
  //
		// 	GetNodes(node => node.GraphIndex = graphIndex);
  //
		// 	// First connect all tiles with an EVEN coordinate sum
		// 	// This would be the white squares on a chess board.
		// 	// Then connect all tiles with an ODD coordinate sum (which would be all black squares on a chess board).
		// 	// This will prevent the different threads that do all
		// 	// this in parallel from conflicting with each other.
		// 	// The directions are also done separately
		// 	// first they are connected along the X direction and then along the Z direction.
		// 	// Looping over 0 and then 1
		// 	for (int coordinateSum = 0; coordinateSum <= 1; coordinateSum++) {
		// 		for (int direction = 0; direction <= 1; direction++) {
		// 			for (int i = 0; i < tiles.Length; i++) {
		// 				if ((tiles[i].x + tiles[i].z) % 2 == coordinateSum) {
		// 					tileQueue.Enqueue(new Int2(tiles[i].x, tiles[i].z));
		// 				}
		// 			}
  //
		// 			workQueue = new ParallelWorkQueue<Int2>(tileQueue);
		// 			workQueue.action = (tile, threadIndex) => {
		// 				// Connect with tile at (x+1,z) and (x,z+1)
		// 				if (direction == 0 && tile.x < tileXCount - 1)
		// 					ConnectTiles(tiles[tile.x + tile.y * tileXCount], tiles[tile.x + 1 + tile.y * tileXCount]);
		// 				if (direction == 1 && tile.y < tileZCount - 1)
		// 					ConnectTiles(tiles[tile.x + tile.y * tileXCount], tiles[tile.x + (tile.y + 1) * tileXCount]);
		// 			};
  //
		// 			var numTilesInQueue = tileQueue.Count;
		// 			// Connect all tiles in parallel
		// 			foreach (var done in workQueue.Run(timeoutMillis)) {
		// 				yield return new Progress(0.95f, "Connected Tiles " + (numTilesInQueue - done) + "/" + numTilesInQueue + " (Phase " + (direction + 1 + 2*coordinateSum) + " of 4)");
		// 			}
		// 		}
		// 	}
  //
		// 	for (int i = 0; i < meshes.Count; i++) meshes[i].Pool();
		// 	ListPool<RasterizationMesh>.Release(ref meshes);
  //
		// 	// Signal that tiles have been recalculated to the navmesh cutting system.
		// 	navmeshUpdateData.OnRecalculatedTiles(tiles);
		// 	if (OnRecalculatedTiles != null) OnRecalculatedTiles(tiles.Clone() as NavmeshTile[]);
		// }
    }
}