// Assets/_Project/Scripts/UI/Minimap/MinimapEntityRegistry.cs
using System;
using System.Collections.Generic;
using UnityEngine;

namespace TD.UI.Minimap
{
    /// <summary>
    /// Visual category for a minimap entity. Determines the shape and size of the icon drawn
    /// by <see cref="MinimapView"/>. Color is supplied separately by the entity itself so the
    /// view does not need to know about ownership / faction mapping.
    /// </summary>
    public enum MinimapIconKind : byte
    {
        Enemy,
        Tower,
        Builder,
    }

    /// <summary>
    /// Anything that wants to appear on the minimap as a dynamic icon implements this. The view
    /// reads every getter on each refresh tick, so values may change between reads (movement is
    /// fine — no caching needed).
    /// </summary>
    public interface IMinimapEntity
    {
        Vector3 WorldPosition { get; }
        Color MinimapColor { get; }
        MinimapIconKind IconKind { get; }

        /// <summary>
        /// The entity's diameter (or footprint extent) in world units. The view converts this
        /// to pixels using the current minimap zoom, so a 2×2 tower will appear as a 2-tile
        /// square on the minimap and adjacent towers will visually touch — matching their
        /// world-space relationship. For point-like entities (builders, enemies) the view
        /// also enforces a minimum pixel size so they stay visible when fully zoomed out.
        /// </summary>
        float MinimapWorldSize { get; }
    }

    /// <summary>
    /// Static registry of every entity currently visible on the minimap. Entities register
    /// themselves on spawn and deregister on despawn; the <see cref="MinimapView"/> iterates the
    /// set on each refresh.
    /// </summary>
    /// <remarks>
    /// Backing store is a <see cref="HashSet{T}"/> so duplicate Register calls are no-ops and
    /// Deregister is O(1). Iteration uses the struct enumerator via <see cref="ForEach"/> to
    /// avoid the boxing that would happen if we exposed an <see cref="IEnumerable{T}"/>.
    ///
    /// Static lifetime is intentional: the registry survives scene transitions only if entities
    /// remember to deregister. NGO's <c>OnNetworkDespawn</c> covers that for towers / builders /
    /// enemies. Manual <see cref="Clear"/> is provided for tests and for explicit reset on match
    /// teardown if needed.
    /// </remarks>
    public static class MinimapEntityRegistry
    {
        private static readonly HashSet<IMinimapEntity> s_entities = new HashSet<IMinimapEntity>();

        /// <summary>Number of currently-registered entities. Cheap, no allocation.</summary>
        public static int Count => s_entities.Count;

        /// <summary>
        /// Registers <paramref name="entity"/> for minimap rendering. No-op if already registered.
        /// Safe to call from any thread that Unity allows (i.e., the main thread).
        /// </summary>
        public static void Register(IMinimapEntity entity)
        {
            if (entity == null) return;
            s_entities.Add(entity);
        }

        /// <summary>Removes <paramref name="entity"/> from the registry. No-op if not present.</summary>
        public static void Deregister(IMinimapEntity entity)
        {
            if (entity == null) return;
            s_entities.Remove(entity);
        }

        /// <summary>
        /// Invokes <paramref name="action"/> once per registered entity. Uses HashSet's struct
        /// enumerator so iteration is allocation-free (the only allocation is the closure that
        /// <paramref name="action"/> may carry, which is the caller's choice).
        /// </summary>
        public static void ForEach(Action<IMinimapEntity> action)
        {
            if (action == null) return;
            foreach (var e in s_entities) action(e);
        }

        /// <summary>Drops every registered entity. Tests and explicit match-teardown use only.</summary>
        public static void Clear() => s_entities.Clear();
    }
}
