package com.steerinfo.util;

import com.steerinfo.pretrack.pretrackscale.model.PreTrackScale;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import static org.springframework.beans.BeanUtils.getPropertyDescriptor;
import static org.springframework.beans.BeanUtils.getPropertyDescriptors;

/**
 *
 * @author yxd
 * 2021-11-20
 */
public class CompareUtil {

    public static void main(String[] args) {
        PreTrackScale scaleSource = new PreTrackScale();
        scaleSource.setPredictionNo("主键不变");
        scaleSource.setAddWeightSum(BigDecimal.valueOf(99.88));
        scaleSource.setBatchNo("测试更新前数据非空，更新后没值");
        scaleSource.setPredictionType("测试更新前数据非空Type");
        scaleSource.setCreateTime(new Date());

        PreTrackScale scaleTarget = new PreTrackScale();
        scaleTarget.setPredictionNo("主键不变");
        scaleTarget.setAddWeightSum(BigDecimal.valueOf(23.34));
        scaleTarget.setPredictionType("测试更新后数据非空，有变化");
        scaleTarget.setPredictionCombination("测试更新后数据为空Combination");
        scaleTarget.setCreateTime(new Date());

        String[] strCompare = compareProperties(scaleSource, scaleTarget);
        if (strCompare != null && strCompare.length == 2) {
            //system.out.println(strCompare[0]);
            //system.out.println(strCompare[1]);
        }
    }

    /**
     * 比较两个实体，并输出不一样的字段
     *
     * @param source 源数据（更新前的数据）
     * @param target 目标数据（更新后的数据）
     * @return String[0] strBefore="修改前数据：";
     * String[1] strAfter="修改后数据：";
     * @throws BeansException
     */
    public static String[] compareProperties(Object source, Object target) throws BeansException {
        String[] str;
        str = compareProperties(source, target, null, (String[]) null);
        return str;
    }

    private static String[] compareProperties(Object source, Object target, @Nullable Class<?> editable,
                                              @Nullable String... ignoreProperties) throws BeansException {
        //string数组，第一个是修改前的数据，第二个是修改后的数据
        String[] str = new String[]{"",""};
        String strBefore = "修改前数据：";
        String strAfter = "修改后数据：";
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        Class<?> actualEditable = target.getClass();
        if (editable != null) {
            if (!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
                        "] not assignable to Editable class [" + editable.getName() + "]");
            }
            actualEditable = editable;
        }
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

        for (PropertyDescriptor targetPd : targetPds) {
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null &&
                            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
                            Object value = readMethod.invoke(source);
                            Object value1 = readMethod.invoke(source);
                            Object value2 = readMethod.invoke(target);
                            if (value1 != null && value2 != null
                                    && !value1.toString().equals(value2.toString())) {
                                strBefore = strBefore + targetPd.getName() + "值：" + value1.toString() + "；";
                                strAfter = strAfter + targetPd.getName() + "值：" + value2.toString() + "；";
                            }
                            if (value1 == null && value2 != null) {
                                strBefore = strBefore + targetPd.getName() + "值：无；";
                                strAfter = strAfter + targetPd.getName() + "值：" + value2.toString() + "；";
                            }
                            if (value1 != null && value2 == null) {
                                strBefore = strBefore + targetPd.getName() + "值：" + value1.toString() + "；";
                                strAfter = strAfter + targetPd.getName() + "值：无；";
                            }
                            /**
                             *一下为BeanUtils源码，取消赋值
                             if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                             writeMethod.setAccessible(true);
                             }
                             writeMethod.invoke(target, value);
                             */
                        } catch (Throwable ex) {
                            throw new FatalBeanException(
                                    "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                        }
                    }
                }
            }
        }
        str[0] = strBefore;
        str[1] = strAfter;
        return str;
    }
}
