package net.ranides.assira.collection.query.derived;

import net.ranides.assira.collection.iterators.IteratorUtils;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.collection.query.base.CQAbstractFilter;

import java.util.Iterator;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * Adapter query: it filters provided stream using predicate.
 * Used by {@link CQuery#filter(Predicate)} operation.
 *
 * Supports fast iterator and stream if source supports it.
 * Does not support other fast operations.
 *
 * It supports statefull predicates which can be used for example, by "distinct" operation.
 *
 * @param <T> T
 */
public class CQFilter<T> extends CQAbstractFilter<T,T> {
    private final Supplier<Predicate<? super T>> p;

    /**
     * Creates new filtered query.
     * It assumes that predicate is stateless, and is reused by all terminal operations.
     *
     * @param source source
     * @param predicate predicate
     */
    public CQFilter(CQuery<T> source, Predicate<? super T> predicate) {
        super(source);
        this.p = () -> predicate;
    }

    /**
     * Creates new filtered query.
     * It assummes that supplier creates statefull predicate, which is recreated by every terminal operation.
     *
     * @param source source
     * @param predicate predicate
     */
    public CQFilter(CQuery<T> source, Supplier<Predicate<? super T>> predicate) {
        super(source);
        this.p = predicate;
    }

    @Override
    public CQuery<T> parallel() {
        return isParallel() ? this : new CQFilter<>(source.parallel(), p);
    }

    @Override
    public CQuery<T> sequential() {
        return isParallel() ? new CQFilter<>(source.sequential(), p) : this;
    }

    @Override
    public Stream<T> stream() {
        return source.stream().filter(p.get());
    }

    @Override
    public Iterator<T> iterator() {
        return IteratorUtils.filter(source.iterator(), p.get());
    }

    @Override
    public boolean whileEach(Predicate<? super T> consumer) {
        Predicate<? super T> predicate = p.get();
        return source.whileEach(v -> !predicate.test(v) || consumer.test(v));
    }

}
