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

import lombok.experimental.UtilityClass;

import net.ranides.assira.functional.Consumers;
import net.ranides.assira.functional.Predicates;

import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;

/**
 * This is internal utility class not supposed to use by clients.
 * It has default implementations of parallel query operations using only Spliterator contract.
 *
 * Those methods should not be called directly, but by "BaseSpliterator" facade.
 */
@UtilityClass
@SuppressWarnings("MissingJavadoc")
public class BaseSpliteratorSequential {

    public static <T> void forEach(Spliterator<T> that, Consumer<? super T> consumer) {
        that.forEachRemaining(consumer);
    }

    public static <T> void forEach(Spliterator<T> that, Consumers.EachConsumer<? super T> consumer) {
        BaseState.Index index = BaseState.newIndex(false);
        that.forEachRemaining(v -> consumer.accept(index.next(), v));
    }

    public static <T> boolean whileEach(Spliterator<T> that, Predicate<? super T> consumer) {
        return StreamSupport.stream(that, false).allMatch(consumer);
    }

    public static <T> boolean whileEach(Spliterator<T> that, Predicates.EachPredicate<? super T> consumer) {
        BaseState.Index index = BaseState.newIndex(false);
        return StreamSupport.stream(that, false).allMatch(v -> {
            return consumer.test(index.next(), v);
        });
    }

    public static <T> int size(Spliterator<T> that) {
        if(that.hasCharacteristics(Spliterator.SIZED)) {
            return (int) that.estimateSize();
        }
        BaseState.Index index = BaseState.newIndex(false);
        that.forEachRemaining(v -> index.next());
        return index.value();
    }

}
