package com.yeejoin.amos.fas.business.action.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * <pre>
 * 反射工具类
 * </pre>
 *
 * @author HK
 * @version $Id: ReflectUtil.java, v 0.1 2017年12月25日 下午8:05:45 HK Exp $
 */
public class ReflectUtil
{
	private final static Logger logger = LoggerFactory
			.getLogger(ReflectUtil.class);

	/**
	 * 
	 * <pre>
	 * 设置属性值
	 * </pre>
	 *
	 * @param target
	 *            目标对象
	 * @param fname
	 *            字段名称
	 * @param ftype
	 *            字典类型
	 * @param fvalue
	 *            字段值
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void setFieldValue(Object target, String fname, Class ftype,
			Object fvalue)
	{ // 设置字段值 如：username 字段,setUsername(String username)
		if (target == null || fname == null || "".equals(fname)
				|| (fvalue != null
						&& !ftype.isAssignableFrom(fvalue.getClass())))
		{// 如果类型不匹配，直接退出
			return;
		}
		Class clazz = target.getClass();
		try
		{ // 先通过setXxx()方法设置类属性值
			String methodname = "set" + Character.toUpperCase(fname.charAt(0))
					+ fname.substring(1);
			// System.out.println(methodname);
			Method method = clazz.getDeclaredMethod(methodname, ftype); // 获取定义的方法
			if (!Modifier.isPublic(method.getModifiers()))
			{ // 设置非共有方法权限
				ReflectionUtils.makeAccessible(method);
			}
			method.invoke(target, fvalue); // 执行方法回调
		}
		catch (Exception me)
		{// 如果set方法不存在，则直接设置类属性值
			try
			{
				Field field = clazz.getDeclaredField(fname); // 获取定义的类属性
				if (!Modifier.isPublic(field.getModifiers()))
				{ // 设置非共有类属性权限
					ReflectionUtils.makeAccessible(field);
				}
				field.set(target, fvalue); // 设置类属性值

			}
			catch (Exception fe)
			{
				if (logger.isDebugEnabled())
				{
					logger.debug(fe.getMessage());
				}
			}
		}
	}

	/**
	 * 
	 * <pre>
	 * 获取属性值
	 * </pre>
	 *
	 * @param target
	 *            目标对象
	 * @param fname
	 *            字段名称
	 * @param ftype
	 *            字段类型
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Object getFieldValue(Object target, String fname, Class ftype)
	{// 获取字段值 如：username 字段,getUsername()
		if (target == null || fname == null || "".equals(fname))
		{
			return null;
		}
		Class clazz = target.getClass();
		try
		{ // 先通过getXxx()方法获取类属性值
			String methodname = "get" + Character.toUpperCase(fname.charAt(0))
					+ fname.substring(1);
			// System.out.println(methodname);
			Method method = clazz.getDeclaredMethod(methodname); // 获取定义的方法
			if (!Modifier.isPublic(method.getModifiers()))
			{ // 设置非共有方法权限
				ReflectionUtils.makeAccessible(method);
			}
			return method.invoke(target); // 方法回调，返回值
		}
		catch (Exception me)
		{// 如果get方法不存在，则直接获取类属性值
			if (logger.isDebugEnabled())
			{
				logger.debug(me.getMessage());
			}
			try
			{
				Field field = clazz.getDeclaredField(fname); // 获取定义的类属性
				if (!Modifier.isPublic(field.getModifiers()))
				{ // 设置非共有类属性权限
					ReflectionUtils.makeAccessible(field);
				}
				return field.get(target);// 返回类属性值
			}
			catch (Exception fe)
			{
				if (logger.isDebugEnabled())
				{
					logger.debug(fe.getMessage());
				}
			}
		}
		return null;
	}

	public static <E> E newInstance(final Class<? extends E> clazz)
	{
		try
		{
			return clazz.newInstance();
		}
		catch (final Exception e)
		{
			logger.warn("Could not instantiate {}: {}", clazz, e);
			if (e instanceof RuntimeException)
			{
				throw (RuntimeException) e;
			}
			throw new IllegalStateException(e);
		}
	}

	public static <A extends Annotation> A getAnnotation(
			final Enum<?> enumConstant, final Class<A> annotationClass)
	{
		try
		{
			final Field field = enumConstant.getClass()
					.getDeclaredField(enumConstant.name());
			return getAnnotation(field, annotationClass);
		}
		catch (final Exception e)
		{
			throw new IllegalStateException(e);
		}
	}

	public static <A extends Annotation> A getAnnotation(
			final AnnotatedElement element, final Class<A> annotationClass)
	{
		final A annotation = element.getAnnotation(annotationClass);
		if (annotation == null && element instanceof Method)
		{
			// check for annotations on overridden methods. Since Java 8
			// those are not returned by .getAnnotation(...)
			final Method m = (Method) element;
			final Class<?> declaringClass = m.getDeclaringClass();
			final Class<?> superClass = declaringClass.getSuperclass();
			final String methodName = m.getName();
			final Class<?>[] methodParameterTypes = m.getParameterTypes();
			if (superClass != null)
			{
				try
				{
					final Method overriddenMethod = superClass
							.getMethod(methodName, methodParameterTypes);
					return getAnnotation(overriddenMethod, annotationClass);
				}
				catch (final NoSuchMethodException e)
				{
					logger.debug("Failed to get overridden method '{}' from {}",
							methodName, superClass);
				}
			}

			// check for annotations on interface methods too.
			final Class<?>[] interfaces = declaringClass.getInterfaces();
			for (final Class<?> interfaceClass : interfaces)
			{
				try
				{
					final Method overriddenMethod = interfaceClass
							.getMethod(methodName, methodParameterTypes);
					return getAnnotation(overriddenMethod, annotationClass);
				}
				catch (final NoSuchMethodException e)
				{
					logger.debug("Failed to get overridden method '{}' from {}",
							methodName, interfaceClass);
				}
			}
		}
		if(annotation == null && element instanceof Class<?>){
			final Class<?> clazz = (Class<?>) element;
//			final Class<?> declaringClass = clazz.getDeclaringClass();
			final Class<?> superClass = clazz.getSuperclass();
			if (superClass != null)
			{
				return getAnnotation(superClass, annotationClass);
			}
		}
		return annotation;
	}

	public static boolean isAnnotationPresent(final Enum<?> enumConstant,
			final Class<? extends Annotation> annotationClass)
	{
		try
		{
			final Field field = enumConstant.getClass()
					.getDeclaredField(enumConstant.name());
			return isAnnotationPresent(field, annotationClass);
		}
		catch (final Exception e)
		{
			throw new IllegalStateException(e);
		}
	}

	public static boolean isAnnotationPresent(final AnnotatedElement element,
			final Class<? extends Annotation> annotationClass)
	{
		return getAnnotation(element, annotationClass) != null;
	}
	
	
	public static Field[] getAllFields(String clazzName,
			final Class<? extends Annotation> withAnnotation) throws ClassNotFoundException
	{
		final List<Field> result = new ArrayList<>();

		final Field[] fields = getAllFields(Class.forName(clazzName));
		for (final Field field : fields)
		{
			if (isAnnotationPresent(field, withAnnotation))
			{
				result.add(field);
			}
		}

		return result.toArray(new Field[result.size()]);
	}

	public static Field[] getAllFields(final Class<?> clazz,
			final Class<? extends Annotation> withAnnotation)
	{
		final List<Field> result = new ArrayList<>();

		final Field[] fields = getAllFields(clazz);
		for (final Field field : fields)
		{
			if (isAnnotationPresent(field, withAnnotation))
			{
				result.add(field);
			}
		}

		return result.toArray(new Field[result.size()]);
	}

	public static Field[] getAllFields(final Class<?> clazz)
	{
		final List<Field> allFields = new ArrayList<>();
		addFields(allFields, clazz);
		return allFields.toArray(new Field[allFields.size()]);
	}

	private static void addFields(final List<Field> allFields,
			final Class<?> clazz)
	{
		addFields(allFields, clazz, false);
	}

	private static void addFields(final List<Field> allFields,
			final Class<?> clazz, final boolean excludeSynthetic)
	{
		if (clazz == Object.class)
		{
			return;
		}

		final Field[] f = clazz.getDeclaredFields();

		for (final Field field : f)
		{
			if (excludeSynthetic && field.isSynthetic())
			{
				continue;
			}

			allFields.add(field);
		}

		final Class<?> superclass = clazz.getSuperclass();
		addFields(allFields, superclass, excludeSynthetic);
	}
	
    public static Method[] getMethods(final Class<?> clazz, final Class<? extends Annotation> withAnnotation) {
        final List<Method> result = new ArrayList<>();

        final Method[] methods = getMethods(clazz);
        for (final Method method : methods) {
            if (isAnnotationPresent(method, withAnnotation)) {
                result.add(method);
            }
        }

        return result.toArray(new Method[result.size()]);
    }
    
    public static Method[] getMethods(final Class<?> clazz) {
        final List<Method> allMethods = new ArrayList<>();
        addMethods(allMethods, clazz);

        return allMethods.toArray(new Method[allMethods.size()]);
    }
    
    private static void addMethods(final List<Method> allMethods, final Class<?> clazz) {
        if (clazz == Object.class || clazz == null) {
            return;
        }

        final Method[] methods = clazz.getMethods();
        for (final Method method : methods) {
            final Class<?> declaringClass = method.getDeclaringClass();
            if (declaringClass != Object.class) {
                allMethods.add(method);
            }
        }
    }
}
