// Path: warehouse-infrastructure/src/main/java/tools/AgentQueue.java
// public class AgentQueue {
// 
//     public interface Command {
//         void execute() throws Exception;
//     }
// 
//     public interface CommandWithResult<T> extends Query<T> {
//         T execute() throws Exception;
//     }
// 
//     public interface Query<T> {
//         T execute() throws Exception;
//     }
// 
//     private final Queue<Runnable> tasks = new ConcurrentLinkedDeque<>();
//     private final ReentrantLock lock = new ReentrantLock();
// 
//     public CompletionStage<?> command(Command command) {
//         CompletableFuture<?> future = new CompletableFuture<>();
//         tasks.offer(new CommandTask(future, command));
//         execute();
//         return future;
//     }
// 
//     public <T> CompletionStage<T> command(CommandWithResult<T> command) {
//         CompletableFuture<T> future = new CompletableFuture<>();
//         tasks.offer(new QueryTask<>(future, command));
//         execute();
//         return future;
//     }
// 
//     public <T> CompletionStage<T> query(Query<T> query) {
//         CompletableFuture<T> future = new CompletableFuture<>();
//         tasks.offer(new QueryTask<>(future, query));
//         execute();
//         return future;
//     }
// 
//     public <T> T querySync(Query<T> query) {
//         CompletableFuture<T> future = new CompletableFuture<>();
//         tasks.offer(new QueryTask<>(future, query));
//         execute();
//         try {
//             return future.get();
//         } catch (InterruptedException e) {
//             throw new RuntimeException(e.getCause());
//         } catch (ExecutionException e) {
//             throw e.getCause() instanceof RuntimeException
//                     ? ((RuntimeException) e.getCause())
//                     : new RuntimeException(e.getCause());
//         }
//     }
// 
//     private void execute() {
//         if (lock.tryLock()) {
//             try {
//                 while (!tasks.isEmpty()) {
//                     Runnable task = tasks.poll();
//                     task.run();
//                 }
//             } finally {
//                 lock.unlock();
//             }
//             if (!tasks.isEmpty()) {
//                 execute();
//             }
//         }
//     }
// 
//     private static class CommandTask implements Runnable {
//         final CompletableFuture<?> future;
//         final Command command;
// 
//         private CommandTask(CompletableFuture<?> future, Command command) {
//             this.future = future;
//             this.command = command;
//         }
// 
//         @Override
//         public void run() {
//             try {
//                 command.execute();
//                 future.complete(null);
//             } catch (Throwable t) {
//                 future.completeExceptionally(t);
//             }
//         }
//     }
// 
//     private static class QueryTask<T> implements Runnable {
//         final CompletableFuture<T> future;
//         final Query<T> query;
// 
//         private QueryTask(CompletableFuture<T> future, Query<T> query) {
//             this.future = future;
//             this.query = query;
//         }
// 
//         @Override
//         public void run() {
//             try {
//                 future.complete(query.execute());
//             } catch (Throwable t) {
//                 future.completeExceptionally(t);
//             }
//         }
//     }
// }
// 
// Path: warehouse-model/src/main/java/warehouse/PaletteLabel.java
// @Value
// public class PaletteLabel {
//     private final String id;
//     private final String refNo;
// 
//     @Override
//     public String toString() {
//         return id;
//     }
// }
// 
// Path: warehouse-model/src/main/java/warehouse/locations/Location.java
// @Value
// public class Location {
//     private final String location;
// 
//     public static Location production() {
//         return new Location("production");
//     }
// 
//     public static Location quarantine() {
//         return new Location("quarantine");
//     }
// 
//     public static Location onTheMove(String user) {
//         return new Location("picked by " + user);
//     }
// 
//     public static Location unknown() {
//         return new Location("<unknown>");
//     }
// }


import lombok.AllArgsConstructor;
import tools.AgentQueue;
import warehouse.PaletteLabel;
import warehouse.locations.Location;
import java.util.List;
import java.util.concurrent.CompletionStage;
package warehouse.products;



/**
 * Created by michal on 10.10.2017.
 */
@AllArgsConstructor
public class ProductStockAgent {

    private final String refNo;
    private final ProductStock object;
    private final ProductStockEventSourcingRepository.ProductStockEventsHandler events;
    private final AgentQueue queue;

    public String getRefNo() {
        return refNo;
    }

    public CompletionStage<List<Object>> registerNew(RegisterNew registerNew) {
        return queue.command(() -> object.registerNew(registerNew))
                .thenApply(o -> events.getLast());
    }

    public CompletionStage<List<Object>> pick(Pick pick) {
        return queue.command(() -> object.pick(pick))
                .thenApply(o -> events.getLast());
    }

