public static IDisposable OnAction(this object target, Action <ISerializedActionCall> listener, bool attachAfter = true)
        {
            if (!target.IsStateTreeNode())
            {
                throw new InvalidOperationException("Can not listen for action on Non State Tree Node");
            }

            void FireListener(IMiddlewareEvent call)
            {
                if (call.Type == MiddlewareEventType.Action && call.Id == call.RootId)
                {
                    var source = call.Context.GetStateTreeNode();

                    var data = new SerializedActionCall
                    {
                        Name = call.Name,

                        Path = StateTreeUtils.GetRelativePathBetweenNodes(target.GetStateTreeNode(), source),

                        //TODO: serialize arguments
                        Arguments = call.Arguments.ToArray()
                    };

                    listener(data);
                }
            }

            Action <IMiddlewareEvent, Action <IMiddlewareEvent, Func <object, object> >, Action <object> > onAfterAction = (IMiddlewareEvent call, Action <IMiddlewareEvent, Func <object, object> > next, Action <object> action) =>
            {
                next(call, null);

                FireListener(call);
            };

            Action <IMiddlewareEvent, Action <IMiddlewareEvent, Func <object, object> >, Action <object> > onBeforeAction = (IMiddlewareEvent call, Action <IMiddlewareEvent, Func <object, object> > next, Action <object> action) =>
            {
                FireListener(call);

                next(call, null);
            };

            return(StateTreeAction.AddMiddleware(target.GetStateTree(), attachAfter ? onAfterAction : onBeforeAction));
        }
 public static string GetRelativePath(this object target, object relative)
 {
     return(StateTreeUtils.GetRelativePathBetweenNodes(target.GetStateTreeNode(), relative.GetStateTreeNode()));
 }