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

import lombok.RequiredArgsConstructor;

import net.ranides.assira.collection.iterators.IteratorUtils;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.collection.query.base.CQListSupplier;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * Adapter query: it sorts elements from source every time, when terminal operation is called.
 * It is used by {@link CQuery#sort()}.
 *
 * It does not support any fast operations except "length".
 * Reports fast length if source stream reports it.
 *
 * @param <T> T
 */
public class CQSort<T> extends CQListSupplier<T> {

    private final CQuery<T> source;

    private final Comparator<? super T> cmp;

    public CQSort(Supplier<? extends RuntimeException> message, CQuery<T> source, Comparator<? super T> cmp) {
        super(message);
        this.source = source;
        this.cmp = cmp;
    }

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

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

    @Override
    public List<T> list() {
        List<T> list = new ArrayList<>(source.list());
        list.sort(cmp);
        return list;
    }

    @Override
    public Stream<T> stream() {
        if (source.features().hasFastStream()) {
            return source.stream().sorted(cmp);
        } else {
            return list().stream();
        }
    }

    @Override
    public Iterator<T> iterator() {
        if (source.features().hasFastStream()) {
            return IteratorUtils.withCause(stream().iterator(), message());
        } else {
            return IteratorUtils.withCause(list().iterator(), message());
        }
    }

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