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.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Consumer;
import java.util.function.Predicate;

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

    public static <T> void forEach(List<T> list, Consumer<? super T> consumer) {
        list.parallelStream().forEach(consumer);
    }

    public static <T> void forEach(List<T> list, Consumers.EachConsumer<? super T> consumer) {
        int threads = ForkJoinPool.getCommonPoolParallelism();
        int blocksize = Math.max(1, list.size() / threads);
        List<Callable<Void>> tasks = new ArrayList<>(threads);

        for (int offset = 0; offset < list.size(); offset += blocksize) {
            int let_offset = offset;
            tasks.add(() -> {
                BaseListSequential.forEach(list, let_offset, let_offset + blocksize, consumer);
                return null;
            });
        }

        if (list.size() % threads != 0) {
            tasks.add(() -> {
                BaseListSequential.forEach(list, list.size() - list.size() %threads, list.size(), consumer);
                return null;
            });
        }
        ForkJoinPool.commonPool().invokeAll(tasks);
    }

    public static <T> boolean whileEach(List<T> list, Predicate<? super T> consumer) {
        return list.parallelStream().allMatch(consumer);
    }

    public static <T> boolean whileEach(List<T> list, Predicates.EachPredicate<? super T> consumer) {
        int threads = ForkJoinPool.getCommonPoolParallelism();
        int blocksize = Math.max(1, list.size() / threads);
        List<Callable<Void>> tasks = new ArrayList<>(threads);
        BaseState.Terminator terminator = BaseState.newTerminator(true);

        for (int offset = 0; offset < list.size(); offset += blocksize) {
            int let_offset = offset;
            tasks.add(() -> {
                BaseListSequential.whileEach(list, let_offset, let_offset+blocksize, terminator, consumer);
                return null;
            });
        }

        if (list.size() % threads != 0) {
            tasks.add(() -> {
                BaseListSequential.whileEach(list, list.size() - list.size() %threads, list.size(), terminator, consumer);
                return null;
            });
        }

        ForkJoinPool.commonPool().invokeAll(tasks);
        return !terminator.stopped();
    }

}
