// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/Utils.java
// @NotNull
// public static <EventT extends BaseEvent> String ids(
//         @NotNull List<EventT> events) {
//     return events.stream()
//             .map(i -> String.valueOf(i.getId()))
//             .collect(JOIN_EVENT_IDS);
// }
// 
// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/Utils.java
// @NotNull
// public static <EventT extends BaseEvent> String stringify(
//         @NotNull List<EventT> events) {
//     return events.stream()
//             .map(EventT::toString)
//             .collect(JOIN_EVENTS);
// }


import com.github.rahulsom.grooves.api.EventApplyOutcome;
import com.github.rahulsom.grooves.api.GroovesException;
import com.github.rahulsom.grooves.api.events.BaseEvent;
import com.github.rahulsom.grooves.api.events.DeprecatedBy;
import com.github.rahulsom.grooves.api.events.Deprecates;
import com.github.rahulsom.grooves.api.events.RevertEvent;
import com.github.rahulsom.grooves.api.snapshots.internal.BaseSnapshot;
import io.reactivex.Flowable;
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.github.rahulsom.grooves.api.EventApplyOutcome.RETURN;
import static com.github.rahulsom.grooves.queries.internal.Utils.ids;
import static com.github.rahulsom.grooves.queries.internal.Utils.stringify;
import static io.reactivex.Flowable.*;
package com.github.rahulsom.grooves.queries.internal;




/**
 * Executes a query. This makes a query more flexible by allowing the use of different query
 * executors.
 *
 * @param <AggregateT>  The aggregate over which the query executes
 * @param <EventIdT>    The type of the EventT's id field
 * @param <EventT>      The type of the Event
 * @param <SnapshotIdT> The type of the SnapshotT's id field
 * @param <SnapshotT>   The type of the Snapshot
 * @param <QueryT>      A reference to the query type.
 * @author Rahul Somasunderam
 */
public class QueryExecutor<
        AggregateT,
        EventIdT,
        EventT extends BaseEvent<AggregateT, EventIdT, EventT>,
        SnapshotIdT,
        SnapshotT extends BaseSnapshot<AggregateT, SnapshotIdT, EventIdT, EventT>,
        QueryT extends BaseQuery<AggregateT, EventIdT, EventT, SnapshotIdT, SnapshotT>
        > implements Executor<AggregateT, EventIdT, EventT, SnapshotIdT, SnapshotT> {

    @SuppressWarnings("WeakerAccess")
    protected final Logger log = LoggerFactory.getLogger(getClass());

    private static <T> List<T> plus(List<T> list, T element) {
        List<T> retval = new ArrayList<>(list);
        retval.add(element);
        return retval;
    }

    /**
     * Applies all revert events from a list and returns the list with only valid forward events.
     *
     * @param events The list of events
     * @return An Flowable of forward only events
     */
    @NotNull
    @Override
    public Flowable<EventT> applyReverts(@NotNull Flowable<EventT> events) {

        return events.toList().toFlowable().flatMap(eventList -> {
            log.debug("     Event Ids (includes reverts that won't be applied): {}",
// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/Utils.java
// @NotNull
// public static <EventT extends BaseEvent> String ids(
//         @NotNull List<EventT> events) {
//     return events.stream()
//             .map(i -> String.valueOf(i.getId()))
//             .collect(JOIN_EVENT_IDS);
// }
// 
// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/Utils.java
// @NotNull
// public static <EventT extends BaseEvent> String stringify(
//         @NotNull List<EventT> events) {
//     return events.stream()
//             .map(EventT::toString)
//             .collect(JOIN_EVENTS);
// }


// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/QueryExecutor.java
import com.github.rahulsom.grooves.api.EventApplyOutcome;
import com.github.rahulsom.grooves.api.GroovesException;
import com.github.rahulsom.grooves.api.events.BaseEvent;
import com.github.rahulsom.grooves.api.events.DeprecatedBy;
import com.github.rahulsom.grooves.api.events.Deprecates;
import com.github.rahulsom.grooves.api.events.RevertEvent;
import com.github.rahulsom.grooves.api.snapshots.internal.BaseSnapshot;
import io.reactivex.Flowable;
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.github.rahulsom.grooves.api.EventApplyOutcome.RETURN;
import static com.github.rahulsom.grooves.queries.internal.Utils.ids;
import static com.github.rahulsom.grooves.queries.internal.Utils.stringify;
import static io.reactivex.Flowable.*;

package com.github.rahulsom.grooves.queries.internal;




/**
 * Executes a query. This makes a query more flexible by allowing the use of different query
 * executors.
 *
 * @param <AggregateT>  The aggregate over which the query executes
 * @param <EventIdT>    The type of the EventT's id field
 * @param <EventT>      The type of the Event
 * @param <SnapshotIdT> The type of the SnapshotT's id field
 * @param <SnapshotT>   The type of the Snapshot
 * @param <QueryT>      A reference to the query type.
 * @author Rahul Somasunderam
 */
public class QueryExecutor<
        AggregateT,
        EventIdT,
        EventT extends BaseEvent<AggregateT, EventIdT, EventT>,
        SnapshotIdT,
        SnapshotT extends BaseSnapshot<AggregateT, SnapshotIdT, EventIdT, EventT>,
        QueryT extends BaseQuery<AggregateT, EventIdT, EventT, SnapshotIdT, SnapshotT>
        > implements Executor<AggregateT, EventIdT, EventT, SnapshotIdT, SnapshotT> {

    @SuppressWarnings("WeakerAccess")
    protected final Logger log = LoggerFactory.getLogger(getClass());

    private static <T> List<T> plus(List<T> list, T element) {
        List<T> retval = new ArrayList<>(list);
        retval.add(element);
        return retval;
    }

    /**
     * Applies all revert events from a list and returns the list with only valid forward events.
     *
     * @param events The list of events
     * @return An Flowable of forward only events
     */
    @NotNull
    @Override
    public Flowable<EventT> applyReverts(@NotNull Flowable<EventT> events) {

        return events.toList().toFlowable().flatMap(eventList -> {
            log.debug("     Event Ids (includes reverts that won't be applied): {}",
                    ids(eventList));