namespace Fusion.Addons.InterestManagement
{
	using System.Collections.Generic;
	using UnityEngine;
	/// 
	/// Contains helper methods related to interest sphere.
	/// 
	public static class InterestSphereUtility
	{
		// PUBLIC METHODS
		/// 
		/// Converts sphere to Fusion interest cell coordinate system.
		/// 
		public static void GetCells(Vector3 position, float radius, HashSet cells)
		{
			var   gridSize   = Simulation.AreaOfInterest.GetGridSize();
			int   gridSizeX  = gridSize.Item1;
			int   gridSizeY  = gridSize.Item2;
			int   gridSizeXY = gridSizeX * gridSizeY;
			int   gridSizeZ  = gridSize.Item3;
			int   gridHalfX  = gridSizeX / 2;
			int   gridHalfY  = gridSizeY / 2;
			int   gridHalfZ  = gridSizeZ / 2;
			int   cellSize   = Simulation.AreaOfInterest.GetCellSize();
			int   cellHalf   = cellSize / 2;
			float cellInv    = 1.0f / cellSize;
			if (radius > 0.0f)
			{
				float sqrDistance = radius + cellHalf * InterestUtility.SQRT3;
				sqrDistance *= sqrDistance;
				GetCellCoordinates(position - new Vector3(radius, radius, radius), out int minX, out int minY, out int minZ);
				GetCellCoordinates(position + new Vector3(radius, radius, radius), out int maxX, out int maxY, out int maxZ);
				for (int z = minZ; z <= maxZ; ++z)
				{
					for (int y = minY; y <= maxY; ++y)
					{
						for (int x = minX; x <= maxX; ++x)
						{
							Vector3 cellPosition = default;
							cellPosition.x = (float)((x - gridHalfX) * cellSize + cellHalf);
							cellPosition.y = (float)((y - gridHalfY) * cellSize + cellHalf);
							cellPosition.z = (float)((z - gridHalfZ) * cellSize + cellHalf);
							if (Vector3.SqrMagnitude(cellPosition - position) <= sqrDistance)
							{
								int cell = z * gridSizeXY + y * gridSizeX + x + 1;
								cells.Add(cell);
							}
						}
					}
				}
			}
			else
			{
				GetCellCoordinates(position, out int x, out int y, out int z);
				int cell = z * gridSizeXY + y * gridSizeX + x + 1;
				cells.Add(cell);
			}
			return;
			void GetCellCoordinates(Vector3 targetPosition, out int x, out int y, out int z)
			{
				x = gridHalfX + (int)(targetPosition.x * cellInv);
				y = gridHalfY + (int)(targetPosition.y * cellInv);
				z = gridHalfZ + (int)(targetPosition.z * cellInv);
				if (targetPosition.x < 0.0f) { x -= 1; }
				if (targetPosition.y < 0.0f) { y -= 1; }
				if (targetPosition.z < 0.0f) { z -= 1; }
				if (x < 0) { x = 0; } else if (x >= gridSizeX) { x = gridSizeX - 1; }
				if (y < 0) { y = 0; } else if (y >= gridSizeY) { y = gridSizeY - 1; }
				if (z < 0) { z = 0; } else if (z >= gridSizeZ) { z = gridSizeZ - 1; }
			}
		}
		/// 
		/// Draws interest sphere gizmo.
		/// 
		public static void DrawGizmo(Vector3 position, float radius, Color color, bool drawIcon = true)
		{
			Color gizmoColor = Gizmos.color;
			Gizmos.color = color;
			if (drawIcon == true)
			{
				Gizmos.DrawIcon(position, "d_ViewToolOrbit On@2x");
			}
			if (radius > 0.0f)
			{
				Gizmos.DrawWireSphere(position, radius);
			}
			if (InterestUtility.DrawSamplePositions == true)
			{
				DrawSamplePositions(position, radius);
			}
			Gizmos.color = gizmoColor;
		}
		/// 
		/// Draws positions that are sampled when resolving interest cells.
		/// 
		public static void DrawSamplePositions(Vector3 position, float radius)
		{
			Color gizmoColor = Gizmos.color;
			var   gridSize  = Simulation.AreaOfInterest.GetGridSize();
			int   gridSizeX = gridSize.Item1;
			int   gridSizeY = gridSize.Item2;
			int   gridSizeZ = gridSize.Item3;
			int   gridHalfX = gridSizeX / 2;
			int   gridHalfY = gridSizeY / 2;
			int   gridHalfZ = gridSizeZ / 2;
			int   cellSize  = Simulation.AreaOfInterest.GetCellSize();
			int   cellHalf  = cellSize / 2;
			float cellInv   = 1.0f / cellSize;
			if (radius > 0.0f)
			{
				float sqrDistance = radius + cellHalf * InterestUtility.SQRT3;
				sqrDistance *= sqrDistance;
				GetCellCoordinates(position - new Vector3(radius, radius, radius), out int minX, out int minY, out int minZ);
				GetCellCoordinates(position + new Vector3(radius, radius, radius), out int maxX, out int maxY, out int maxZ);
				for (int z = minZ; z <= maxZ; ++z)
				{
					for (int y = minY; y <= maxY; ++y)
					{
						for (int x = minX; x <= maxX; ++x)
						{
							Vector3 cellPosition = default;
							cellPosition.x = (float)((x - gridHalfX) * cellSize + cellHalf);
							cellPosition.y = (float)((y - gridHalfY) * cellSize + cellHalf);
							cellPosition.z = (float)((z - gridHalfZ) * cellSize + cellHalf);
							if (Vector3.SqrMagnitude(cellPosition - position) > sqrDistance)
							{
								Gizmos.color = Color.red;
								Gizmos.DrawCube(cellPosition, new Vector3(cellHalf, cellHalf, cellHalf));
							}
							else
							{
								Gizmos.color = Color.green;
								Gizmos.DrawCube(cellPosition, new Vector3(cellHalf, cellHalf, cellHalf));
							}
						}
					}
				}
			}
			else
			{
				GetCellCoordinates(position, out int x, out int y, out int z);
				Vector3 cellPosition = default;
				cellPosition.x = (x - gridHalfX) * cellSize + cellHalf;
				cellPosition.y = (y - gridHalfY) * cellSize + cellHalf;
				cellPosition.z = (z - gridHalfZ) * cellSize + cellHalf;
				Gizmos.color = Color.green;
				Gizmos.DrawCube(cellPosition, new Vector3(cellHalf, cellHalf, cellHalf));
			}
			Gizmos.color = gizmoColor;
			return;
			void GetCellCoordinates(Vector3 targetPosition, out int x, out int y, out int z)
			{
				x = gridHalfX + (int)(targetPosition.x * cellInv);
				y = gridHalfY + (int)(targetPosition.y * cellInv);
				z = gridHalfZ + (int)(targetPosition.z * cellInv);
				if (targetPosition.x < 0.0f) { x -= 1; }
				if (targetPosition.y < 0.0f) { y -= 1; }
				if (targetPosition.z < 0.0f) { z -= 1; }
				if (x < 0) { x = 0; } else if (x >= gridSizeX) { x = gridSizeX - 1; }
				if (y < 0) { y = 0; } else if (y >= gridSizeY) { y = gridSizeY - 1; }
				if (z < 0) { z = 0; } else if (z >= gridSizeZ) { z = gridSizeZ - 1; }
			}
		}
	}
}