package com.bcxin.auth.common.utils.bean;

import com.bcxin.auth.common.exception.BusinessException;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Bean 工具类
 * 
 * @author bcxin
 */
public class BeanUtils extends org.springframework.beans.BeanUtils
{
    /** Bean方法名中属性名开始的下标 */
    private static final int BEAN_METHOD_PROP_INDEX = 3;

    /** * 匹配getter方法的正则表达式 */
    private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");

    /** * 匹配setter方法的正则表达式 */
    private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");

    /**
     * Bean属性复制工具方法。
     * 
     * @param dest 目标对象
     * @param src 源对象
     */
    public static void copyBeanProp(Object dest, Object src)
    {
        try
        {
            copyProperties(src, dest);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 获取对象的setter方法。
     * 
     * @param obj 对象
     * @return 对象的setter方法列表
     */
    public static List<Method> getSetterMethods(Object obj)
    {
        // setter方法列表
        List<Method> setterMethods = new ArrayList<Method>();

        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();

        // 查找setter方法

        for (Method method : methods)
        {
            Matcher m = SET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 1))
            {
                setterMethods.add(method);
            }
        }
        // 返回setter方法列表
        return setterMethods;
    }

    /**
     * 获取对象的getter方法。
     * 
     * @param obj 对象
     * @return 对象的getter方法列表
     */

    public static List<Method> getGetterMethods(Object obj)
    {
        // getter方法列表
        List<Method> getterMethods = new ArrayList<Method>();
        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();
        // 查找getter方法
        for (Method method : methods)
        {
            Matcher m = GET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 0))
            {
                getterMethods.add(method);
            }
        }
        // 返回getter方法列表
        return getterMethods;
    }

    /**
     * 检查Bean方法名中的属性名是否相等。<br>
     * 如getName()和setName()属性名一样，getName()和setAge()属性名不一样。
     * 
     * @param m1 方法名1
     * @param m2 方法名2
     * @return 属性名一样返回true，否则返回false
     */

    public static boolean isMethodPropEquals(String m1, String m2)
    {
        return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
    }
    /**
     * @param src        要被复制的对象 A
     * @param target     目标对象 B
     * @param ignoreNull 是否需要排除空值
     *                   A复制给B
     *                   A的值赋值给B
     *                  src的值赋值给target
     *                   true：找到要 复制的对象src 的所有空值的属性，不复制到 目标对象target
     *                   false：找到目标对象target的非空属性，然后这些对象不需被复制
     *                   ex：被复制的对象：src：name="aaa" id="213213" version=""
     *                   目标对象：target: name="bbb" version="1.0"--->
     *                   ignoreNull=true   result: target:name="aaa" id="213213" version="1.0" （src为空不复制）
     *                   ignoreNull=false   result: target:name="bbb" id="213213" version="1.0" (target有值不替换)
     * @return void
     * @Decription:拷贝的时候排除
     * @author：zhongjianhui
     * @method copyPropertiesIgnoreNull
     * @date：2018/6/8 17:05
     * @params：
     */
    public static void copyPropertiesIgnore(Object src, Object target, Boolean ignoreNull) {
        Object ignoreObject = src;
        if (!ignoreNull) {
            ignoreObject = target;
        }
        org.springframework.beans.BeanUtils.copyProperties(src, target, getPropertyNames(ignoreObject, src, ignoreNull, new String[0]));
    }

    /**
     * @param source   传入的类
     * @param needNull 是否需要排除空值，true：找到空值的对象 flase：返回不是空值的对象
     * @return java.lang.String[]
     * @Decription:两种用法： 一：找到要复制的类的空值，不复制到目标类
     * 二：找到目标类的非空对象，然后这些对象不需要复制
     * @author：zhongjianhui
     * @method getPropertyNames
     * @date：2018/6/8 17:11
     * @params：
     */
    public static String[] getPropertyNames(Object source, Object validateExist, Boolean needNull, String[] needCopyArr) {
        final BeanWrapper src = new BeanWrapperImpl(source);

        final BeanWrapper validate = new BeanWrapperImpl(validateExist);
        //找到类的所有属性
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
        java.beans.PropertyDescriptor[] pdValidate = validate.getPropertyDescriptors();

        //空值的属性名称
        Set<String> emptyNames = new HashSet<String>();
        //有值的属性名称
        Set<String> notEmptyNames = new HashSet<String>();
        //需要忽略的属性名称
        Set<String> ignorePropertiesNames = new HashSet<String>();
        //需要拷贝的属性的长度
        int length = needCopyArr.length;
        //未找到值

        if (length > 0) {
            int pdLength = length;
            int validateLength = length;

            for (String needCopyStr : needCopyArr) {

                //判断被复制的对象有没有存在这个属性
                for (java.beans.PropertyDescriptor pd : pdValidate) {
                    if (needCopyStr.equals(pd.getName())) {
                        validateLength--;
                        break;
                    }
                }
                //目标无需被复制时校验，无需被复制的属性是否存在
                if (!needNull) {
                    for (java.beans.PropertyDescriptor pd : pds) {
                        if (needCopyStr.equals(pd.getName())) {
                            pdLength--;
                            break;
                        }
                    }
                }
            }
            if (!needNull) {
                if (pdLength > 0) {
                    throw new BusinessException("目标的对象有不存在的属性，请校验名称是否写错！");
                }
            }
            if (validateLength > 0) {
                throw new BusinessException("复制的对象有不存在的属性，请校验名称是否写错！");
            }


        }
        //如果不存在则报错

        //获取所有属性名称
        for (java.beans.PropertyDescriptor pd : pds) {


            //如果需要拷贝的值为空，则
            if (length <= 0) {
                //对象的值
                Object srcValue = src.getPropertyValue(pd.getName());
                if (srcValue == null || "".equals(srcValue)) {
                    //加入需要忽略复制空值的属性名字集合：src
                    emptyNames.add(pd.getName());
                } else {
                    //加入需要忽略目标有值的属性名字集合：desc
                    notEmptyNames.add(pd.getName());
                }
            } else {

                boolean flag = false;
                //遍历所有需要拷贝的属性数组
                for (String needCopyStr : needCopyArr) {
                    //如果属性名不等于拷贝的属性名则添加进入忽略的数组
                    if (needCopyStr.equals(pd.getName())) {
                        flag = true;
                        break;
                    }
                }
                if (!flag) {
                    ignorePropertiesNames.add(pd.getName());
                }

            }
        }
        //定义返回的小
        String[] resultEmpty = new String[emptyNames.size()];
        String[] resultNotEmpty = new String[notEmptyNames.size()];
        String[] resultIgnoreProperties = new String[ignorePropertiesNames.size()];
        if (length <= 0) {
            if (needNull) {
                return emptyNames.toArray(resultEmpty);
            } else {
                return notEmptyNames.toArray(resultNotEmpty);
            }
        } else {
            //不需要复制到目标的属性
            if (needNull){
                return needCopyArr;
            }else {
                return ignorePropertiesNames.toArray(resultIgnoreProperties);
            }
        }
    }
}
