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

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.collection.query.CQueryBuilder;
import net.ranides.assira.reflection.*;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class AFields extends AElements<IField> implements IFields {
    
    static final AFields EMPTY = FElements.newFields(AHints.EMPTY, CQueryBuilder.empty());

    AFields(IHints hints, CQuery<IField> stream) {
        super(hints, stream);
    }
    
    @Override
    public List<String> names() {
        return list(IField::name);
    }

    @Override
    public IFields require(Predicate<? super IField> predicate) {
        filter(predicate);
		return this;
    }

    @Override
    public IFields require(IAnnotations req) {
        return require(f -> {
			Set<Annotation> set = f.annotations().stream().collect(Collectors.toSet());
			return req.stream().matchAll(set::contains);
		});
    }

    @Override
    public IFields require(Class<? extends Annotation> annotation) {
        return require(f -> !f.annotations().require(annotation).isEmpty());
    }

    @Override
    public IFields require(IClass<?> type) {
        return require(f -> f.type().isSubclass(type));
    }

    @Override
    public IFields require(IAttribute attribute) {
        hints().require(attribute);
        return attribute.equals(IAttribute.ANY) ? this : require(f -> f.attributes().contains(attribute));
    }
    
    @Override
    public IFields require(Set<IAttribute> attributes) {
        hints().require(attributes);
        return attributes.contains(IAttribute.ANY) ? this : require(f -> f.attributes().containsAll(attributes));
    }

    @Override
    public IFields require(String name) {
        return require(f -> f.name().equals(name));
    }

    @Override
    public IFields discard(Predicate<? super IField> predicate) {
        return require(predicate.negate());
    }

    @Override
    public IFields discard(IAnnotations annotations) {
        Set<Annotation> req = annotations.stream().set();
		return require(f -> !f.annotations().stream().matchAny(req::contains));
    }

    @Override
    public IFields discard(Class<? extends Annotation> annotation) {
        return require(f -> f.annotations().require(annotation).isEmpty());
    }

    @Override
    public IFields discard(IClass<?> type) {
        return require(f -> !f.type().isSuper(type));
    }

    @Override
    public IFields discard(IAttribute attribute) {
        hints().discard(attribute);
        return require(f -> !f.attributes().contains(attribute));
    }
    
    @Override
    public IFields discard(Set<IAttribute> attributes) {
        hints().discard(attributes);
        return require(f -> !f.attributes().containsAll(attributes));
    }
    
    @Override
    public IFields discard(String name) {
        return require(f -> !f.name().equals(name));
    }

    @Override
    public IClasses type() {
        return FElements.newClasses(AHints.EMPTY, stream(IField::type));
    }

    @Override
    public CQuery<Field> reflective() {
        return stream().map(IField::reflective);
    }
    
    @Override
    public IClasses parent() {
        return FElements.newClasses(AHints.EMPTY, stream(IField::parent));
    }

}
