# Events

Events are messages sent used to notify about state changes of an `Entity`.

These events can be consumed in two different ways.

* Process events directly by an event handler subscribed to an event like `entity.OnComponentChanged`.
* Or by recording all events using an [EventRecorder](#eventrecorder) and process recorded events later within a `Query`.

**🚀 New in v3.5.0**\
Introduced a new pattern to process events on adding/removing components or tags concise in **O(1)**.\
See: [Map components to enums](#map-components-to-enums) and [Map tags to enums](#map-tags-to-enums).

## Entity changes

If changing an entity by adding or removing components, tags, scripts or child entities events are emitted.\
An application can subscribe to these events like shown in the example.\
Emitting these type of events increase code decoupling.\
Without events these modifications need to be notified by direct method calls.\
The *build-in* events can be subscribed on `EntityStore` and on `Entity` level like shown in the example below.

```csharp
public static void AddEventHandlers()
{
    var store   = new EntityStore();
    var entity  = store.CreateEntity();
    entity.OnComponentChanged     += ev => { Console.WriteLine(ev); }; // > entity: 1 - event > Add Component: [MyComponent]
    entity.OnTagsChanged          += ev => { Console.WriteLine(ev); }; // > entity: 1 - event > Add Tags: [#MyTag1]
    entity.OnScriptChanged        += ev => { Console.WriteLine(ev); }; // > entity: 1 - event > Add Script: [*MyScript]
    entity.OnChildEntitiesChanged += ev => { Console.WriteLine(ev); }; // > entity: 1 - event > Add Child[0] = 2

    entity.AddComponent(new MyComponent());
    entity.AddTag<MyTag1>();
    entity.AddScript(new MyScript());
    entity.AddChild(store.CreateEntity());
}
```

### Component events

Event handlers for component changes notify one of the following `ev.Action`

* `Add` - a component was newly added to the entity.
* `Update` - the component value changed.
* `Remove` - the component was removed.

In case of `Update` or `Remove` the handler provide access to the old component value.

```csharp
public static void ComponentEvents()
{
    var store  = new EntityStore();
    var entity = store.CreateEntity();
    entity.OnComponentChanged += ev =>
    {
        if (ev.Type == typeof(EntityName)) {
            string log = ev.Action switch
            {
                Add    => $"new: {ev.Component<EntityName>()}",
                Update => $"new: {ev.Component<EntityName>()}  old: {ev.OldComponent<EntityName>()}",
                Remove => $"old: {ev.OldComponent<EntityName>()}",
                _      => null
            };
            Console.WriteLine($"entity: {ev.Entity.Id} - {ev.Action} {log}");
        }
    };
    entity.AddComponent(new EntityName("Peter"));
    entity.AddComponent(new EntityName("Paul"));
    entity.RemoveComponent<EntityName>();
}
```

Log Output

```js
entity: 1 - Add new: 'Peter'
entity: 1 - Update new: 'Paul'  old: 'Peter'
entity: 1 - Remove old: 'Paul'
```

### Tag events

Event handlers for tag changes notify which entity tags are changed.

* `AddedTags` - tags added to an entity.
* `RemovedTags` - tags removed from an entity.
* `ChangedTags` - tags added or removed from an entity.

```csharp
public struct MyTag1 : ITag { }
public struct MyTag2 : ITag { }

public static void TagEvents()
{
    var store  = new EntityStore();
    var entity = store.CreateEntity();
    entity.OnTagsChanged += ev =>
    {
        string log = "";
        if (ev.AddedTags.  Has<MyTag1>()) { log += " added:   MyTag1"; }
        if (ev.RemovedTags.Has<MyTag1>()) { log += " removed: MyTag1"; }
        
        if (ev.AddedTags.  Has<MyTag2>()) { log += " added:   MyTag2"; }
        if (ev.RemovedTags.Has<MyTag2>()) { log += " removed: MyTag2"; }
        
        Console.WriteLine($"entity: {entity.Id} -{log}");
    };
    entity.AddTag<MyTag1>();
    entity.RemoveTag<MyTag1>();
    entity.AddTags(Tags.Get<MyTag1, MyTag2>());
}
```

Log Output

```js
entity: 1 - added:   MyTag1
entity: 1 - removed: MyTag1
entity: 1 - added:   MyTag1 added:   MyTag2
```

<br>

## Map components to enums

**🚀 New feature in v3.5.0**

Component types can now be mapped to `enum` ids with the `[MapComponent<>]` attribute.\
The mapped id can be retrieved with `ComponentType.AsEnum<>()`.

This is especially useful in event handlers like `store.OnComponentAdded` or `store.OnComponentRemoved`.\
Before v3.5.0 a chain of `if`, `else if`, ... branches were required to execute code specific for a component type.\
This pattern had two downsides:

* The CPU had to execute multiple branches slowing down performance.
* It is easy to forget a curly bracket and the execution is not as intended.

```cs
store.OnComponentAdded += (componentChanged) =>
{
    var type = componentChanged.ComponentType.Type;
    if      (type == typeof(Burning))  { ShowFlameParticles(componentChanged.Entity); }
    else if (type == typeof(Frozen))   { ShowIceOverlay(componentChanged.Entity); }
    else if (type == typeof(Poisoned)) { ShowPoisonIcon(componentChanged.Entity); }
    else if (type == typeof(Stunned))  { ShowStunStars(componentChanged.Entity); }
};
```

With v3.5.0 component types mapped to enum ids can be used in switch statements or expressions.\
So a chain of `if`, `else if`, ... branches converts to a single switch statement.\
The compiler can now create a fast jump table which enables direct branching to specific code.\
The new pattern also enables to check that a switch statement is *exhaustive* by the compiler.

With this new pattern an `enum` needs to be declared which maps component types to enum ids.

```cs
public enum StatusEffect
{
    None = 0,
    [MapComponent<Burning>]   Burning,
    [MapComponent<Frozen>]    Frozen,
    [MapComponent<Poisoned>]  Poisoned,
    [MapComponent<Stunned>]   Stunned,
}
```

The event handler code will now convert to

```cs
store.OnComponentAdded += (componentChanged) =>
{
    switch (componentChanged.ComponentType.AsEnum<StatusEffect>())
    {
        case StatusEffect.Burning:  ShowFlameParticles(componentChanged.Entity);  break;
        case StatusEffect.Frozen:   ShowIceOverlay(componentChanged.Entity);      break;
        case StatusEffect.Poisoned: ShowPoisonIcon(componentChanged.Entity);      break;
        case StatusEffect.Stunned:  ShowStunStars(componentChanged.Entity);       break;
    }
};
```

`ComponentType.AsEnum<>()` executes in O(1) - a simple array index lookup.

<br>

## Map tags to enums

**🚀 New feature in v3.5.0**

For tag types this feature is similar to component types.

Tag types are mapped to `enum` ids with the `[MapTag<>]` attribute.\
The mapped id can be retrieved with `TagType.AsEnum<>()`.

This is especially useful in the event handler `store.OnTagsChanged` accessing the properties `TagsChanged.AddedTags` or `TagsChanged.RemovedTags`. Typically both properties contain none or only a single `TagType`.\
Before v3.5.0 a chain of `if`, `else if`, ... branches were required to execute code for added or removed tags.\
This pattern had two downsides:

* The CPU had to execute multiple branches slowing down performance.
* It is easy to forget a curly bracket and the execution is not as intended.

```cs
store.OnTagsChanged += (tagsChanged) =>
{
    var addedTags = tagsChanged.AddedTags;    
    if      (addedTags.Has<Burning>)  { ShowFlameParticles(componentChanged.Entity); }
    else if (addedTags.Has<Frozen>)   { ShowIceOverlay(componentChanged.Entity); }
    else if (addedTags.Has<Poisoned>) { ShowPoisonIcon(componentChanged.Entity); }
    else if (addedTags.Has<Stunned>)  { ShowStunStars(componentChanged.Entity); }    
};
```

With v3.5.0 tag types mapped to enum ids can be used in switch statements or expressions.\
So a chain of `if`, `else if`, ... branches converts to a single switch statement.\
The compiler can now create a fast jump table which enables direct branching to specific code.\
The new pattern also enables to check that a switch statement is *exhaustive* by the compiler.

With this new pattern an `enum` needs to be declared which maps tag types to enum ids.

```cs
public enum EffectTags
{
    None = 0,
    [MapTag<Burning>]   Burning,
    [MapTag<Frozen>]    Frozen,
    [MapTag<Poisoned>]  Poisoned,
    [MapTag<Stunned>]   Stunned,
}
```

The event handler code will now convert to

```cs
store.OnTagsChanged += (tagsChanged) =>
{
    foreach (var addedTag in tagsChanged.AddedTags) {
        switch (addedTag.AsEnum<EffectTags>())
        {
            case EffectTags.Burning:  ShowFlameParticles(componentChanged.Entity);  break;
            case EffectTags.Frozen:   ShowIceOverlay(componentChanged.Entity);      break;
            case EffectTags.Poisoned: ShowPoisonIcon(componentChanged.Entity);      break;
            case EffectTags.Stunned:  ShowStunStars(componentChanged.Entity);       break;
        }
    }
};
```

`TagType.AsEnum<>()` executes in O(1) - a simple array index lookup.

<br>

## EventRecorder

An `EventRecorder` record all component and tag changes of an `EntityStore` when `Enabled`.\
A recorder is required for queries using `EventFilter`'s.\
To clear all recorded events use `store.EventRecorder.ClearEvents()`.\
In a game loop this is typically performed at the beginning of a new frame.\
To stop recording events entirely use `store.EventRecorder.Enabled = false`.

### EventFilter

The intended use-case for `EventFilter`'s are queries.\
When iterating the entities of a query result it can be checked if an entity was changed by an operation matching the specified filters.\
E.g a specific component or tag was added.

```cs
    query.EventFilter.ComponentAdded<Position>();
    query.EventFilter.TagAdded<MyTag1>();
```

`EventFilter`'s can be used on its own or within a query, see the examples below.\
All events that need to be filtered - like added/removed components/tags - can be added to the `EventFilter`.

```csharp
public static void FilterEntityEvents()
{
    var store   = new EntityStore();
    store.EventRecorder.Enabled = true; // required for EventFilter
    
    var entity1 = store.CreateEntity(1);
    var entity2 = store.CreateEntity(2);
    var entity3 = store.CreateEntity(3);
    
    entity1.AddComponent(new Position());
    entity2.AddTag<MyTag1>();
    
    var query = store.Query();
    query.EventFilter.ComponentAdded<Position>();
    query.EventFilter.TagAdded<MyTag1>();
    
    foreach (var entity in store.Entities) {
        bool hasEvent = query.HasEvent(entity.Id);
        Console.WriteLine($"{entity,-20} hasEvent: {hasEvent}");
    }
    // id: 3  []            hasEvent: False
    // id: 1  [Position]    hasEvent: True
    // id: 2  [#MyTag1]     hasEvent: True
    
    store.EventRecorder.ClearEvents(); // typically called on new frame 
    foreach (var entity in store.Entities) {
        bool hasEvent = query.HasEvent(entity.Id);
        Console.WriteLine($"{entity,-20} hasEvent: {hasEvent}");
    }
    // id: 3  []            hasEvent: False
    // id: 1  [Position]    hasEvent: False
    // id: 2  [#MyTag1]     hasEvent: False
}
```

<br>

## Signals

**Signals** are similar to events.\
They are used to **send** and **subscribe** **custom events** on entity level in an application.\
To prevent heap allocations signal types must be structs.

The use of signals is intended for scenarios when something happens occasionally.\
This avoids the need to check a state every frame to detect a specific condition.\
For example signals could be used to react on collisions between entities.

Signal handlers can be added as shown below and removed if needed.

```csharp
    var handler = entity.AddSignalHandler<MyEvent2>(signal => { ... });
    entity.RemoveSignalHandler(handler);
```

Multiple signal handlers can be added to an entity and are automatically removed if the entity is deleted.

```csharp
public struct CollisionSignal {
    public Entity other;
}

public static void AddSignalHandler()
{
    var store  = new EntityStore();
    var player = store.CreateEntity(1);
    player.AddSignalHandler<CollisionSignal>(signal => {
        Console.WriteLine($"collision signal - entity: {signal.Entity.Id} other: {signal.Event.other.Id}");
    });
    var npc = store.CreateEntity(2);
    // ... detect collision. e.g. with a collision system. In case of collision:
    player.EmitSignal(new CollisionSignal{ other = npc });
}
```

Log Output

```js
collision signal - entity: 1 other: 2
```

> **Note** Avoid emitting signals inside a query loop.

When using a query loop to detect collisions signals should not be emitted directly.\
The the event handler may perform a structural change - e.g removing or adding a components.\
Doing this will invalidate the query loop.\
To avoid this detected collisions can be stored inside the query loop in a `List<Event, CollisionSignal>`.\
After the collision loop finishes collision events can be emitted and are allowed to perform structural changes.

<br>
