// Path: redux/src/main/java/com/conversationkit/redux/Action.java
// @FunctionalInterface
// public interface Action {
//     public String getType();
// }
// 
// Path: redux/src/main/java/com/conversationkit/redux/Middleware.java
// @FunctionalInterface
// /**
//  * A middleware is a higher-order function that composes a {@link Dispatcher} function to return a new dispatch function. It often turns async actions into actions.
//  * @see <a href="https://redux.js.org/glossary#middleware">https://redux.js.org/glossary#middleware</a>
//  */
// public interface Middleware<S> {
// 
//     void dispatch(Store<S> store, Object action, Middleware<S> next);
// }
// 
// Path: redux/src/main/java/com/conversationkit/redux/Store.java
// public final class Store<S> implements Dispatcher {
// 
//     private static final Logger logger = Logger.getLogger(Store.class.getName());
// 
//     private Map currentState;
// 
//     private final Reducer reducer;
//     private ActionDispatcher dispatcher;
//     private final Function<Map,S> typedStateBuilder;
//     private final Map<UUID, Consumer<Map<String, Object>>> consumers = new HashMap<>();
// 
//     @FunctionalInterface
//     public interface ActionDispatcher {
// 
//         public void dispatch(Object action);
// 
//     }
// 
//     protected Store(Reducer reducer, Map initialState, Function<Map,S> stateBuilder, Middleware... middlewares) {
//         this.reducer = reducer;
//         this.currentState = initialState;
//         this.typedStateBuilder = stateBuilder;
// 
//         List<Middleware> allMiddlewares = new ArrayList();
//         
//         for (Middleware mw : middlewares) {
//             allMiddlewares.add(mw);
//         }
//         //native middleware, last middleware in chain
//         allMiddlewares.add((store, action, next) -> {
//             Map nextState;
//             synchronized (Store.this) {
//                 logger.fine(String.format("[REDUX] reducing action: %s", action.toString()));
//                 if (!(action instanceof Action)) {
//                     throw new RuntimeException("The action must be an instance of Action by the time it is received by the reducer. Action is " + action.getClass().getName());
//                 }
//                 Action a = (Action) action;
//                 nextState = store.reducer.reduce(a, currentState);
//             }
//             if (!nextState.equals(currentState)) {
//                 logger.fine(String.format("[REDUX] state has changed after %s", action.toString()));
//                 currentState = nextState;
//                 consumers.values().parallelStream().forEach(e -> e.accept(currentState));
//             } else {
//                 logger.fine(String.format("[REDUX] state has not changed after %s", action.toString()));
//             }
//         });
// 
//         logger.info(String.format("[REDUX] initializing redux store with %d middleware(s).", (allMiddlewares.size() - 1)));
// 
//         for (int i = allMiddlewares.size() - 1; i >= 0; i--) {
//             final Middleware mw = allMiddlewares.get(i);
//             logger.fine(String.format("[REDUX] chaining middleware (%d)", i));
//             //this will be null for the native middleware only, which is last
//             final Middleware next = (i == allMiddlewares.size() - 1 ? null : allMiddlewares.get(i + 1));
//             this.dispatcher = (action) -> {
//                 mw.dispatch(Store.this, action, next);
//             };
// 
//         }
//     }
// 
//     @Override
//     public S dispatch(Object action) {
//         logger.fine(String.format("[REDUX] dispatching action: %s", action.toString()));
// 
//         dispatcher.dispatch(action);
//         logger.finer(String.format("[REDUX] reduced state: %s", getState().toString()));
//         return getState();
//     }
// 
//     public S getState() {
//         return typedStateBuilder.apply(currentState);
//     }
// 
//     public UUID subscribe(Consumer<Map<String, Object>> subscriber) {
//         UUID uuid = UUID.randomUUID();
//         this.consumers.put(uuid, subscriber);
// 
//         return uuid;
//     }
// 
//     public void unsubscribe(UUID uuid) {
// 
//         this.consumers.remove(uuid);
//     }
// }


import com.conversationkit.redux.Action;
import com.conversationkit.redux.Middleware;
import com.conversationkit.redux.Store;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
/*
 * The MIT License
 *
 * Copyright 2019 Synclab Consulting LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.conversationkit.redux.impl;


/**
 * Redux middleware that handles an async {@link Action} of type {@link Future} 
 * @author pdtyreus
 */
public class CompletableFutureMiddleware implements Middleware {

    private static final Logger logger = Logger.getLogger(CompletableFutureMiddleware.class.getName());
    
