package net.ranides.test.mockup.query;

import lombok.RequiredArgsConstructor;
import lombok.experimental.UtilityClass;

import net.ranides.assira.collection.iterators.IteratorUtils;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.collection.query.CQueryAbstract;
import net.ranides.assira.collection.query.support.BaseStream;
import net.ranides.assira.functional.Predicates;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

@UtilityClass
public class CQueryMockup {

    public static <T> CQuery<T> of(String features) {
        return new Values<>(features, Collections.emptyList(), null);
    }

    public static <T> CQuery<T> of(String features, T[] data) {
        return new Values<>(features, new ArrayList<>(Arrays.asList(data)), null);
    }

    public static <T> CQuery<T> of(String features, List<T> data) {
        return new Values<>(features, new ArrayList<>(data), null);
    }

    @RequiredArgsConstructor
    private static class Values<T> extends CQueryAbstract<T> {
        private final String features;
        private final List<T> data;
        private final Supplier<? extends RuntimeException> message;

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

        @Override
        public CQuery<T> message(Supplier<? extends RuntimeException> message) {
            return new Values<>(features, data, message);
        }

        @Override
        public Stream<T> stream() {
            return data.stream();
        }

        @Override
        public Iterator<T> iterator() {
            return IteratorUtils.withCause(data.iterator(), message);
        }

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

        @Override
        public boolean whileEach(Predicate<? super T> consumer) {
            // we have to override #whileEach because base class has intentional cycle
            if(features.contains("each1")) {
                return BaseStream.whileEach(this, consumer);
            }
            return super.whileEach(consumer);
        }

        @Override
        public boolean whileEach(Predicates.EachPredicate<? super T> consumer) {
            // we have to override #whileEach because base class has intentional cycle
            if(features.contains("each2")) {
                return BaseStream.whileEach(this, consumer);
            }
            return super.whileEach(consumer);
        }

        @Override
        public boolean hasFastEach() {
            return features.contains("each1") || features.contains("each2");
        }

        @Override
        public boolean hasFastStream() {
            return features.contains("stream");
        }

        @Override
        public boolean hasFastIterator() {
            return features.contains("iterator");
        }

        @Override
        public boolean hasFastLength() {
            return features.contains("length");
        }

        @Override
        public boolean hasFastList() {
            return features.contains("list");
        }

        @Override
        public boolean isParallel() {
            return features.contains("parallel");
        }

        @Override
        public CQuery<T> parallel() {
            return this;
        }

        @Override
        public CQuery<T> sequential() {
            return this;
        }
    }

}
