/*
 * @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.functional.special.AnyFunction;

/**
 * Reflective model for method defined in JavaBean.
 * Instances of BeanMethod can be obtained from BeanModel.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public interface BeanMethod {

    /**
     * Returns class declaring method
     *
     * @return type
     */
    IClass<?> parent();

    /**
     * Name of method
     *
     * @return string
     */
    String name();

    IClass<?> returns();

    IArguments arguments();

    /**
     * Creates new function with "this" variable binded to specified object.
     *
     * @param that that
     * @return new function
     */
    BeanMethodBinding bind(Object that);

    /**
     * Invokes method with specified arguments. First argument is used as "this".
     * Behaviour of invoke is identical as {@link IMethod#invoke(Object that)}
     *
     * @param that that
     * @return object
     */
    Object invoke(Object that);

    /**
     * Invokes method with specified arguments. First argument is used as "this".
     * Behaviour of invoke is identical as {@link IMethod#invoke(Object that, Object... arguments)}
     *
     * @param that that
     * @param arguments arguments
     * @return object
     */
    Object invoke(Object that, Object... arguments);

    /**
     * Invokes method with specified arguments. First argument is used as "this".
     * Behaviour of invoke is identical as {@link IMethod#call(Object... arguments)}
     *
     * @param arguments arguments
     * @return object
     */
    Object call(Object... arguments);

    /**
     * Invokes method with specified arguments. First argument is used as "this".
     * Behaviour of invoke is identical as {@link IMethod#apply(Object that, Object[] arguments)}
     *
     * @param that that
     * @param arguments arguments
     * @return object
     */
    Object apply(Object that, Object[] arguments);

    /**
     * Returns true if this method can be invoked with arguments of specified types.
     * Behaviour of invoke is identical as {@link IMethod#matches(IClasses arguments)}
     *
     * @param arguments arguments
     * @return bool
     */
    boolean matches(IClasses arguments);

    /**
     * Returns true if this method can be invoked with arguments of specified types.
     * Behaviour of invoke is identical as {@link IMethod#matches(IClasses arguments)}
     *
     * @param arguments arguments
     * @return bool
     */
    default boolean matches(IClass<?>... arguments) {
        return matches(IClasses.typeinfo(arguments));
    }

    /**
     * Returns true if this method can be invoked with arguments of specified types.
     * Behaviour of invoke is identical as {@link IMethod#matches(Class... arguments)}
     *
     * @param arguments arguments
     * @return bool
     */
    default boolean matches(Class<?>... arguments) {
        return matches(IClasses.typeinfo(arguments));
    }

    /**
     * Returns true if this method can be invoked without arguments.
     * Effectively it means that checks if method is static.
     * Behaviour of invoke is identical as {@link IMethod#matches()}
     *
     * @return bool
     */
    default boolean matches() {
        return matches(IClasses.empty());
    }

    /**
     * Returns true if this method can be invoked with specified argument values.
     * Behaviour of invoke is identical as {@link IMethod#accepts(Object... arguments)}
     *
     * @param arguments arguments
     * @return bool
     */
    boolean accepts(Object... arguments);
        
}
