package com.jeeplus.common.utils.reflect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 获取Class信息的工具类
 * 
 * 1. 取shortClassName 和 packageName
 * 
 * 2. 获取全部父类，全部接口，以及全部Annotation
 * 
 * 3. 获取标注了annotation的所有属性和方法
 * 
 * 4. 获取方法与属性 (兼容了原始类型的参数, 并默认将方法与属性设为可访问)
 * 
 * 5. 其他杂项
 * 
 * @author calvin
 */
public class ClassUtil {

	private static final String CGLIB_CLASS_SEPARATOR = "$$";

	private static Logger logger = LoggerFactory.getLogger(ClassUtil.class);

	private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new IdentityHashMap<Class<?>, Class<?>>(8);

	static {
		primitiveWrapperTypeMap.put(Boolean.class, Boolean.TYPE);
		ClassUtil.primitiveWrapperTypeMap.put(Byte.class, Byte.TYPE);
		ClassUtil.primitiveWrapperTypeMap.put(Character.class, Character.TYPE);
		ClassUtil.primitiveWrapperTypeMap.put(Double.class, Double.TYPE);
		ClassUtil.primitiveWrapperTypeMap.put(Float.class, Float.TYPE);
		ClassUtil.primitiveWrapperTypeMap.put(Integer.class, Integer.TYPE);
		ClassUtil.primitiveWrapperTypeMap.put(Long.class, Long.TYPE);
		ClassUtil.primitiveWrapperTypeMap.put(Short.class, Short.TYPE);
	}

	private static final String SETTER_PREFIX = "set";
	private static final String GETTER_PREFIX = "get";
	private static final String IS_PREFIX = "is";

	////////// shortClassName 和 packageName//////////
	/**
	 * 返回Class名, 不包含PackageName.
	 * 
	 * 内部类的话，返回"主类.内部类"
	 */
	public static String getShortClassName(final Class<?> cls) {
		return ClassUtils.getShortClassName(cls);
	}

	/**
	 * 返回Class名，不包含PackageName
	 * 
	 * 内部类的话，返回"主类.内部类"
	 */
	public static String getShortClassName(final String className) {
		return ClassUtils.getShortClassName(className);
	}

	/**
	 * 返回PackageName
	 */
	public static String getPackageName(final Class<?> cls) {
		return ClassUtils.getPackageName(cls);
	}

	/**
	 * 返回PackageName
	 */
	public static String getPackageName(final String className) {
		return ClassUtils.getPackageName(className);
	}

	////////// 获取全部父类，全部接口，以及全部Annotation//////////
	/**
	 * 递归返回所有的SupperClasses，包含Object.class
	 */
	public static List<Class<?>> getAllSuperclasses(final Class<?> cls) {
		return ClassUtils.getAllSuperclasses(cls);
	}

	/**
	 * 递归返回本类及所有基类继承的接口，及接口继承的接口，比Spring中的相同实现完整
	 */
	public static List<Class<?>> getAllInterfaces(final Class<?> cls) {
		return ClassUtils.getAllInterfaces(cls);
	}

	/**
	 * 递归Class所有的Annotation，一个最彻底的实现.
	 * 
	 * 包括所有基类，所有接口的Annotation，同时支持Spring风格的Annotation继承的父Annotation，
	 */
	public static Set<Annotation> getAllAnnotations(final Class<?> cls) {
		List<Class<?>> allTypes = getAllSuperclasses(cls);
		allTypes.addAll(getAllInterfaces(cls));
		allTypes.add(cls);

		Set<Annotation> anns = new HashSet<Annotation>();
		for (Class<?> type : allTypes) {
			anns.addAll(Arrays.asList(type.getDeclaredAnnotations()));
		}

		Set<Annotation> superAnnotations = new HashSet<Annotation>();
		for (Annotation ann : anns) {
			getSupperAnnotations(ann.annotationType(), superAnnotations);
		}

		anns.addAll(superAnnotations);

		return anns;
	}

	private static <A extends Annotation> void getSupperAnnotations(Class<A> annotationType, Set<Annotation> visited) {
		Annotation[] anns = annotationType.getDeclaredAnnotations();

		for (Annotation ann : anns) {
			if (!ann.annotationType().getName().startsWith("java.lang") && visited.add(ann)) {
				getSupperAnnotations(ann.annotationType(), visited);
			}
		}
	}

	//// 获取标注了annotation的所有属性和方法////////

	/**
	 * 找出所有标注了该annotation的公共属性，循环遍历父类.
	 * 
	 * 暂未支持Spring风格Annotation继承Annotation
	 * 
	 * from org.unitils.util.AnnotationUtils
	 */
	public static <T extends Annotation> Set<Field> getAnnotatedPublicFields(Class<? extends Object> clazz,
			Class<T> annotation) {

		if (Object.class.equals(clazz)) {
			return Collections.emptySet();
		}

		Set<Field> annotatedFields = new HashSet<Field>();
		Field[] fields = clazz.getFields();

		for (Field field : fields) {
			if (field.getAnnotation(annotation) != null) {
				annotatedFields.add(field);
			}
		}

		return annotatedFields;
	}

