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

package net.ranides.assira.reflection.impl;

import lombok.experimental.UtilityClass;

import net.ranides.assira.collection.maps.OrderedMap;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.reflection.*;
import net.ranides.assira.reflection.util.AnnotationUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * Internal factory, used for aggregating values inside appropriate IElements container.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
@UtilityClass
public final class FElements {

    /**
     * Creates IAnnotations
     *
     * @param hints hints
     * @param stream stream
     * @return IAnnotations
     */
    public static IAnnotations newAnnotations(IHints hints, CQuery<Annotation> stream) {
        return new AAnnotations(hints, stream);
    }

    /**
     * Creates IAnnotations
     *
     * @param annotations annotations
     * @return IAnnotations
     */
    @SafeVarargs
    public static IAnnotations newAnnotations(Class<? extends Annotation>... annotations) {
        return newAnnotations(IHints.none(), CQuery.from().array(annotations).map(a -> AnnotationUtils.make(a)));
    }

    /**
     * Creates IAnnotations
     *
     * @param annotations annotations
     * @return IAnnotations
     */
    public static IAnnotations newAnnotations(Annotation... annotations) {
        return newAnnotations(IHints.none(), CQuery.from().array(annotations));
    }

    /**
     * Creates IArguments
     *
     * @param hints hints
     * @param stream stream
     * @return IArguments
     */
    public static IArguments newArguments(IHints hints, CQuery<IArgument> stream) {
        return new AArguments(hints, stream);
    }

    /**
     * Creates IArguments
     *
     * @param types types
     * @return IArguments
     */
    public static IArguments newArguments(IClass<?>... types) {
        return newArguments(IHints.none(), CQuery.from().array(types).map(v -> FArgument.newArgument(v)));
    }

    /**
     * Creates IArguments
     *
     * @param types types
     * @return IArguments
     */
    public static IArguments newArguments(Class<?>... types) {
        return newArguments(IHints.none(), CQuery.from().array(types).map(v -> FArgument.newArgument(v)));
    }

    /**
     * Creates IArguments
     *
     * @param arguments arguments
     * @return IArguments
     */
    public static IArguments newArguments(OrderedMap<String, IClass<?>> arguments) {
        return newArguments(IHints.none(), CQuery.from().collection(arguments.entrySet()).map(v -> FArgument.newArgument(v)));
    }

    /**
     * Creates IClasses
     *
     * @param array array
     * @return IClasses
     */
    public static IClasses newClasses(IClass<?>[] array) {
        return newClasses(IHints.none(), CQuery.from().array(array));
    }

    /**
     * Creates IClasses
     *
     * @param hints hints
     * @param stream stream
     * @return IClasses
     */
    public static IClasses newClasses(IHints hints, CQuery<IClass<?>> stream) {
        return new AClasses(hints, stream);
    }

    /**
     * Creates IFields
     *
     * @param hints hints
     * @param stream stream
     * @return IFields
     */
    public static IFields newFields(IHints hints, CQuery<IField> stream) {
        return new AFields(hints, stream);
    }

    /**
     * Creates IFields
     *
     * @param fields fields
     * @return IFields
     */
    public static IFields newFields(Field... fields) {
        return newFields(IHints.none(), CQuery.from().array(fields).map(IField::typeinfo));
    }

    /**
     * Creates IHints
     * Returns AHints internal class, because we use inside package some helper methods from AHints
     *
     * @return AHints
     */
    public static AHints newHints() {
        return new AHints(IAttributes.of(),IAttributes.of());
    }

    /**
     * Creates IMethods
     *
     * @param hints hints
     * @param stream stream
     * @return IMethods
     */
    public static IMethods newMethods(IHints hints, CQuery<IMethod> stream) {
        return new AMethods(hints, stream);
    }

    /**
     * Creates IConstructors
     *
     * @param hints hints
     * @param stream stream
     * @param <T> T
     * @return IConstructors
     */
    public static <T> IConstructors<T> newConstructors(IHints hints, CQuery<IConstructor<T>> stream) {
        return new AConstructors<>(hints, stream);
    }

    /**
     * Creates IExecutables
     *
     * @param hints hints
     * @param stream stream
     * @return IExecutables
     */
    public static IExecutables<?> newExecutables(IHints hints, CQuery<IExecutable> stream) {
        return new AExecutables(hints, stream);
    }

    /**
     * Creates IMethods
     *
     * @param methods methods
     * @return IMethods
     */
    public static IMethods newMethods(Method... methods) {
        return newMethods(IHints.none(), CQuery.from().array(methods).map(IMethod::typeinfo));
    }

    /**
     * Creates IConstructors
     *
     * @param methods methods
     * @param <T> T
     * @return IConstructors
     */
    @SafeVarargs
    public static <T> IConstructors<T> newConstructors(Constructor<T>... methods) {
        return newConstructors(IHints.none(), CQuery.from().array(methods).map(IConstructor::typeinfo));
    }

    /**
     * Creates IExecutables
     *
     * @param methods methods
     * @param <T> T
     * @return IExecutables
     */
    @SafeVarargs
    public static <T> IExecutables newExecutables(Constructor<T>... methods) {
        return newExecutables(IHints.none(), CQuery.from().array(methods).map(IConstructor::typeinfo));
    }
    
}
