using System;
using System.Collections.Generic;
using UnityEngine;
namespace Unity.VisualScripting
{
/// <summary>
/// A global container that maps Event names and Component references to actions for registered listeners.
/// </summary>
/// <remarks>
/// <para>It is the EventMachine base class for ScriptMachine and StateMachine that triggers events.
/// This overrides almost all Unity callbacks (such as Awake, OnEnable, Update, etc.) and triggers an event
/// on the EventBus.</para>
/// </remarks>
/// <example>
/// <para>The following example shows how to use the EventBus to send a custom event from a script to a node in
/// a graph. It also shows how to use the EventBus as a global event manager by executing a callback in a
/// script, not just a node.
///
/// For more information on how to create custom event nodes refer to the
/// <a href="../manual/vs-create-own-custom-event-node.html">User Manual</a>.
///
/// In this example we've added some code to a GameObject. This code checks for when the user presses a sequence of
/// keys to enable a cheat code, then triggers the <c>CheatCodeActivated</c> event. We register the
/// <c>CheatCodeActivated</c> event in the <c>Start</c> method. The <c>Update</c> method triggers the event twice
/// with 2 different targets: one for the <c>CheatCodeActivated</c> callback and the other to trigger the
/// CheatCodeEnabled Node.</para>
///
/// <code source="../../../DocCodeExamples/EventBusExamples.cs" region="CheatCodeController" title="CheatCodeController"/>
///
/// <para>The CheatCodeEnabled Node:</para>
///
/// <code source="../../../DocCodeExamples/EventBusExamples.cs" region="CheatCodeEnabled" title="CheatCodeEnabled"/>
/// </example>
public static class EventBus
{
static EventBus()
{
events = new Dictionary<EventHook, HashSet<Delegate>>(new EventHookComparer());
}
private static readonly Dictionary<EventHook, HashSet<Delegate>> events;
internal static Dictionary<EventHook, HashSet<Delegate>> testAccessEvents => events;
public static void Register<TArgs>(EventHook hook, Action<TArgs> handler)
{
if (!events.TryGetValue(hook, out var handlers))
{
handlers = new HashSet<Delegate>();
events.Add(hook, handlers);
}
handlers.Add(handler);
}
public static void Unregister(EventHook hook, Delegate handler)
{
if (events.TryGetValue(hook, out var handlers))
{
if (handlers.Remove(handler))
{
// Free the key references for GC collection
if (handlers.Count == 0)
{
events.Remove(hook);
}
}
}
}
public static void Trigger<TArgs>(EventHook hook, TArgs args)
{
HashSet<Action<TArgs>> handlers = null;
if (events.TryGetValue(hook, out var potentialHandlers))
{
foreach (var potentialHandler in potentialHandlers)
{
if (potentialHandler is Action<TArgs> handler)
{
if (handlers == null)
{
handlers = HashSetPool<Action<TArgs>>.New();
}
handlers.Add(handler);
}
}
}
if (handlers != null)
{
foreach (var handler in handlers)
{
if (!potentialHandlers.Contains(handler))
{
continue;
}
handler.Invoke(args);
}
handlers.Free();
}
}
public static void Trigger<TArgs>(string name, GameObject target, TArgs args)
{
Trigger(new EventHook(name, target), args);
}
public static void Trigger(EventHook hook)
{
Trigger(hook, new EmptyEventArgs());
}
public static void Trigger(string name, GameObject target)
{
Trigger(new EventHook(name, target));
}
}
}