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

import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.functional.special.AnyFunction;

import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.util.function.Predicate;

/**
 * Fluent interface for collection of IExecutable objects
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @param <T> type of executable (probably IConstructor or IMethod)
 */
public interface IExecutables<T extends IExecutable> extends IElements<T> {

    /**
     * Returns (this) filtered collection, with methods matching predicate
     *
     * @param predicate predicate
     * @return IExecutables
     */
    IExecutables<T> require(Predicate<? super T> predicate);

    /**
     * Returns (this) filtered collection, with methods annotated by provided annotations
     *
     * @param annotations annotations
     * @return IExecutables
     */
    IExecutables<T> require(IAnnotations annotations);

    /**
     * Returns (this) filtered collection, with methods annotated by provided annotation
     *
     * @param annotation annotation
     * @return IExecutables
     */
    IExecutables<T> require(Class<? extends Annotation> annotation);

    /**
     * Returns (this) filtered collection, with methods with provided attribute
     *
     * @param attribute attribute
     * @return IExecutables
     */
    IExecutables<T> require(IAttribute attribute);

    /**
     * Returns (this) filtered collection, with methods with provided attributes
     *
     * @param attributes attributes
     * @return IExecutables
     */
    IExecutables<T> require(IAttributes attributes);

    /**
     * Returns (this) filtered collection, with methods with provided name
     *
     * @param name name
     * @return IExecutables
     */
    IExecutables<T> require(String name);

    /**
     * Returns (this) filtered collection, with methods returning provided type
     *
     * @param returns returns
     * @return IExecutables
     */
    IExecutables<T> require(IClass<?> returns);

    /**
     * Returns (this) filtered collection, with methods taking identical arguments as provided ones
     *
     * @param arguments arguments
     * @return IExecutables
     */
    IExecutables<T> require(IArguments arguments);

    /**
     * Returns (this) filtered collection, with methods taking provided number of arguments
     *
     * @param arguments arguments
     * @return IExecutables
     */
    IExecutables<T> require(int arguments);

    /**
     * Returns (this) filtered collection, without methods matching predicate
     *
     * @param predicate predicate
     * @return IExecutables
     */
    IExecutables<T> discard(Predicate<? super T> predicate);

    /**
     * Returns (this) filtered collection, without methods annotated by provided annotations
     *
     * @param annotations annotations
     * @return IExecutables
     */
    IExecutables<T> discard(IAnnotations annotations);

    /**
     * Returns (this) filtered collection, without methods annotated by provided annotation
     *
     * @param annotation annotation
     * @return IExecutables
     */
    IExecutables<T> discard(Class<? extends Annotation> annotation);

    /**
     * Returns (this) filtered collection, without methods with provided attribute
     *
     * @param attribute attribute
     * @return IExecutables
     */
    IExecutables<T> discard(IAttribute attribute);

    /**
     * Returns (this) filtered collection, without methods with provided attributes
     *
     * @param attributes attributes
     * @return IExecutables
     */
    IExecutables<T> discard(IAttributes attributes);

    /**
     * Returns (this) filtered collection, without methods with provided name
     *
     * @param name name
     * @return IExecutables
     */
    IExecutables<T> discard(String name);

    /**
     * Returns (this) filtered collection, without methods returning provided type
     *
     * @param returns returns
     * @return IExecutables
     */
    IExecutables<T> discard(IClass<?> returns);

    /**
     * Returns (this) filtered collection, without methods taking identical arguments as provided ones
     *
     * @param arguments arguments
     * @return IExecutables
     */
    IExecutables<T> discard(IArguments arguments);

    /**
     * Returns (this) filtered collection, without methods taking provided number of arguments
     *
     * @param arguments arguments
     * @return IExecutables
     */
    IExecutables<T> discard(int arguments);

    /**
     * Returns (this) filtered collection, with methods taking arguments compatible with provided types
     *
     * @param arguments arguments
     * @return IExecutables
     */
    IExecutables<T> matches(IClasses arguments);

    /**
     * Returns (this) filtered collection, with methods taking arguments compatible with provided values
     *
     * @param arguments arguments
     * @return IExecutables
     */
    IExecutables<T> accepts(Object... arguments);

    /**
     * Returns mapped collection with type returned by each method.
     *
     * @return IClasses
     */
    IClasses returns();

    /**
     * Returns mapped collection with declaring class of each method.
     *
     * @return IClasses
     */
    @Override
    IClasses parent();

    /**
     * Returns mapped collection with each method converted to MethodHandle.
     *
     * @return IClasses
     */
    CQuery<MethodHandle> handle();

    /**
     * Returns mapped collection with each method converted to accessible method
     *
     * @return IExecutables
     */
    IExecutables<T> accessible();

    /**
     * Returns AnyFunction which accepts any arguments and tries to execute best
     * matching method from this collection.
     *
     * Please note that "best match" isn't strictly defined.
     * It should work in *similiar* way to java compiler searching for best override.
     *
     * @return AnyFunction
     */
    AnyFunction<Object> dispatcher();
}
