/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.reflection.impl;

import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.generic.Ref;
import net.ranides.assira.reflection.*;

/**
 * This is abstract base class for all fluent collections.
 *
 * Please note that AElements is mutable class because it stores IHints.
 * That means semantic is different than {@link java.util.stream.Stream} or {@link CQuery}.
 *
 * Internally is stores CQuery and hints.
 *
 * @param <T> T
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public abstract class AElements<T> implements IElements<T> {
    
    private final IHints hints;

    private CQuery<T> cquery;

    protected AElements(IHints hints, CQuery<T> cquery) {
        this.hints = hints;
        this.cquery = cquery; 
    }

    protected final void filter(Predicate<? super T> predicate) {
        this.cquery = cquery.filter(predicate);
    }

    protected final void rebind(CQuery<T> cquery) {
        this.cquery = cquery;
    }
    
    @Override
    public final CQuery<T> stream() {
        return cquery;
    }

    @Override
    public final void each(Consumer<? super T> consumer) {
        stream().forEach(consumer);
    }
    
    @Override
    public final Iterator<T> iterator() {
        return stream().iterator();
    }
    
    @Override
    public final <R> CQuery<R> stream(Function<? super T, R> mapper) {
        return cquery.map(mapper);
    }
    
    @Override
    public final int count() {
        return cquery.size();
    }
    
    @Override
    public final boolean isEmpty() {
        return cquery.isEmpty();
    }
    
    @Override
    public final <R> List<R> list(Function<? super T, R> mapper) {
        return cquery.map(mapper).fetch().list();
    }
    
    @Override
    public final List<T> list() {
        return cquery.fetch().list();
    }
    
    @Override
    public final <R, A> R collect(Collector<? super T, A, R> collector) {
        return cquery.collect(collector);
    }
    
    @Override
    public final Ref<T> first() {
        return cquery.first();
    }

    @Override
    public Ref<T> single() {
        return cquery.single();
    }

    @Override
    public final Ref<T> at(int index) {
        return cquery.at(index);
    }

    @Override
    public final IElements<T> hint(IHints hints) {
        this.hints.replace(hints);
        return this;
    }
    
    protected final IHints hints() {
        return hints;
    }

    @Override
    public final int hashCode() {
        return 0;
    }

    @Override
    public final boolean equals(Object object) {
        if(object instanceof IElements<?>) {
            IElements<?> that = (IElements<?>)object;
            return this.list().equals(that.list());
        }
        return false;
    }

    @Override
    public String toString() {
        return list().toString();
    }
    
}
