private void AddHandlerToMap(Type handlerType, IEventSystemHandler handler)
        {
            bool isParentObjectRegistered = false;

            var componentHandler = handler as Component;

            if (componentHandler != null && EventListeners.Contains(componentHandler.gameObject))
            {
                isParentObjectRegistered = true;
                WarnAboutConflictingApis(componentHandler.gameObject.name);
            }

            List <EventHandlerEntry> handlers;

            if (!EventHandlersByType.TryGetValue(handlerType, out handlers))
            {
                handlers = new List <EventHandlerEntry> {
                    new EventHandlerEntry(handler, isParentObjectRegistered)
                };
                EventHandlersByType.Add(handlerType, handlers);
                return;
            }

            bool handlerExists = false;

            for (int i = handlers.Count - 1; i >= 0; i--)
            {
                if (handlers[i].handler == handler)
                {
                    handlerExists = true;
                    break;
                }
            }

            if (!handlerExists)
            {
                handlers.Add(new EventHandlerEntry(handler, isParentObjectRegistered));
            }
        }
        /// <inheritdoc />
        private void RemoveHandlerFromMap(Type handlerType, IEventSystemHandler handler)
        {
            List <EventHandlerEntry> handlers;

            if (!EventHandlersByType.TryGetValue(handlerType, out handlers))
            {
                return;
            }

            for (int i = handlers.Count - 1; i >= 0; i--)
            {
                if (handlers[i].handler == handler)
                {
                    handlers.RemoveAt(i);
                }
            }

            if (handlers.Count == 0)
            {
                EventHandlersByType.Remove(handlerType);
            }
        }
        /// <inheritdoc />
        public virtual void HandleEvent <T>(BaseEventData eventData, ExecuteEvents.EventFunction <T> eventHandler) where T : IEventSystemHandler
        {
            Debug.Assert(!eventData.used);

            eventExecutionDepth++;

            // This sends the event to every component that implements the corresponding event handling interface,
            // regardless of whether it was the one registering the object as global listener or not.
            // This behavior is kept for backwards compatibility. Will be removed together with the IMixedRealityEventSystem.Register(GameObject listener) interface.
            for (int i = EventListeners.Count - 1; i >= 0; i--)
            {
                ExecuteEvents.Execute(EventListeners[i], eventData, eventHandler);
            }

            // Send events to all handlers registered via RegisterHandler API.
            List <EventHandlerEntry> handlers;

            if (EventHandlersByType.TryGetValue(typeof(T), out handlers))
            {
                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    var handlerEntry = handlers[i];

                    // If handler's parent is in object collection (traversed above), it has already received an event.
                    if (handlerEntry.parentObjectIsInObjectCollection)
                    {
                        continue;
                    }

                    eventHandler.Invoke((T)handlerEntry.handler, eventData);
                }
            }

            eventExecutionDepth--;

            if (eventExecutionDepth == 0 && (postponedActions.Count > 0 || postponedObjectActions.Count > 0))
            {
                foreach (var handler in postponedActions)
                {
                    if (handler.Item1 == Action.Add)
                    {
                        AddHandlerToMap(handler.Item2, handler.Item3);
                    }
                    else if (handler.Item1 == Action.Remove)
                    {
                        RemoveHandlerFromMap(handler.Item2, handler.Item3);
                    }
                }

                foreach (var obj in postponedObjectActions)
                {
                    if (obj.Item1 == Action.Add)
                    {
                        // Can call it here, because guaranteed that eventExecutionDepth is 0
                        Register(obj.Item2);
                    }
                    else if (obj.Item1 == Action.Remove)
                    {
                        Unregister(obj.Item2);
                    }
                }

                postponedActions.Clear();
                postponedObjectActions.Clear();
            }
        }
        /// <inheritdoc />
        public virtual void HandleEvent <T>(BaseEventData eventData, ExecuteEvents.EventFunction <T> eventHandler) where T : IEventSystemHandler
        {
            Debug.Assert(!eventData.used);

            eventExecutionDepth++;

            // This sends the event to every component that implements the corresponding event handling interface,
            // regardless of whether it was the one registering the object as global listener or not.
            // This behavior is kept for backwards compatibility. Will be removed together with the IMixedRealityEventSystem.Register(GameObject listener) interface.
            for (int i = EventListeners.Count - 1; i >= 0; i--)
            {
                // Ensure client code does not put the event dispatch system into a bad state.
                // Note that ExecuteEvents.Execute internally safeguards against exceptions, but
                // this is another layer to ensure that nothing below this layer can affect the state
                // of our eventExecutionDepth tracker.
                try
                {
                    ExecuteEvents.Execute(EventListeners[i], eventData, eventHandler);
                }
                catch (Exception ex)
                {
                    Debug.LogException(ex);
                }
            }

            // Send events to all handlers registered via RegisterHandler API.
            if (EventHandlersByType.TryGetValue(typeof(T), out List <EventHandlerEntry> handlers))
            {
                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    var handlerEntry = handlers[i];

                    // If handler's parent is in object collection (traversed above), it has already received an event.
                    if (handlerEntry.parentObjectIsInObjectCollection)
                    {
                        continue;
                    }

                    // Ensure client code does not put the event dispatch system into a bad state.
                    try
                    {
                        eventHandler.Invoke((T)handlerEntry.handler, eventData);
                    }
                    catch (Exception ex)
                    {
                        Debug.LogException(ex);
                    }
                }
            }

            eventExecutionDepth--;

            if (eventExecutionDepth == 0)
            {
                int postponedActionsCount       = postponedActions.Count;
                int postponedObjectActionsCount = postponedObjectActions.Count;

                if (postponedActionsCount <= 0 && postponedObjectActionsCount <= 0)
                {
                    return;
                }

                for (int i = 0; i < postponedActionsCount; i++)
                {
                    Tuple <Action, Type, IEventSystemHandler> handler = postponedActions[i];
                    if (handler.Item1 == Action.Add)
                    {
                        AddHandlerToMap(handler.Item2, handler.Item3);
                    }
                    else if (handler.Item1 == Action.Remove)
                    {
                        RemoveHandlerFromMap(handler.Item2, handler.Item3);
                    }
                }

                for (int i = 0; i < postponedObjectActionsCount; i++)
                {
                    Tuple <Action, GameObject> obj = postponedObjectActions[i];
                    if (obj.Item1 == Action.Add)
                    {
                        // Can call it here, because guaranteed that eventExecutionDepth is 0
                        Register(obj.Item2);
                    }
                    else if (obj.Item1 == Action.Remove)
                    {
                        Unregister(obj.Item2);
                    }
                }

                postponedActions.Clear();
                postponedObjectActions.Clear();
            }
        }