/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */

package net.ranides.assira.collection.query;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import net.ranides.assira.functional.CheckedFunction;
import net.ranides.assira.functional.CheckedSupplier;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class CQueryBuilder<T> {
    
    @SuppressWarnings("unchecked")
    private static final CQuery EMPTY = new CQueryAbstract.CQCollection(Collections.emptyList());
    
    private final List<CQuery<T>> list = new ArrayList<>();
    
    public CQueryBuilder() {
        // do nothing
    }
    
    @SafeVarargs
    public final CQueryBuilder<T> add(T... items) {
        list.add(of(items));
        return this;
    }
    
    public CQueryBuilder<T> addQuery(CQuery<T> item) {
        list.add(item);
        return this;
    }
    
    public CQueryBuilder<T> addQuery(Supplier<CQuery<T>> item) {
        list.add(fromQuery(item));
        return this;
    }
    
    public <P> CQueryBuilder<T> addQuery(P param, Function<? super P, CQuery<T>> item) {
        list.add(fromQuery(param,item));
        return this;
    }
    
    public CQueryBuilder<T> addIterable(Iterable<T> f) {
        return addQuery(fromIterable(f));
    }
    
    public CQueryBuilder<T> addIterable(Supplier<Iterable<T>> f) {
        return addQuery(fromIterable(f));
    }
    
    public <P> CQueryBuilder<T> addIterable(P param, Function<? super P,Iterable<T>> f) {
        return addQuery(fromIterable(param, f));
    }
    
    public CQueryBuilder<T> addCollection(Collection<T> f) {
        return addQuery(fromCollection(f));
    }
    
    public CQueryBuilder<T> addCollection(Supplier<Collection<T>> f) {
        return addQuery(fromCollection(f));
    }
    
    public <P> CQueryBuilder<T> addCollection(P param, Function<? super P, Collection<T>> f) {
        return addQuery(fromCollection(param, f));
    }
    
    public CQueryBuilder<T> addArray(T[] f) {
        return addQuery(fromArray(f));
    }
    
    public CQueryBuilder<T> addArray(Supplier<T[]> f) {
        return addQuery(fromArray(f));
    }
    
    public <P> CQueryBuilder<T> addArray(P param, Function<? super P, T[]> f) {
        return addQuery(fromArray(param, f));
    }
    
    public CQuery<T> result() {
        return fromCollection(list).unfold(v -> v);
    }
    
    public static <T> CQuery<T> empty() {
        return EMPTY;
    }
    
    @SafeVarargs
    public static <T> CQuery<T> of(T... source) {
        return new CQueryAbstract.CQArray<>(source);
    }
    
    public static <T> CQuery<T> fromIterable(Iterable<T> source) {
        return new CQueryAbstract.CQIterable<>(source);
    }
    
    public static <T> CQuery<T> fromIterable(Supplier<Iterable<T>> source) {
        return new CQueryAbstract.CQIterable<>(source);
    }
    
    public static <T,P> CQuery<T> fromIterable(P param, Function<? super P, Iterable<T>> source) {
        return fromIterable(() -> source.apply(param));
    }
    
    public static <T,E extends Exception> CQuery<T> fromStream(CheckedSupplier<Stream<T>,E> source) throws E {
        return new CQueryAbstract.CQIterable<>(() -> source.$get().iterator());
    }
    
    public static <T,P,E extends Exception> CQuery<T> fromStream(P param, CheckedFunction<? super P, Stream<T>,E> source) throws E {
        return fromIterable(() -> source.$apply(param).iterator());
    }
    
    public static <T> CQuery<T> fromCollection(Collection<T> source) {
        return new CQueryAbstract.CQCollection<>(source);
    }
    
    public static <T> CQuery<T> fromCollection(Supplier<Collection<T>> source) {
        return new CQueryAbstract.CQCollection<>(source);
    }
    
    public static <T,P> CQuery<T> fromCollection(P param, Function<? super P, Collection<T>> source) {
        return fromCollection(() -> source.apply(param));
    }
    
    public static <T> CQuery<T> fromArray(T[] source) {
        return new CQueryAbstract.CQArray<>(source);
    }
    
    public static <T> CQuery<T> fromArray(Supplier<T[]> source) {
        return new CQueryAbstract.CQArray<>(() -> source.get());
    }
    
    public static <T,P> CQuery<T> fromArray(P param, Function<? super P, T[]> source) {
        return fromArray(() -> source.apply(param));
    }
    
    public static <T> CQuery<T> fromQuery(Supplier<CQuery<T>> source) {
        return new CQueryAbstract.CQQuery<>(source);
    }
    
    public static <T,P> CQuery<T> fromQuery(P param, Function<? super P, CQuery<T>> source) {
        return fromQuery(() -> source.apply(param));
    }
    
}
