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

import lombok.RequiredArgsConstructor;

import net.ranides.assira.collection.iterators.IteratorUtils;
import net.ranides.assira.collection.lists.ListUtils;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.collection.query.CQueryAbstract;
import net.ranides.assira.functional.Consumers;
import net.ranides.assira.functional.Predicates;

import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * Adapter query: it calculates new value for every item.
 * Used by {@link CQuery#map(Function)} operation.
 *
 * It supports stateless mappings only.
 *
 * Supports all features supported by source query, except "list".
 *
 * @param <S> S
 * @param <T> T
 */
@RequiredArgsConstructor
public class CQMap<S,T> extends CQueryAbstract<T> {

    protected final CQuery<S> source;

    protected final Function<? super S, ? extends T> function;

    @Override
    public Supplier<? extends RuntimeException> message() {
        return source.message();
    }

    @Override
    public CQuery<T> message(Supplier<? extends RuntimeException> message) {
        return new CQMap<>(source.message(message), function);
    }

    @Override
    public boolean hasFastEach() {
        return source.features().hasFastEach();
    }

    @Override
    public boolean hasFastStream() {
        return source.features().hasFastStream();
    }

    @Override
    public boolean hasFastIterator() {
        return source.features().hasFastIterator();
    }

    @Override
    public boolean hasFastLength() {
        return source.features().hasFastLength();
    }

    @Override
    public boolean hasFastList() {
        return false;
    }

    @Override
    public boolean isParallel() {
        return source.features().isParallel();
    }

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

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

    @Override
    public Stream<T> stream() {
        return source.stream().map(function);
    }

    @Override
    public Iterator<T> iterator() {
        return IteratorUtils.map(source.iterator(), function);
    }

    @Override
    public int size() {
        return source.size();
    }

    @Override
    public List<T> list() {
        return ListUtils.map(source.list(), function);
    }

    @Override
    public void forEach(Consumer<? super T> consumer) {
        source.forEach(v -> consumer.accept(function.apply(v)));
    }

    @Override
    public void forEach(Consumers.EachConsumer<? super T> consumer) {
        source.forEach((i,v) -> consumer.accept(i, function.apply(v)));
    }

    @Override
    public boolean whileEach(Predicate<? super T> consumer) {
        return source.whileEach(v -> consumer.test(function.apply(v)));
    }

    @Override
    public boolean whileEach(Predicates.EachPredicate<? super T> consumer) {
        return source.whileEach((i,v) -> consumer.test(i, function.apply(v)));
    }

}