    public CompletionStage<List<Object>> store(Store store) {
// Path: warehouse-infrastructure/src/main/java/tools/AgentQueue.java
// public class AgentQueue {
// 
//     public interface Command {
//         void execute() throws Exception;
//     }
// 
//     public interface CommandWithResult<T> extends Query<T> {
//         T execute() throws Exception;
//     }
// 
//     public interface Query<T> {
//         T execute() throws Exception;
//     }
// 
//     private final Queue<Runnable> tasks = new ConcurrentLinkedDeque<>();
//     private final ReentrantLock lock = new ReentrantLock();
// 
//     public CompletionStage<?> command(Command command) {
//         CompletableFuture<?> future = new CompletableFuture<>();
//         tasks.offer(new CommandTask(future, command));
//         execute();
//         return future;
//     }
// 
//     public <T> CompletionStage<T> command(CommandWithResult<T> command) {
//         CompletableFuture<T> future = new CompletableFuture<>();
//         tasks.offer(new QueryTask<>(future, command));
//         execute();
//         return future;
//     }
// 
//     public <T> CompletionStage<T> query(Query<T> query) {
//         CompletableFuture<T> future = new CompletableFuture<>();
//         tasks.offer(new QueryTask<>(future, query));
//         execute();
//         return future;
//     }
// 
//     public <T> T querySync(Query<T> query) {
//         CompletableFuture<T> future = new CompletableFuture<>();
//         tasks.offer(new QueryTask<>(future, query));
//         execute();
//         try {
//             return future.get();
//         } catch (InterruptedException e) {
//             throw new RuntimeException(e.getCause());
//         } catch (ExecutionException e) {
//             throw e.getCause() instanceof RuntimeException
//                     ? ((RuntimeException) e.getCause())
//                     : new RuntimeException(e.getCause());
//         }
//     }
// 
//     private void execute() {
//         if (lock.tryLock()) {
//             try {
//                 while (!tasks.isEmpty()) {
//                     Runnable task = tasks.poll();
//                     task.run();
//                 }
//             } finally {
//                 lock.unlock();
//             }
//             if (!tasks.isEmpty()) {
//                 execute();
//             }
//         }
//     }
// 
//     private static class CommandTask implements Runnable {
//         final CompletableFuture<?> future;
//         final Command command;
// 
//         private CommandTask(CompletableFuture<?> future, Command command) {
//             this.future = future;
//             this.command = command;
//         }
// 
//         @Override
//         public void run() {
//             try {
//                 command.execute();
//                 future.complete(null);
//             } catch (Throwable t) {
//                 future.completeExceptionally(t);
//             }
//         }
//     }
// 
//     private static class QueryTask<T> implements Runnable {
//         final CompletableFuture<T> future;
//         final Query<T> query;
// 
//         private QueryTask(CompletableFuture<T> future, Query<T> query) {
//             this.future = future;
//             this.query = query;
//         }
// 
//         @Override
//         public void run() {
//             try {
//                 future.complete(query.execute());
//             } catch (Throwable t) {
//                 future.completeExceptionally(t);
//             }
//         }
//     }
// }
// 
// Path: warehouse-model/src/main/java/warehouse/PaletteLabel.java
// @Value
// public class PaletteLabel {
//     private final String id;
//     private final String refNo;
// 
//     @Override
//     public String toString() {
//         return id;
//     }
// }
// 
// Path: warehouse-model/src/main/java/warehouse/locations/Location.java
// @Value
// public class Location {
//     private final String location;
// 
//     public static Location production() {
//         return new Location("production");
//     }
// 
//     public static Location quarantine() {
//         return new Location("quarantine");
//     }
// 
//     public static Location onTheMove(String user) {
//         return new Location("picked by " + user);
//     }
// 
//     public static Location unknown() {
//         return new Location("<unknown>");
//     }
// }


// Path: warehouse-infrastructure/src/main/java/warehouse/products/ProductStockAgent.java
import lombok.AllArgsConstructor;
import tools.AgentQueue;
import warehouse.PaletteLabel;
import warehouse.locations.Location;
import java.util.List;
import java.util.concurrent.CompletionStage;

package warehouse.products;



/**
 * Created by michal on 10.10.2017.
 */
@AllArgsConstructor
public class ProductStockAgent {

    private final String refNo;
    private final ProductStock object;
    private final ProductStockEventSourcingRepository.ProductStockEventsHandler events;
    private final AgentQueue queue;

    public String getRefNo() {
        return refNo;
    }

    public CompletionStage<List<Object>> registerNew(RegisterNew registerNew) {
        return queue.command(() -> object.registerNew(registerNew))
                .thenApply(o -> events.getLast());
    }

    public CompletionStage<List<Object>> pick(Pick pick) {
        return queue.command(() -> object.pick(pick))
                .thenApply(o -> events.getLast());
    }

    public CompletionStage<List<Object>> store(Store store) {
        return queue.command(() -> object.store(store))