/**
 * All rights Reserved, Designed By www.freemud.cn
 *
 * @Title: BeanUtil
 * @Package cn.freemud.utils
 * @Description:
 * @author: liming.guo
 * @date: 2018/5/16 14:40
 * @version V1.0
 * @Copyright: 2018 www.freemud.cn Inc. All rights reserved.
 * 注意：本内容仅限于上海非码科技内部传阅，禁止外泄以及用于其他的商业目
 */
package cn.freemud.utils;

import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/**
 * 对象转换工具，扩展BeanUtils，实现对象List批量转换
 */
public class BeanUtil {

    /**
     * 转换单个对象
     *
     * @param <S>
     * @param <T>
     * @param source
     * @param target
     */
    public static <S, T> void convertBean(S source, T target) {
        BeanUtils.copyProperties(source, target);
    }

    /**
     * 转换单个对象
     *
     * @param <S>
     * @param <T>
     * @param source
     * @param supplier
     */
    public static <S, T> T convertBean(S source, Supplier<T> supplier) {
        T target = supplier.get();
        BeanUtils.copyProperties(source, target);
        return target;
    }

    /**
     * 转换对象列表，要求T类型拥有无参构造方法
     *
     * @param <S>
     * @param <T>
     * @param source
     * @param targetClass
     * @return
     * @deprecated replaced by <code>convertBeans(List, Supplier)</code>
     */
    @Deprecated
    public static <S, T> List<T> convertList(List<S> source, final Class<T> targetClass) {
        return convertBeans(source, () -> {
            try {
                return targetClass.newInstance();
            } catch (Exception e) {
                throw new RuntimeException("Error copy properties.", e);
            }
        });
    }

    /**
     * 转换对象列表
     *
     * @param <S>
     * @param <T>
     * @param source
     * @param objectCreator
     * @return
     * @deprecated replaced by <code>convertBeans(List, Supplier)</code>
     */
    @Deprecated
    public static <S, T> List<T> convertList(List<S> source, ObjectCreator<T> objectCreator) {
        return convertBeans(source, objectCreator::createObj);
    }

    /**
     * 转换对象列表
     *
     * @param <S>
     * @param <T>
     * @param source   源类型列表
     * @param supplier 目标类型对象生成器
     * @return
     */
    public static <S, T> List<T> convertBeans(List<S> source, Supplier<T> supplier) {
        List<T> target = new ArrayList<T>();
        if (CollectionUtils.isEmpty(source)) {
            return target;
        }
        for (S sourceEntity : source) {
            T targetEntity = supplier.get();
            convertBean(sourceEntity, targetEntity);
            target.add(targetEntity);
        }
        return target;
    }

    @FunctionalInterface
    @Deprecated
    public interface ObjectCreator<T> {
        T createObj();
    }


    public static PropertyDescriptor getPropertyDescriptor(Class clazz, String propertyName) {
        StringBuffer sb = new StringBuffer();
        Method setMethod;
        Method getMethod;
        PropertyDescriptor pd = null;

        try {
            //根据字段名来获取字段
            Field f = clazz.getDeclaredField(propertyName);
            if (f != null) {
                //构建方法的后缀
                String methodEnd = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                sb.append("set" + methodEnd);
                //构建set方法
                setMethod = clazz.getDeclaredMethod(sb.toString(), new Class[]{f.getType()});
                sb.delete(0, sb.length());
                sb.append("get" + methodEnd);
                //构建get 方法
                getMethod = clazz.getDeclaredMethod(sb.toString(), new Class[]{});
                //构建一个属性描述器 把对应属性 propertyName 的 get 和 set 方法保存到属性描述器中
                pd = new PropertyDescriptor(propertyName, getMethod, setMethod);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return pd;
    }

    public static void setProperty(Object obj, String propertyName, Object value) {
        Class clazz = obj.getClass();
        //获取 clazz 类型中的 propertyName 的属性描述器
        PropertyDescriptor pd = getPropertyDescriptor(clazz, propertyName);
        //从属性描述器中获取 set 方法
        Method setMethod = pd.getWriteMethod();
        try {
            //调用 set 方法将传入的value值保存属性中去
            setMethod.invoke(obj, new Object[]{value});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Object getProperty(Object obj, String propertyName) {
        Class clazz = obj.getClass();
        PropertyDescriptor pd = getPropertyDescriptor(clazz, propertyName);
        Method getMethod = pd.getReadMethod();
        Object value = null;
        try {
            //调用方法获取方法的返回值
            value = getMethod.invoke(clazz, new Object[]{});
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }

}