	/**
	 * 找出所有标注了该annotation的属性，循环遍历父类，包含private属性.
	 * 
	 * 暂未支持Spring风格Annotation继承Annotation
	 * 
	 * from org.unitils.util.AnnotationUtils
	 */
	public static <T extends Annotation> Set<Field> getAnnotatedFields(Class<? extends Object> clazz,
			Class<T> annotation) {
		if (Object.class.equals(clazz)) {
			return Collections.emptySet();
		}
		Set<Field> annotatedFields = new HashSet<Field>();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (field.getAnnotation(annotation) != null) {
				annotatedFields.add(field);
			}
		}
		annotatedFields.addAll(getAnnotatedFields(clazz.getSuperclass(), annotation));
		return annotatedFields;
	}

	/**
	 * 找出所有标注了该annotation的公共方法(含父类的公共函数)，循环其接口.
	 * 
	 * 暂未支持Spring风格Annotation继承Annotation
	 * 
	 * 另，如果子类重载父类的公共函数，父类函数上的annotation不会继承，只有接口上的annotation会被继承.
	 */
	public static <T extends Annotation> Set<Method> getAnnotatedPublicMethods(Class<?> clazz, Class<T> annotation) {
		// 已递归到Objebt.class, 停止递归
		if (Object.class.equals(clazz)) {
			return Collections.emptySet();
		}

		List<Class<?>> ifcs = ClassUtils.getAllInterfaces(clazz);
		Set<Method> annotatedMethods = new HashSet<Method>();

		// 遍历当前类的所有公共方法
		Method[] methods = clazz.getMethods();

		for (Method method : methods) {
			// 如果当前方法有标注，或定义了该方法的所有接口有标注
			if (method.getAnnotation(annotation) != null || searchOnInterfaces(method, annotation, ifcs)) {
				annotatedMethods.add(method);
			}
		}

		return annotatedMethods;
	}

	private static <T extends Annotation> boolean searchOnInterfaces(Method method, Class<T> annotationType,
			List<Class<?>> ifcs) {
		for (Class<?> iface : ifcs) {
			try {
				Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());
				if (equivalentMethod.getAnnotation(annotationType) != null) {
					return true;
				}
			} catch (NoSuchMethodException ex) {
				// Skip this interface - it doesn't have the method...
			}
		}
		return false;
	}

	///////// 获取方法////
	/**
	 * 循环遍历，按属性名获取前缀为get或is的函数，并设为可访问
	 */
	public static Method getSetterMethod(Class<?> clazz, String propertyName, Class<?> parameterType) {
		String setterMethodName = ClassUtil.SETTER_PREFIX + StringUtils.capitalize(propertyName);
		return ClassUtil.getAccessibleMethod(clazz, setterMethodName, parameterType);
	}

	/**
	 * 循环遍历，按属性名获取前缀为set的函数，并设为可访问
	 */
	public static Method getGetterMethod(Class<?> clazz, String propertyName) {
		String getterMethodName = ClassUtil.GETTER_PREFIX + StringUtils.capitalize(propertyName);

		Method method = ClassUtil.getAccessibleMethod(clazz, getterMethodName);

		// retry on another name
		if (method == null) {
			getterMethodName = ClassUtil.IS_PREFIX + StringUtils.capitalize(propertyName);
			method = ClassUtil.getAccessibleMethod(clazz, getterMethodName);
		}
		return method;
	}

	/**
	 * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
	 * 
	 * 如向上转型到Object仍无法找到, 返回null.
	 * 
	 * 因为class.getFiled(); 不能获取父类的private函数, 因此采用循环向上的getDeclaredField();
	 */
	public static Field getAccessibleField(final Class clazz, final String fieldName) {
		Validate.notNull(clazz, "clazz can't be null");
		Validate.notEmpty(fieldName, "fieldName can't be blank");
		for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				Field field = superClass.getDeclaredField(fieldName);
				ClassUtil.makeAccessible(field);
				return field;
			} catch (NoSuchFieldException e) {// NOSONAR
				// Field不在当前类定义,继续向上转型
			}
		}
		return null;
	}

	/**
	 * 循环向上转型, 获取对象的DeclaredMethod, 并强制设置为可访问.
	 * 
	 * 如向上转型到Object仍无法找到, 返回null.
	 * 
	 * 匹配函数名+参数类型.
	 * 
	 * 因为class.getFiled() 不能获取父类的private函数, 因此采用循环向上的getDeclaredField();
	 */
	public static Method getAccessibleMethod(final Class<?> clazz, final String methodName,
			Class<?>... parameterTypes) {
		Validate.notNull(clazz, "class can't be null");
		Validate.notEmpty(methodName, "methodName can't be blank");
		Class[] theParameterTypes = ArrayUtils.nullToEmpty(parameterTypes);

		// 处理原子类型与对象类型的兼容
		ClassUtil.wrapClassses(theParameterTypes);

		for (Class<?> searchType = clazz; searchType != Object.class; searchType = searchType.getSuperclass()) {
			try {
				Method method = searchType.getDeclaredMethod(methodName, theParameterTypes);
				ClassUtil.makeAccessible(method);
				return method;
			} catch (NoSuchMethodException e) {
				// Method不在当前类定义,继续向上转型
			}
		}
		return null;
	}

	/**
	 * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
	 * 
	 * 如向上转型到Object仍无法找到, 返回null.
	 * 
	 * 只匹配函数名, 如果有多个同名函数返回第一个
	 * 
	 * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
	 * 
	 * 因为class.getMethods() 不能获取父类的private函数, 因此采用循环向上的getMethods();
	 */
	public static Method getAccessibleMethodByName(final Class clazz, final String methodName) {
		Validate.notNull(clazz, "clazz can't be null");
		Validate.notEmpty(methodName, "methodName can't be blank");

		for (Class<?> searchType = clazz; searchType != Object.class; searchType = searchType.getSuperclass()) {
			Method[] methods = searchType.getDeclaredMethods();
			for (Method method : methods) {
				if (method.getName().equals(methodName)) {
					ClassUtil.makeAccessible(method);
					return method;
				}
			}
		}
		return null;
	}

	/**
	 * 改变private/protected的方法为public，尽量不调用实际改动的语句，避免JDK的SecurityManager抱怨。
	 */
	public static void makeAccessible(Method method) {
		if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
				&& !method.isAccessible()) {
			method.setAccessible(true);
		}
	}

	/**
	 * 改变private/protected的成员变量为public，尽量不调用实际改动的语句，避免JDK的SecurityManager抱怨。
	 */
	public static void makeAccessible(Field field) {
		if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
				|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
			field.setAccessible(true);
		}
	}

	/**
	 * 兼容原子类型与非原子类型的转换，不考虑依赖两者不同来区分不同函数的场景
	 */
	private static void wrapClassses(Class<?>[] source) {
		for (int i = 0; i < source.length; i++) {
			Class<?> wrapClass = primitiveWrapperTypeMap.get(source[i]);
			if (wrapClass != null) {
				source[i] = wrapClass;
			}
		}
	}

	/////////// 杂项 /////////
	/**
	 * From Spring, 按顺序获取默认ClassLoader
	 * 
	 * 1. Thread.currentThread().getContextClassLoader()
	 * 
	 * 2. ClassUtil的加载ClassLoader
	 * 
	 * 3. SystemClassLoader
	 */
	public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		} catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
				try {
					cl = ClassLoader.getSystemClassLoader();
				} catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
	}

	/**
	 * 获取CGLib处理过后的实体的原Class.
	 */
	public static Class<?> unwrapCglib(Object instance) {
		Validate.notNull(instance, "Instance must not be null");
		Class<?> clazz = instance.getClass();
		if ((clazz != null) && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
			Class<?> superClass = clazz.getSuperclass();
			if ((superClass != null) && !Object.class.equals(superClass)) {
				return superClass;
			}
		}
		return clazz;
	}

	/**
	 * 通过反射, 获得Class定义中声明的泛型参数的类型,
	 * 
	 * 注意泛型必须定义在父类处. 这是唯一可以通过反射从泛型获得Class实例的地方.
	 * 
	 * 如无法找到, 返回Object.class.
	 * 
	 * eg. public UserDao extends HibernateDao<User>
	 * 
	 * @param clazz The class to introspect
	 * @return the first generic declaration, or Object.class if cannot be determined
	 */
	public static <T> Class<T> getClassGenricType(final Class clazz) {
		return getClassGenricType(clazz, 0);
	}

	/**
	 * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
	 * 
	 * 注意泛型必须定义在父类处. 这是唯一可以通过反射从泛型获得Class实例的地方.
	 * 
	 * 如无法找到, 返回Object.class.
	 * 
	 * 如public UserDao extends HibernateDao<User,Long>
	 * 
	 * @param clazz clazz The class to introspect
	 * @param index the Index of the generic ddeclaration, start from 0.
	 * @return the index generic declaration, or Object.class if cannot be determined
	 */
	public static Class getClassGenricType(final Class clazz, final int index) {

		Type genType = clazz.getGenericSuperclass();

		if (!(genType instanceof ParameterizedType)) {
			logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
			return Object.class;
		}

		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

		if ((index >= params.length) || (index < 0)) {
			logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
					+ params.length);
			return Object.class;
		}
		if (!(params[index] instanceof Class)) {
			logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
			return Object.class;
		}

		return (Class) params[index];
	}

	/**
	 * 探测类是否存在classpath中
	 */
	public static boolean isPresent(String className, ClassLoader classLoader) {
		try {
			classLoader.loadClass(className);
			return true;
		} catch (Throwable ex) {
			// Class or one of its dependencies is not present...
			return false;
		}
	}

}
