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

package net.ranides.assira.reflection.impl.bean;

import lombok.RequiredArgsConstructor;
import net.ranides.assira.functional.special.AnyFunction;
import net.ranides.assira.reflection.*;

import java.io.Serializable;
import java.util.Collection;

/**
 * Default implementation of BeanMethod.
 * Stores reference to one, nonambiguous method.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class RBeanMethod implements BeanMethod, Serializable {
    
    private final IMethod method;
    
    RBeanMethod(Collection<BeanException> errors, IMethod method) {
        this.method = method;

        if(!method.parent().attributes().has(IAttribute.PUBLIC)) {
            method.accessible();
        }
    }

    @Override
    public String name() {
        return method.name();
    }

    @Override
    public IClass<?> parent() {
        return method.parent();
    }

    @Override
    public IClass<?> returns() {
        return method.returns();
    }

    @Override
    public IArguments arguments() {
        return method.arguments();
    }

    @Override
    public BeanMethodBinding bind(Object that) {
        if(null == that) {
            throw new BeanException(method.parent(), "non-static method requires non-null 'this'");
        }
        return new RBinding(that);
    }
    
    @Override
    public Object invoke(Object that) {
        return method.invoke(that);
    }

    @Override
    public Object invoke(Object that, Object... arguments) {
        return method.invoke(that, arguments);
    }

    @Override
    public Object call(Object... arguments) {
        return method.call(arguments);
    }

    @Override
    public Object apply(Object that, Object[] arguments) {
        return method.apply(that, arguments);
    }
    
    @Override
    public boolean matches(IClass<?>... arguments) {
        return matches(IClasses.typeinfo(arguments));
    }

    @Override
    public boolean matches(IClasses arguments) {
        return method.matches(arguments);
    }

    @Override
    public boolean accepts(Object... arguments) {
        return method.accepts(arguments);
    }

    @RequiredArgsConstructor
    private class RBinding implements BeanMethodBinding {

        private final Object that;

        @Override
        public String name() {
            return method.name();
        }

        @Override
        public boolean matches(IClasses arguments) {
            return method.matches(arguments);
        }

        @Override
        public Object apply(Object[] arguments) {
            return method.apply(that, arguments);
        }
    }
}
