using System;
using System.Linq;
using UnityEditor;
namespace Unity.Play.Publisher.Editor
{
    /// 
    /// Defines the structure of a method that can be called when an action is dispatched
    /// 
    /// The action to dispatch
    /// 
    public delegate object Dispatcher(object action);
    /// 
    /// Defines the structure of a method that can be called when the State reducer
    /// 
    /// 
    /// 
    /// 
    /// 
    public delegate State Reducer(State previousState, object action);
    /// 
    /// Defines the structure of a method that can be called in order to alter the state of the application
    /// 
    /// 
    /// 
    /// 
    public delegate Func Middleware(Store store);
    /// 
    /// Defines the structure of a method that can be called when the state of an object changes
    /// 
    /// 
    /// 
    public delegate void StateChangedHandler(State action);
    /// 
    /// Manages the communication between all the application components
    /// 
    /// 
    public class Store
    {
        /// 
        /// Delegate that reacts on state change
        /// 
        public StateChangedHandler stateChanged;
        State _state;
        readonly Dispatcher _dispatcher;
        readonly Reducer _reducer;
        /// 
        /// Initializes and returns an instance of Store
        /// 
        /// 
        /// 
        /// 
        public Store(
            Reducer reducer, State initialState = default(State),
            params Middleware[] middlewares)
        {
            this._reducer = reducer;
            this._dispatcher = this.ApplyMiddlewares(middlewares);
            this._state = initialState;
        }
        /// 
        /// Dispatches an action
        /// 
        /// 
        /// Returns an object affected by the action
        public object Dispatch(object action)
        {
            return this._dispatcher(action);
        }
        /// 
        /// The state
        /// 
        public State state
        {
            get { return this._state; }
        }
        Dispatcher ApplyMiddlewares(params Middleware[] middlewares)
        {
            return middlewares.Reverse().Aggregate, Dispatcher>(this.InnerDispatch,
                (current, middleware) => middleware(this)(current));
        }
        object InnerDispatch(object action)
        {
            this._state = this._reducer(this._state, action);
            if (this.stateChanged != null)
            {
                this.stateChanged(this._state);
            }
            return action;
        }
    }
}