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

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.function.Predicate;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.reflection.impl.AClasses;
import net.ranides.assira.reflection.impl.AMethods;
import net.ranides.assira.reflection.impl.FElements;

/**
 * Fluent interface for collection of IClass objects
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
@SuppressWarnings("PMD.TooManyDifferentMethods")
public interface IClasses extends IElements<IClass<?>> {

	/**
     * Factory: returns collection with each type converted to IClass
     *
     * @param types types
     * @return IClasses
     */
    static IClasses typeinfo(Type... types) {
        return IContext.getDefault().typesinfo(types);
    }

    /**
     * Factory: returns collection with each type converted to IClass
     *
     * @param types types
     * @return IClasses
     */
    static IClasses typeinfo(IClass<?>... types) {
        return FElements.newClasses(types);
    }

    /**
     * Factory: returns collection with each object converted to IClass
     *
     * @param objects objects
     * @return IClasses
     */
    static IClasses typefor(Object... objects) {
        return IContext.getDefault().typesfor(objects);
    }

    /**
     * Factory: returns empty collection
     *
     * @return collection
     */
    static IClasses empty() {
        return AClasses.$empty();
    }

    /**
     * Returns (this) filtered collection, with types matching predicate
     *
     * @param predicate predicate
     * @return IClasses
     */
    IClasses require(Predicate<? super IClass<?>> predicate);

    /**
     * Returns (this) filtered collection, with types annotated by provided annotations
     *
     * @param annotations annotations
     * @return IClasses
     */
    IClasses require(IAnnotations annotations);

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

    /**
     * Returns (this) filtered collection, with types with provided attribute
     *
     * @param attribute attribute
     * @return IClasses
     */
    IClasses require(IAttribute attribute);

    /**
     * Returns (this) filtered collection, with types with provided attributes
     *
     * @param attributes attributes
     * @return IClasses
     */
    IClasses require(IAttributes attributes);

    /**
     * Returns (this) filtered collection, with types with provided method
     *
     * @param method method
     * @return IClasses
     */
    IClasses require(IMethod method);

    /**
     * Returns (this) filtered collection, without types matching predicate
     *
     * @param predicate predicate
     * @return IClasses
     */
    IClasses discard(Predicate<? super IClass<?>> predicate);

    /**
     * Returns (this) filtered collection, without types annotated by provided annotations
     *
     * @param annotations annotations
     * @return IClasses
     */
    IClasses discard(IAnnotations annotations);

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

    /**
     * Returns (this) filtered collection, without types with provided attribute
     *
     * @param attribute attribute
     * @return IClasses
     */
    IClasses discard(IAttribute attribute);

    /**
     * Returns (this) filtered collection, without types with provided attributes
     *
     * @param attributes attributes
     * @return IClasses
     */
    IClasses discard(IAttributes attributes);

    /**
     * Returns (this) filtered collection, without types with provided method
     *
     * @param method method
     * @return IClasses
     */
    IClasses discard(IMethod method);

    /**
     * Returns aggregate collection of fields found inside all types.
     *
     * Strategy for members scan:
     *   - by default returns all public fields (including inherited)
     *   - if you use non-public hints, inherited members are still included
     *   - if you add DECLARED hint, inherited members will be discarded.
     *
     * @return IFields
     */
    IFields fields();

    /**
     * Returns aggregate collection of constructors found inside all types.
     *
     * By default returns only public constructors.
     *
     * @return IConstructors
     */
    IConstructors<?> constructors();

    /**
     * Returns aggregate collection of methods found inside all types.
     *
     * Strategy for members scan:
     *   - by default returns all public methods (including inherited)
     *   - if you use non-public hints, inherited members are still included
     *   - if you add DECLARED hint, inherited members will be discarded.
     *
     * @return IMethods
     */
    IMethods methods();

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

    /**
     * Returns aggregate collection with all base classes of each type.
     *
     * @return IClasses
     */
    IClasses parents();

    /**
     * Returns aggregate collection with all interfaces implemented by each type.
     *
     * Strategy for members scan:
     *   - by default returns all implemented interfaces
     *   - if you use DECLARED hint, inherited interfaces will be discarded
     *
     * @return IClasses
     */
    IClasses interfaces();

    /**
     * Returns mapped collection with outer class of each type.
     *
     * @return IClasses
     */
    IClasses outer();

    /**
     * Returns aggregate collection with all inner classes inside each type.
     *
     * Strategy for members scan:
     *   - by default returns all declared, public and non-public classes
     *   - inherited inner classes are not returned
     *
     * @return IClasses
     */
    IClasses inner();

    /**
     * Returns stream with each type mapped to reflective Type
     *
     * @return CQuery
     */
    CQuery<Type> reflective();

    /**
     * Returns stream with each type mapped to raw Class
     *
     * @return CQuery
     */
    CQuery<Class<?>> raw();

    /**
     * Returns true if each type from this collection
     * is superclass of corresponding type from "types" collection.
     *
     * @param types types
     * @return boolean
     */
	boolean isSuper(IClasses types);

    /**
     * Returns true if each type from this collection
     * is subclass of corresponding type from "types" collection.
     *
     * @param types types
     * @return boolean
     */
    boolean isSubclass(IClasses types);

    /**
     * Returns true if each value is instance of corresponding type from this collection.
     *
     * @param values values
     * @return boolean
     */
    boolean isInstance(Object... values);

    /**
     * Returns true if each value is instance of corresponding type from this collection.
     *
     * This is special method:
     * First type from collection is skipped, so size of "values" must be smaller by one.
     *
     * Generally it is written for edge cases, when we process types of arguments,
     * but type of "this" must be processed separately.
     *
     * For example it is used by {@link IExecutable#$accepts1(Object...)}
     *
     * @param values values
     * @return boolean
     */
    boolean $isInstance1(Object... values);
            
}
