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

package net.ranides.assira.reflection.impl;

import java.lang.invoke.MethodHandle;
import java.util.function.Function;
import net.ranides.assira.reflection.*;
import net.ranides.assira.text.StrBuilder;
import net.ranides.assira.trace.ExceptionUtils;
import net.ranides.assira.collection.query.CQueryBuilder;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public abstract class AMethod implements IMethod {
    
    private final IContext context;

    AMethod(IContext context) {
        this.context = context;
    }

    @Override
    public IContext context() {
        return context;
    }
    	
	@Override
	public final IMethods collect() {
		return new AMethods(AHints.EMPTY, CQueryBuilder.of(this));
	}

    @Override
    public final boolean matches(IClasses arguments) {
        return this.arguments().types().issuper(arguments);
    }

    @Override
    public final boolean accepts(Object... arguments) {
        return this.arguments().types().issuper(IClasses.typefor(arguments));
    }

	@Override
	public final Function<Object[], Object> bind(Object that) {
        MethodHandle mh = handle();
        return (args) -> {
            try {
                return mh.invoke(args);
            } catch (Throwable cause) { //NOPMD rethrow idiom
                throw ExceptionUtils.rethrow(cause);
            }
        };
	}
    
    @Override
    public final int hashCode() {
        return name().hashCode();
    }

    @Override
    public final boolean equals(Object object) {
        if(object == this) {
            return true;
        }
        if(!(object instanceof IMethod)) {
            return false;
        }
        IMethod that = (IMethod)object;
        return this.name().equals(that.name())
            && this.arguments().equals(that.arguments())
            && this.returns().equals(that.returns());
    }
    
    @Override
    public String toString() {
        return new StrBuilder()
            .append(returns()).append(" ")
            .append(parent())
            .append(".")
            .append(name())
            .open("(", ")",", ").append(arguments().list()).close()
            .toString();
    }
	
}
