package net.ranides.assira.ui.query;

import javax.accessibility.AccessibleContext;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.generic.Ref;
import net.ranides.assira.reflection.BeanPropertyBinding;

import java.awt.*;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;

class RComponents implements IComponents {

    public static final RComponents EMPTY = new RComponents(CQuery.empty());

    private final CQuery<IComponent> query;

    RComponents(Component[] components) {
        this.query = CQuery.from().array(components).map(IComponent::of);
    }

    RComponents(CQuery<IComponent> query) {
        this.query = query;
    }

    RComponents $empty() {
        return EMPTY;
    }

    @Override
    public IComponents find(String[] path) {
        return new RComponents(query.flat(c -> c.find(path).stream()));
    }

    @Override
    public IComponents find(String path) {
        return new RComponents(query.flat(c -> c.find(path).stream()));
    }

    @Override
    public IComponents find(Predicate<IComponent> visitor) {
        return new RComponents(query.flat(c -> c.find(visitor).stream()));
    }

    @Override
    public CQuery<BeanPropertyBinding<Object>> property(String name) {
        return stream().map(c -> c.property(name));
    }

    @Override
    public void property(String name, Object value) {
        stream().forEach(c -> c.property(name, value));
    }

    @Override
    public void set(String name, Object value) {
        stream().forEach(c -> c.set(name, value));
    }

    @Override
    public CQuery<Object> get(String name) {
        return stream().map(c -> c.get(name));
    }

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

    @Override
    public boolean isEmpty() {
        return query.isEmpty();
    }

    @Override
    public <R> CQuery<R> stream(Function<? super IComponent, R> mapper) {
        return stream().map(mapper);
    }

    @Override
    public CQuery<IComponent> stream() {
        return query;
    }

    @Override
    public void each(Consumer<? super IComponent> consumer) {
        stream().forEach(consumer);
    }

    @Override
    public <R> List<R> list(Function<? super IComponent, R> mapper) {
        return stream(mapper).list();
    }

    @Override
    public List<IComponent> list() {
        return stream().list();
    }

    @Override
    public <R, A> R collect(Collector<? super IComponent, A, R> collector) {
        return stream().collect(collector);
    }

    @Override
    public IComponents filter(Predicate<IComponent> predicate) {
        return new RComponents(query.filter(predicate));
    }

    @Override
    public <T extends Component> IComponents filter(Class<T> type) {
        return new RComponents(query.filter(c -> c.isType(type)));
    }

    @Override
    public Ref<IComponent> first() {
        return stream().first();
    }

    @Override
    public <T extends Component> Ref<T> first(Class<T> type) {
        return query.map(IComponent::unwrap).filter(type).first();
    }

    @Override
    public <T extends Component> Ref<T> single(Class<T> type) {
        return query.map(IComponent::unwrap).filter(type).single();
    }

    @Override
    public Ref<IComponent> single() {
        return stream().single();
    }

    @Override
    public Ref<IComponent> at(int index) {
        return Ref.empty();
    }

    @Override
    public CQuery<String> names() {
        return query.map(IComponent::name);
    }

    @Override
    public CQuery<AccessibleContext> accessibles() {
        return query.map(IComponent::accessible);
    }

    @Override
    public IComponents parent() {
        return new RComponents(query.map(IComponent::parent));
    }

    @Override
    public Iterator<IComponent> iterator() {
        return query.iterator();
    }

}