    @Override
    public void dispatch(Store store, Object action, Middleware next) {
        if (action instanceof Future) {
            logger.log(Level.FINE, "middleware handling future action {0}", action.toString());
            Future f = (Future) action;
            try {
// Path: redux/src/main/java/com/conversationkit/redux/Action.java
// @FunctionalInterface
// public interface Action {
//     public String getType();
// }
// 
// Path: redux/src/main/java/com/conversationkit/redux/Middleware.java
// @FunctionalInterface
// /**
//  * A middleware is a higher-order function that composes a {@link Dispatcher} function to return a new dispatch function. It often turns async actions into actions.
//  * @see <a href="https://redux.js.org/glossary#middleware">https://redux.js.org/glossary#middleware</a>
//  */
// public interface Middleware<S> {
// 
//     void dispatch(Store<S> store, Object action, Middleware<S> next);
// }
// 
// Path: redux/src/main/java/com/conversationkit/redux/Store.java
// public final class Store<S> implements Dispatcher {
// 
//     private static final Logger logger = Logger.getLogger(Store.class.getName());
// 
//     private Map currentState;
// 
//     private final Reducer reducer;
//     private ActionDispatcher dispatcher;
//     private final Function<Map,S> typedStateBuilder;
//     private final Map<UUID, Consumer<Map<String, Object>>> consumers = new HashMap<>();
// 
//     @FunctionalInterface
//     public interface ActionDispatcher {
// 
//         public void dispatch(Object action);
// 
//     }
// 
//     protected Store(Reducer reducer, Map initialState, Function<Map,S> stateBuilder, Middleware... middlewares) {
//         this.reducer = reducer;
//         this.currentState = initialState;
//         this.typedStateBuilder = stateBuilder;
// 
//         List<Middleware> allMiddlewares = new ArrayList();
//         
//         for (Middleware mw : middlewares) {
//             allMiddlewares.add(mw);
//         }
//         //native middleware, last middleware in chain
//         allMiddlewares.add((store, action, next) -> {
//             Map nextState;
//             synchronized (Store.this) {
//                 logger.fine(String.format("[REDUX] reducing action: %s", action.toString()));
//                 if (!(action instanceof Action)) {
//                     throw new RuntimeException("The action must be an instance of Action by the time it is received by the reducer. Action is " + action.getClass().getName());
//                 }
//                 Action a = (Action) action;
//                 nextState = store.reducer.reduce(a, currentState);
//             }
//             if (!nextState.equals(currentState)) {
//                 logger.fine(String.format("[REDUX] state has changed after %s", action.toString()));
//                 currentState = nextState;
//                 consumers.values().parallelStream().forEach(e -> e.accept(currentState));
//             } else {
//                 logger.fine(String.format("[REDUX] state has not changed after %s", action.toString()));
//             }
//         });
// 
//         logger.info(String.format("[REDUX] initializing redux store with %d middleware(s).", (allMiddlewares.size() - 1)));
// 
//         for (int i = allMiddlewares.size() - 1; i >= 0; i--) {
//             final Middleware mw = allMiddlewares.get(i);
//             logger.fine(String.format("[REDUX] chaining middleware (%d)", i));
//             //this will be null for the native middleware only, which is last
//             final Middleware next = (i == allMiddlewares.size() - 1 ? null : allMiddlewares.get(i + 1));
//             this.dispatcher = (action) -> {
//                 mw.dispatch(Store.this, action, next);
//             };
// 
//         }
//     }
// 
//     @Override
//     public S dispatch(Object action) {
//         logger.fine(String.format("[REDUX] dispatching action: %s", action.toString()));
// 
//         dispatcher.dispatch(action);
//         logger.finer(String.format("[REDUX] reduced state: %s", getState().toString()));
//         return getState();
//     }
// 
//     public S getState() {
//         return typedStateBuilder.apply(currentState);
//     }
// 
//     public UUID subscribe(Consumer<Map<String, Object>> subscriber) {
//         UUID uuid = UUID.randomUUID();
//         this.consumers.put(uuid, subscriber);
// 
//         return uuid;
//     }
// 
//     public void unsubscribe(UUID uuid) {
// 
//         this.consumers.remove(uuid);
//     }
// }


// Path: redux/src/main/java/com/conversationkit/redux/impl/CompletableFutureMiddleware.java
import com.conversationkit.redux.Action;
import com.conversationkit.redux.Middleware;
import com.conversationkit.redux.Store;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;

/*
 * The MIT License
 *
 * Copyright 2019 Synclab Consulting LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.conversationkit.redux.impl;


/**
 * Redux middleware that handles an async {@link Action} of type {@link Future} 
 * @author pdtyreus
 */
public class CompletableFutureMiddleware implements Middleware {

    private static final Logger logger = Logger.getLogger(CompletableFutureMiddleware.class.getName());
    
    @Override
    public void dispatch(Store store, Object action, Middleware next) {
        if (action instanceof Future) {
            logger.log(Level.FINE, "middleware handling future action {0}", action.toString());
            Future f = (Future) action;
            try {
                Object a = f.get();