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

import lombok.experimental.UtilityClass;
import net.ranides.assira.collection.arrays.ArrayUtils;
import net.ranides.assira.collection.lists.CopyList;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.collection.sets.OpenSet;
import net.ranides.assira.functional.Consumers;
import net.ranides.assira.functional.Predicates;
import net.ranides.assira.generic.Ref;

import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * This is internal utility class not supposed to use by clients.
 * It has default implementations of query operations for arrays.
 *
 * It is almost complete facade: most of the time it delegates work to BaseArrayParallel or BaseArraySequential.
 */
@UtilityClass
@SuppressWarnings("MissingJavadoc")
public class BaseArray {

    public static <T> Spliterator<T> spliterator(T[] that) {
        return Arrays.spliterator(that);
    }

    public static <T> Spliterator<T> spliterator(T[] that, int begin, int end) {
        return Arrays.spliterator(that, begin, end);
    }

    public static <T> ListIterator<T> iterator(T[] that) {
        return ArrayUtils.iterator(that);
    }

    public static <T> ListIterator<T> iterator(T[] that, int begin, int end) {
        return ArrayUtils.iterator(that, begin, end);
    }

    public static <T> Stream<T> stream(T[] that, boolean parallel) {
        return StreamSupport.stream(Arrays.spliterator(that), parallel);
    }

    public static <T> Stream<T> stream(T[] that, int begin, int end, boolean parallel) {
        return StreamSupport.stream(Arrays.spliterator(that, begin, end), parallel);
    }

    public static <T> List<T> list(CQuery<T> that) {
        return list(that.array());
    }

    public static <T> List<T> list(T[] array) {
        List<T> source = Arrays.asList(array);
        return new CopyList<>(source);
    }

    public static <T> List<T> list(T[] array, int begin, int end) {
        List<T> source = Arrays.asList(array).subList(begin, end);
        return new CopyList<>(source);
    }

    public static <T> Set<T> set(CQuery<T> that) {
        return new OpenSet<>(that.array());
    }

    public static <T> Ref<T> at(CQuery<T> that, int index) {
        return at(that.array(), index).withMessage(that.message());
    }

    public static <T> Ref<T> first(CQuery<T> that) {
        return at(that.array(), 0).withMessage(that.message());
    }

    public static <T> Ref<T> last(CQuery<T> that) {
        T[] array = that.array();
        return at(array, array.length-1).withMessage(that.message());
    }

    public static <T> Ref<T> at(T[] a, int index) {
        return index < a.length ? Ref.of(a[index]) : Ref.empty();
    }

    public static <T> void forEach(T[] array, boolean paralell, Consumer<? super T> consumer) {
        if(paralell) {
            BaseArrayParalell.forEach(array, consumer);
        } else {
            BaseArraySequential.forEach(array, consumer);
        }
    }

    public static <T> void forEach(T[] array, int begin, int end, boolean paralell, Consumer<? super T> consumer) {
        if(paralell) {
            BaseArrayParalell.forEach(array, begin, end, consumer);
        } else {
            BaseArraySequential.forEach(array, begin, end, consumer);
        }
    }

    public static <T> void forEach(T[] array, boolean paralell, Consumers.EachConsumer<? super T> consumer) {
        if(paralell) {
            BaseArrayParalell.forEach(array, consumer);
        } else {
            BaseArraySequential.forEach(array, consumer);
        }
    }

    public static <T> void forEach(T[] array, int begin, int end, boolean paralell, Consumers.EachConsumer<? super T> consumer) {
        if(paralell) {
            BaseArrayParalell.forEach(array, begin, end, consumer);
        } else {
            BaseArraySequential.forEach(array, begin, end, consumer);
        }
    }

    public static <T> boolean whileEach(T[] array, boolean paralell, Predicate<? super T> consumer) {
        if(paralell) {
            return BaseArrayParalell.whileEach(array, consumer);
        } else {
            return BaseArraySequential.whileEach(array, consumer);
        }
    }

    public static <T> boolean whileEach(T[] array, int begin, int end, boolean paralell, Predicate<? super T> consumer) {
        if(paralell) {
            return BaseArrayParalell.whileEach(array, begin, end, consumer);
        } else {
            return BaseArraySequential.whileEach(array, begin, end, consumer);
        }
    }

    public static <T> boolean whileEach(T[] array, boolean paralell, Predicates.EachPredicate<? super T> consumer) {
        if(paralell) {
            return BaseArrayParalell.whileEach(array, consumer);
        } else {
            return BaseArraySequential.whileEach(array, consumer);
        }
    }

    public static <T> boolean whileEach(T[] array, int begin, int end, boolean paralell, Predicates.EachPredicate<? super T> consumer) {
        if(paralell) {
            return BaseArrayParalell.whileEach(array, begin, end, consumer);
        } else {
            return BaseArraySequential.whileEach(array, begin, end, consumer);
        }
    }
}
