namespace Fusion.Addons.InterestManagement
{
	using System.Collections.Generic;
	using UnityEngine;
	/// 
	/// Contains helper methods related to interest cone.
	/// 
	public static class InterestConeUtility
	{
		// PUBLIC METHODS
		/// 
		/// Converts cone to Fusion interest cell coordinate system.
		/// 
		public static void GetCells(Vector3 position, Quaternion rotation, float length, Vector2 startSize, Vector2 endSize, Vector2 minSize, Vector2 maxSize, HashSet cells)
		{
			if (length <= 0.0f)
				return;
			if (startSize.x < 0.0f) startSize.x = 0.0f;
			if (startSize.y < 0.0f) startSize.y = 0.0f;
			if (endSize.x < 0.0f) endSize.x = 0.0f;
			if (endSize.y < 0.0f) endSize.y = 0.0f;
			if (minSize.x <= 0.0f) { minSize.x = Mathf.Min(startSize.x, endSize.x); }
			if (minSize.y <= 0.0f) { minSize.y = Mathf.Min(startSize.y, endSize.y); }
			if (maxSize.x <= 0.0f) { maxSize.x = Mathf.Max(startSize.x, endSize.x); }
			if (maxSize.y <= 0.0f) { maxSize.y = Mathf.Max(startSize.y, endSize.y); }
			if (maxSize.x < minSize.x) { maxSize.x = minSize.x; }
			if (maxSize.y < minSize.y) { maxSize.y = minSize.y; }
			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;
			float maxStepSize = cellHalf * InterestUtility.SQRT2;
			Vector3 forward = rotation * Vector3.forward;
			Vector3 right   = rotation * Vector3.right;
			Vector3 up      = rotation * Vector3.up;
			int     zSteps = (int)(length / maxStepSize) + 1;
			Vector3 zStep  = forward * length / zSteps;
			float   zInv   = 1.0f / zSteps;
			for (int z = 0; z <= zSteps; ++z)
			{
				float zAlpha = z * zInv;
				float   yLength = Mathf.Clamp(Mathf.LerpUnclamped(startSize.y, endSize.y, zAlpha), minSize.y, maxSize.y);
				int     ySteps  = (int)(yLength / maxStepSize) + 1;
				Vector3 yStep   = up * yLength / ySteps;
				float   xLength = Mathf.Clamp(Mathf.LerpUnclamped(startSize.x, endSize.x, zAlpha), minSize.x, maxSize.x);
				int     xSteps  = (int)(xLength / maxStepSize) + 1;
				Vector3 xStep   = right * xLength / xSteps;
				Vector3 basePosition = position + zStep * z - 0.5f * (yStep * ySteps + xStep * xSteps);
				for (int y = 0; y <= ySteps; ++y)
				{
					Vector3 checkPosition = basePosition + yStep * y;
					for (int x = 0; x <= xSteps; ++x)
					{
						int x1 = gridHalfX + (int)(checkPosition.x * cellInv);
						int y1 = gridHalfY + (int)(checkPosition.y * cellInv);
						int z1 = gridHalfZ + (int)(checkPosition.z * cellInv);
						if (checkPosition.x < 0.0f) { x1 -= 1; }
						if (checkPosition.y < 0.0f) { y1 -= 1; }
						if (checkPosition.z < 0.0f) { z1 -= 1; }
						if (x1 < 0) { x1 = 0; } else if (x1 >= gridSizeX) { x1 = gridSizeX - 1; }
						if (y1 < 0) { y1 = 0; } else if (y1 >= gridSizeY) { y1 = gridSizeY - 1; }
						if (z1 < 0) { z1 = 0; } else if (z1 >= gridSizeZ) { z1 = gridSizeZ - 1; }
						int cell = z1 * gridSizeY * gridSizeX + y1 * gridSizeX + x1 + 1;
						cells.Add(cell);
						checkPosition += xStep;
					}
				}
			}
		}
		/// 
		/// Draws interest cone gizmo.
		/// 
		public static void DrawGizmo(Vector3 position, Quaternion rotation, float length, Vector2 startSize, Vector2 endSize, Vector2 minSize, Vector2 maxSize, Color color, bool drawIcon = true)
		{
			if (length <= 0.0f)
				return;
			Color gizmoColor = Gizmos.color;
			Gizmos.color = color;
			if (drawIcon == true)
			{
				Gizmos.DrawIcon(position, "d_ViewToolOrbit On@2x");
			}
			Vector3 centerPosition = position;
			Vector3 forward = rotation * Vector3.forward;
			Vector3 right   = rotation * Vector3.right;
			Vector3 up      = rotation * Vector3.up;
			Vector3 rightOffset = right * startSize.x * 0.5f;
			Vector3 upOffset    = up    * startSize.y * 0.5f;
			Vector3 point00 = centerPosition + upOffset - rightOffset;
			Vector3 point01 = centerPosition + upOffset + rightOffset;
			Vector3 point02 = centerPosition - upOffset - rightOffset;
			Vector3 point03 = centerPosition - upOffset + rightOffset;
			centerPosition += forward * length;
			rightOffset = right * endSize.x * 0.5f;
			upOffset    = up    * endSize.y * 0.5f;
			Vector3 point10 = centerPosition + upOffset - rightOffset;
			Vector3 point11 = centerPosition + upOffset + rightOffset;
			Vector3 point12 = centerPosition - upOffset - rightOffset;
			Vector3 point13 = centerPosition - upOffset + rightOffset;
			Gizmos.DrawLine(point10, point11);
			Gizmos.DrawLine(point11, point13);
			Gizmos.DrawLine(point13, point12);
			Gizmos.DrawLine(point12, point10);
			Gizmos.DrawLine(point00, point10);
			Gizmos.DrawLine(point01, point11);
			Gizmos.DrawLine(point02, point12);
			Gizmos.DrawLine(point03, point13);
			Gizmos.DrawLine(point00, point01);
			Gizmos.DrawLine(point01, point03);
			Gizmos.DrawLine(point03, point02);
			Gizmos.DrawLine(point02, point00);
			if (InterestUtility.DrawSamplePositions == true)
			{
				DrawSamplePositions(position, rotation, length, startSize, endSize, minSize, maxSize);
			}
			Gizmos.color = gizmoColor;
		}
		/// 
		/// Draws positions that are sampled when resolving interest cells.
		/// 
		public static void DrawSamplePositions(Vector3 position, Quaternion rotation, float length, Vector2 startSize, Vector2 endSize, Vector2 minSize, Vector2 maxSize)
		{
			if (length <= 0.0f)
				return;
			if (startSize.x < 0.0f) startSize.x = 0.0f;
			if (startSize.y < 0.0f) startSize.y = 0.0f;
			if (endSize.x < 0.0f) endSize.x = 0.0f;
			if (endSize.y < 0.0f) endSize.y = 0.0f;
			if (minSize.x <= 0.0f) { minSize.x = Mathf.Min(startSize.x, endSize.x); }
			if (minSize.y <= 0.0f) { minSize.y = Mathf.Min(startSize.y, endSize.y); }
			if (maxSize.x <= 0.0f) { maxSize.x = Mathf.Max(startSize.x, endSize.x); }
			if (maxSize.y <= 0.0f) { maxSize.y = Mathf.Max(startSize.y, endSize.y); }
			if (maxSize.x < minSize.x) { maxSize.x = minSize.x; }
			if (maxSize.y < minSize.y) { maxSize.y = minSize.y; }
			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;
			float maxStepSize = cellHalf * InterestUtility.SQRT2;
			Vector3 forward = rotation * Vector3.forward;
			Vector3 right   = rotation * Vector3.right;
			Vector3 up      = rotation * Vector3.up;
			int     zSteps = (int)(length / maxStepSize) + 1;
			Vector3 zStep  = forward * length / zSteps;
			float   zInv   = 1.0f / zSteps;
			Color gizmoColor = Gizmos.color;
			Gizmos.color = Color.red;
			for (int z = 0; z <= zSteps; ++z)
			{
				float zAlpha = z * zInv;
				float   yLength = Mathf.Clamp(Mathf.LerpUnclamped(startSize.y, endSize.y, zAlpha), minSize.y, maxSize.y);
				int     ySteps  = (int)(yLength / maxStepSize) + 1;
				Vector3 yStep   = up * yLength / ySteps;
				float   xLength = Mathf.Clamp(Mathf.LerpUnclamped(startSize.x, endSize.x, zAlpha), minSize.x, maxSize.x);
				int     xSteps  = (int)(xLength / maxStepSize) + 1;
				Vector3 xStep   = right * xLength / xSteps;
				Vector3 basePosition = position + zStep * z - 0.5f * (yStep * ySteps + xStep * xSteps);
				for (int y = 0; y <= ySteps; ++y)
				{
					Vector3 checkPosition = basePosition + yStep * y;
					for (int x = 0; x <= xSteps; ++x)
					{
						Gizmos.DrawSphere(checkPosition, cellSize * 0.05f);
						int x1 = gridHalfX + (int)(checkPosition.x * cellInv);
						int y1 = gridHalfY + (int)(checkPosition.y * cellInv);
						int z1 = gridHalfZ + (int)(checkPosition.z * cellInv);
						if (checkPosition.x < 0.0f) { x1 -= 1; }
						if (checkPosition.y < 0.0f) { y1 -= 1; }
						if (checkPosition.z < 0.0f) { z1 -= 1; }
						if (x1 < 0) { x1 = 0; } else if (x1 >= gridSizeX) { x1 = gridSizeX - 1; }
						if (y1 < 0) { y1 = 0; } else if (y1 >= gridSizeY) { y1 = gridSizeY - 1; }
						if (z1 < 0) { z1 = 0; } else if (z1 >= gridSizeZ) { z1 = gridSizeZ - 1; }
						Vector3 cellPosition = default;
						cellPosition.x = (x1 - gridHalfX) * cellSize + cellHalf;
						cellPosition.y = (y1 - gridHalfY) * cellSize + cellHalf;
						cellPosition.z = (z1 - gridHalfZ) * cellSize + cellHalf;
						checkPosition += xStep;
					}
				}
			}
			Gizmos.color = gizmoColor;
		}
	}
}