/*
 * @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.invoke.MethodHandle;
import java.lang.reflect.Method;
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 AMethods extends AElements<IMethod> implements IMethods {
    
    static final AMethods EMPTY = FElements.newMethods(AHints.EMPTY, CQueryBuilder.empty());

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

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

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

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

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

	@Override
	public IMethods require(IClass<?> returns) {
		return require(m -> m.returns().isSubclass(returns));
	}

	@Override
	public IMethods require(IArguments args) {
		List<IClass<?>> req = args.types().list();
		return require(m -> m.arguments().types().list().equals(req));
	}
    
    @Override
	public IMethods require(int args) {
		return require(m -> m.arguments().count()==args);
	}

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

	@Override
	public IMethods discard(IAnnotations annotations) {
		Set<Annotation> req = annotations.stream().collect(Collectors.toSet());
		return require(m -> !m.annotations().stream().matchAny(req::contains));
	}

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

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

	@Override
	public IMethods discard(IClass<?> returns) {
		return require(m -> !m.returns().isSubclass(returns));
	}

	@Override
	public IMethods discard(IArguments args) {
		IClasses req = args.types();
		return require(m -> !m.arguments().types().equals(req));
	}
    
    @Override
	public IMethods discard(int args) {
		return require(m -> m.arguments().count()!=args);
	}

	@Override
	public IMethods matches(IClasses args) {
        return require(m -> m.matches(args));
	}
    
    @Override
	public IMethods accepts(Object... args) {
        return require(m -> m.accepts(args));
	}
	
	@Override
	public IClasses returns() {
        return FElements.newClasses(AHints.EMPTY, stream().map(IMethod::returns));
	}

	@Override
	public IClasses parent() {
        return FElements.newClasses(AHints.EMPTY, stream().map(IMethod::parent));
	}

    @Override
    public CQuery<Method> reflective() {
        return stream().map(IMethod::reflective);
    }

    @Override
    public CQuery<MethodHandle> handle() {
        return stream().map(m -> m.handle());
    }

    
}
