package me.j360.dubbo.modules.util.reflect;

import me.j360.dubbo.modules.util.base.ExceptionUtil;
import me.j360.dubbo.modules.util.collection.MapUtil;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;

/**
 * 基于Cglib, 基于字节码生成的快速反射, 比JDK反射的速度快.
 * 
 * 比如反射调用A类的“hello” 方法,cglib会直接生成一个调用a.hello()的FastMethod子类.
 * 
 * 注意: 需要参考本模块的POM文件,显式引用cglib.
 * 
 * @author calvin
 */
public class FastMethodInvoker {

	// 存放cglib的FastClass
	private static ConcurrentMap<Class<?>, FastClass> fastClassMap = MapUtil.newConcurrentHashMap();

	private final FastMethod fastMethod;

	protected FastMethodInvoker(FastMethod fastMethod) {
		this.fastMethod = fastMethod;
	}

	/**
	 * 获取cglib生成的FastMethod,创建方法的FastMethodInvoker实例.
	 */
	public static FastMethodInvoker create(final Class<?> clz, final String methodName, Class<?>... parameterTypes) {
		Method method = ClassUtil.getAccessibleMethod(clz, methodName, parameterTypes);
		if (method == null) {
			throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + clz + ']');
		}
		return build(clz, method);
	}

	/**
	 * 获取cglib生成的FastMethod,创建Getter方法的FastMethodInvoker实例.
	 */
	public static FastMethodInvoker createGetter(final Class<?> clz, final String propertyName) {
		Method method = ClassUtil.getGetterMethod(clz, propertyName);
		if (method == null) {
			throw new IllegalArgumentException(
					"Could not find getter method [" + propertyName + "] on target [" + clz + ']');
		}
		return build(clz, method);
	}

	/**
	 * 获取cglib生成的FastMethod,创建Setter方法的FastMethodInvoker实例.
	 */
	public static FastMethodInvoker createSetter(final Class<?> clz, final String propertyName,
			Class<?> parameterType) {
		Method method = ClassUtil.getSetterMethod(clz, propertyName, parameterType);
		if (method == null) {
			throw new IllegalArgumentException(
					"Could not find getter method [" + propertyName + "] on target [" + clz + ']');
		}
		return build(clz, method);
	}

	private static FastMethodInvoker build(final Class<?> clz, Method method) {
		FastClass fastClz = MapUtil.createIfAbsent(fastClassMap, clz, new MapUtil.ValueCreator<FastClass>() {
			@Override
			public FastClass get() {
				return FastClass.create(clz);
			}
		});

		return new FastMethodInvoker(fastClz.getMethod(method));
	}

	/**
	 * 调用方法
	 */
	@SuppressWarnings("unchecked")
	public <T> T invoke(Object obj, Object... args) {
		try {
			return (T) fastMethod.invoke(obj, args);
		} catch (Exception e) {
			throw ExceptionUtil.uncheckedAndWrap(e);
		}
	}
}