using System;
using System.Collections.Generic;
using UnityEngine;
namespace OM
{
///
/// A static event manager supporting both typed and named event subscriptions.
/// Provides publish-subscribe functionality for decoupled communication.
///
public static class OM_EventsManager
{
#if UNITY_EDITOR
///
/// Invoked in the Editor whenever an event is published (typed or named).
/// Used for debugging and visualization purposes.
///
public static Action OnEventPublished;
#endif
private static readonly Dictionary TypedEvents = new();
private static readonly Dictionary SimpleNamedEvents = new();
private static readonly Dictionary GenericNamedEvents = new();
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void ResetStatics()
{
TypedEvents.Clear();
SimpleNamedEvents.Clear();
GenericNamedEvents.Clear();
}
///
/// Subscribes a callback to a typed event.
///
public static void Subscribe(Action callback)
{
if (TypedEvents.TryGetValue(typeof(T), out var del))
TypedEvents[typeof(T)] = Delegate.Combine(del, callback);
else
TypedEvents[typeof(T)] = callback;
}
///
/// Unsubscribes a callback from a typed event.
///
public static void Unsubscribe(Action callback)
{
if (TypedEvents.TryGetValue(typeof(T), out var del))
{
var newDel = Delegate.Remove(del, callback);
if (newDel == null)
TypedEvents.Remove(typeof(T));
else
TypedEvents[typeof(T)] = newDel;
}
}
///
/// Subscribes a callback to a named event with no parameters.
///
public static void Subscribe(string key, Action callback)
{
if (SimpleNamedEvents.TryGetValue(key, out var existing))
SimpleNamedEvents[key] = (Action)Delegate.Combine(existing, callback);
else
SimpleNamedEvents[key] = callback;
}
///
/// Subscribes a callback to a named event with a parameter.
///
public static void Subscribe(string key, Action callback)
{
if (GenericNamedEvents.TryGetValue(key, out var existing))
{
if (existing is not Action)
throw new InvalidOperationException($"Key '{key}' is already registered with a different parameter type.");
GenericNamedEvents[key] = Delegate.Combine(existing, callback);
}
else
{
GenericNamedEvents[key] = callback;
}
}
///
/// Unsubscribes a callback from a named event with no parameters.
///
public static void Unsubscribe(string key, Action callback)
{
if (SimpleNamedEvents.TryGetValue(key, out var existing))
{
var updated = Delegate.Remove(existing, callback);
if (updated == null)
SimpleNamedEvents.Remove(key);
else
SimpleNamedEvents[key] = (Action)updated;
}
}
///
/// Unsubscribes a callback from a named event with parameters.
///
public static void Unsubscribe(string key, Action callback)
{
if (GenericNamedEvents.TryGetValue(key, out var existing))
{
if (existing is not Action)
throw new InvalidOperationException($"Key '{key}' is not registered with Action<{typeof(T).Name}>.");
var updated = Delegate.Remove(existing, callback);
if (updated == null)
GenericNamedEvents.Remove(key);
else
GenericNamedEvents[key] = updated;
}
}
///
/// Publishes a typed event.
///
public static void Publish(T data)
{
if (TypedEvents.TryGetValue(typeof(T), out var del))
(del as Action)?.Invoke(data);
#if UNITY_EDITOR
OnEventPublished?.Invoke(typeof(T).Name);
#endif
}
///
/// Publishes a named event with no parameters.
///
public static void Publish(string key)
{
if (SimpleNamedEvents.TryGetValue(key, out var action))
action?.Invoke();
#if UNITY_EDITOR
OnEventPublished?.Invoke(key);
#endif
}
///
/// Publishes a named event with a parameter. Optionally also triggers any matching simple event.
///
public static void Publish(string key, T arg, bool triggerSimpleEventsIfExists = true)
{
if (GenericNamedEvents.TryGetValue(key, out var del) && del is Action action)
{
action(arg);
}
if (triggerSimpleEventsIfExists && SimpleNamedEvents.TryGetValue(key, out var simpleAction))
{
simpleAction?.Invoke();
}
#if UNITY_EDITOR
OnEventPublished?.Invoke(key);
#endif
}
///
/// Clears all stored events of all types.
///
public static void ClearAllEvents()
{
TypedEvents.Clear();
SimpleNamedEvents.Clear();
GenericNamedEvents.Clear();
}
///
/// Removes all listeners associated with a named key (both simple and generic).
///
public static void UnsubscribeAll(string key)
{
SimpleNamedEvents.Remove(key);
GenericNamedEvents.Remove(key);
}
///
/// Clears a specific typed event by its type.
///
public static void ClearTypedEvent() => TypedEvents.Remove(typeof(T));
///
/// Clears all named event listeners associated with the given key.
///
public static void ClearNamedEvent(string key)
{
SimpleNamedEvents.Remove(key);
GenericNamedEvents.Remove(key);
}
///
/// Checks if a typed event has listeners.
///
public static bool HasTypedEvent() => TypedEvents.ContainsKey(typeof(T));
///
/// Checks if a named event has listeners.
///
public static bool HasNamedEvent(string key)
{
return SimpleNamedEvents.ContainsKey(key) || GenericNamedEvents.ContainsKey(key);
}
///
/// Logs all subscribed events and their listener counts to the console.
///
public static void LogAllEvents()
{
Debug.Log("=== EventManager Subscriptions ===");
Debug.Log($"[Typed] Count: {TypedEvents.Count}");
foreach (var e in TypedEvents)
Debug.Log($" Type: {e.Key.Name} → Listeners: {e.Value?.GetInvocationList().Length}");
Debug.Log($"[Named] Count: {SimpleNamedEvents.Count}");
foreach (var e in SimpleNamedEvents)
Debug.Log($" Key: \"{e.Key}\" → Listeners: {e.Value?.GetInvocationList().Length}");
Debug.Log($"[Generic Named] Count: {GenericNamedEvents.Count}");
foreach (var e in GenericNamedEvents)
Debug.Log($" Key: \"{e.Key}\" → Listeners: {e.Value?.GetInvocationList().Length}");
}
#if UNITY_EDITOR
///
/// Returns all typed events for editor debugging.
///
public static Dictionary Debug_GetTypedEvents() => TypedEvents;
///
/// Returns all named events (simple + generic) for editor debugging.
///
public static Dictionary Debug_GetNamedEvents()
{
var combined = new Dictionary();
foreach (var pair in SimpleNamedEvents)
combined[pair.Key] = pair.Value;
foreach (var pair in GenericNamedEvents)
combined[pair.Key] = pair.Value;
return combined;
}
///
/// Clears a specific typed event by its System.Type.
///
public static void ClearTypedEventByType(Type type) => TypedEvents.Remove(type);
#endif
}
}