// Assets/_Project/Scripts/Visuals/ConeMesh.cs
using UnityEngine;

namespace TD.Visuals
{
    /// <summary>
    /// Procedurally generates a cone mesh and assigns it to the GameObject's
    /// <see cref="MeshFilter"/>. Unity ships no built-in cone primitive, so this
    /// fills that gap for tower visuals (e.g. the Basic Arrow Tower).
    /// </summary>
    /// <remarks>
    /// <b>Visual only.</b> No networking, no collision — the mesh is purely cosmetic.
    /// The existing MeshRenderer + material (and any owner/paint tinting via
    /// <c>TowerInstance.tintedRenderers</c>) keep working unchanged.
    ///
    /// <b>Pivot.</b> The cone is centred on the local origin: its base ring sits at
    /// <c>y = -height/2</c> and its apex at <c>y = +height/2</c>, matching the vertical
    /// extents of a unit cube so it sits on a build tile the same way the old cube did.
    ///
    /// <b>Edit-time.</b> <c>[ExecuteAlways]</c> rebuilds the mesh in the editor so the
    /// cone is visible without entering play mode. The generated mesh is flagged
    /// <c>HideFlags.DontSave</c> so it is never written out as a project asset.
    /// </remarks>
    [ExecuteAlways]
    [RequireComponent(typeof(MeshFilter))]
    public class ConeMesh : MonoBehaviour
    {
        [Tooltip("Number of radial segments around the base. Higher = smoother circle.")]
        [Range(3, 64)]
        [SerializeField] private int sides = 16;

        [Tooltip("Radius of the cone's base in local units.")]
        [Min(0.001f)]
        [SerializeField] private float radius = 0.5f;

        [Tooltip("Height of the cone from base to apex in local units.")]
        [Min(0.001f)]
        [SerializeField] private float height = 1f;

        [Tooltip("Rotation of the base ring around the vertical axis, in degrees. With a low " +
                 "side count this makes a pyramid; e.g. sides = 4 with a 45° offset and " +
                 "radius 0.7071 yields a square-based pyramid whose faces align to the X/Z axes.")]
        [SerializeField] private float angularOffsetDegrees = 0f;

        private Mesh mesh;

        private void OnEnable() => Rebuild();

        private void OnValidate()
        {
            // OnValidate can fire before OnEnable in the editor; guard the component.
            if (isActiveAndEnabled) Rebuild();
        }

        private void OnDisable()
        {
            // Clean up the generated mesh so it doesn't linger when the object is destroyed.
            if (mesh != null)
            {
                if (Application.isPlaying) Destroy(mesh);
                else DestroyImmediate(mesh);
                mesh = null;
            }
        }

        private void Rebuild()
        {
            var filter = GetComponent<MeshFilter>();
            if (filter == null) return;

            if (mesh == null)
            {
                mesh = new Mesh { name = "ProceduralCone", hideFlags = HideFlags.DontSave };
            }

            BuildCone(mesh);
            filter.sharedMesh = mesh;
        }

        private void BuildCone(Mesh target)
        {
            target.Clear();

            float halfHeight = height * 0.5f;

            // Vertex layout:
            //   [0 .. sides-1]      base ring
            //   [sides]             apex
            //   [sides + 1]         base centre (for the bottom cap)
            int baseRingStart = 0;
            int apexIndex = sides;
            int baseCentreIndex = sides + 1;

            var vertices = new Vector3[sides + 2];
            for (int i = 0; i < sides; i++)
            {
                float angle = (i / (float)sides) * Mathf.PI * 2f
                              + angularOffsetDegrees * Mathf.Deg2Rad;
                vertices[baseRingStart + i] = new Vector3(
                    Mathf.Cos(angle) * radius,
                    -halfHeight,
                    Mathf.Sin(angle) * radius);
            }
            vertices[apexIndex] = new Vector3(0f, halfHeight, 0f);
            vertices[baseCentreIndex] = new Vector3(0f, -halfHeight, 0f);

            // Side faces (base ring -> apex) + base cap (fan from centre).
            // Wind both so they face outward / downward respectively.
            var triangles = new int[sides * 3 * 2];
            int t = 0;
            for (int i = 0; i < sides; i++)
            {
                int next = (i + 1) % sides;

                // Side triangle, outward-facing.
                triangles[t++] = baseRingStart + i;
                triangles[t++] = apexIndex;
                triangles[t++] = baseRingStart + next;

                // Base cap triangle, downward-facing.
                triangles[t++] = baseCentreIndex;
                triangles[t++] = baseRingStart + next;
                triangles[t++] = baseRingStart + i;
            }

            target.vertices = vertices;
            target.triangles = triangles;
            target.RecalculateNormals();
            target.RecalculateBounds();
        }
    }
}
