package ink.wgink.gateway.util;

import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: ReflectUtil
 * @Description: 反射工具
 * @Author: WangGeng
 * @Date: 2021/2/25 20:10
 * @Version: 1.0
 **/
public class ReflectUtil {

    /**
     * 获取单例对象
     *
     * @param className  完整类路径
     * @param superClass 父级类或接口，没有就是当前类
     * @param <T>
     * @return
     * @throws ReflectException
     */
    public static <T> T getSingleInstance(String className, Class<T> superClass) throws ReflectException {
        return getSingleInstance(className, superClass, null);
    }

    /**
     * 获取单例对象
     *
     * @param className             完整类路径
     * @param superClass            父级类或接口，没有就是当前类
     * @param getInstanceMethodName 单例方法名，默认 getInstance
     * @param <T>
     * @return
     * @throws ReflectException
     */
    public static <T> T getSingleInstance(String className, Class<T> superClass, String getInstanceMethodName) throws ReflectException {
        if (className == null) {
            throw new ReflectException("className 不能为空");
        }
        if (superClass == null) {
            throw new ReflectException("superClass 不能为空");
        }
        Object singleInstanceObject = null;
        try {
            Class clazz = Class.forName(className);
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (StringUtils.equals(method.getName(), StringUtils.isBlank(getInstanceMethodName) ? "getInstance" : getInstanceMethodName)) {
                    singleInstanceObject = method.invoke(null);
                }
            }
        } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) {
            throw new ReflectException(e.getMessage(), e);
        }
        if (singleInstanceObject == null) {
            throw new ReflectException("非单例对象");
        }
        return (T) singleInstanceObject;
    }

    /**
     * bean转bean，相同属性的值可以相互复制
     *
     * @param srcBean       源bean
     * @param destBeanClass 转换后的bean类
     * @param <T>
     * @return
     * @throws ReflectException
     */
    public static <T> T beanToBean(Object srcBean, Class<T> destBeanClass) throws ReflectException {
        try {
            Field[] srcFieldArray = srcBean.getClass().getDeclaredFields();
            Field[] destFieldArray = destBeanClass.getDeclaredFields();
            T t = destBeanClass.newInstance();
            for (Field field : srcFieldArray) {
                for (Field destField : destFieldArray) {
                    String destFieldName = destField.getName();
                    if (!StringUtils.equals(field.getName(), destFieldName)) {
                        continue;
                    }
                    String firstLetter = destFieldName.substring(0, 1).toUpperCase();
                    String firstUpperMethodName = firstLetter + destFieldName.substring(1, destFieldName.length());
                    Method getMethod = srcBean.getClass().getMethod("get" + firstUpperMethodName);
                    Object value = getMethod.invoke(srcBean);
                    Method setMethod = destBeanClass.getMethod("set" + firstUpperMethodName, value.getClass());
                    setMethod.invoke(t, value);
                }
            }
            return t;
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            throw new ReflectException(e.getMessage(), e);
        }
    }

    public static class ReflectException extends ReflectiveOperationException {

        public ReflectException() {
        }

        public ReflectException(String message) {
            super(message);
        }

        public ReflectException(String message, Throwable cause) {
            super(message, cause);
        }

        public ReflectException(Throwable cause) {
            super(cause);
        }
    }

}
