using System;

namespace Editor.MeshEditor;

[Title( "Sphere" ), Icon( "circle" )]
internal class SpherePrimitive : PrimitiveBuilder
{
	[Title( "Number of sides" )]
	public int NumberOfSides { get; set; } = 8;

	[Hide] public Vector3 Center { get; set; }
	[Hide] public Vector3 Size { get; set; }

	public override void SetFromBox( BBox box )
	{
		Center = box.Center;
		Size = box.Size;
	}

	public override void Build( PolygonMesh mesh )
	{
		//
		// Build the sphere by building slices at constant angular intervals.
		// 
		// Each slice is a ring of four-sided faces, except for the top and bottom slices,
		// which are flattened cones.
		//
		// Unrolled, a sphere made with 5 'sides' has 25 faces and looks like this:
		//				
		//			/\  /\  /\  /\  /\
		//		   / 0\/ 1\/ 2\/ 3\/ 4\
		//		  |  5|  6|  7|  8|  9| 	
		//		  | 10| 11| 12| 13| 14| 	
		//		  | 15| 16| 17| 18| 19| 	
		//		   \20/\21/\22/\23/\24/
		//			\/  \/  \/  \/  \/
		//

		var halfSize = Size / 2;

		float angle = 0.0f;
		float angleStep = MathF.PI / NumberOfSides;

		for ( int slice = 0; slice < NumberOfSides; slice++ )
		{
			float angle1 = angle + angleStep;

			// Make the upper polygon.
			var upperPoints = new Vector3[NumberOfSides + 1];
			{
				for ( int i = 0; i < NumberOfSides; i++ )
				{
					var angle2 = i * (MathF.PI * 2.0f / NumberOfSides);
					upperPoints[i] = Center + new Vector3( MathF.Sin( angle2 ), MathF.Cos( angle2 ), -1.0f ) * halfSize * MathF.Sin( angle );
				}

				upperPoints[NumberOfSides] = upperPoints[0];
			}

			// Make the lower polygon.
			var lowerPoints = new Vector3[NumberOfSides + 1];
			{
				for ( int i = 0; i < NumberOfSides; i++ )
				{
					var angle2 = i * (MathF.PI * 2.0f / NumberOfSides);
					lowerPoints[i] = Center + new Vector3( MathF.Sin( angle2 ), MathF.Cos( angle2 ), -1.0f ) * halfSize * MathF.Sin( angle1 );
				}

				lowerPoints[NumberOfSides] = lowerPoints[0];
			}

			float upperHeight = Center.z + halfSize.z * MathF.Cos( angle );
			float lowerHeight = Center.z + halfSize.z * MathF.Cos( angle1 );

			for ( int i = 0; i < NumberOfSides; i++ )
			{
				// Top and bottom are cones, not rings
				if ( slice == 0 )
				{
					mesh.AddFace(
						upperPoints[i + 1].WithZ( upperHeight ),
						lowerPoints[i + 1].WithZ( lowerHeight ),
						lowerPoints[i].WithZ( lowerHeight )
					);
				}
				else if ( slice == NumberOfSides - 1 )
				{
					mesh.AddFace(
						upperPoints[i].WithZ( upperHeight ),
						upperPoints[i + 1].WithZ( upperHeight ),
						lowerPoints[i + 1].WithZ( lowerHeight )
					);
				}
				else
				{
					mesh.AddFace(
						upperPoints[i].WithZ( upperHeight ),
						upperPoints[i + 1].WithZ( upperHeight ),
						lowerPoints[i + 1].WithZ( lowerHeight ),
						lowerPoints[i].WithZ( lowerHeight )
					);
				}
			}

			angle += angleStep;
		}
	}
}
