package cn.axzo.msg.center.common.utils;


import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.util.*;
import java.util.function.Supplier;

/**
 * @author: wangli
 * @date: 2021/11/18 14:30
 */
public class CustomBeanUtils extends BeanUtils {

  public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
    return copyListProperties(sources, target, null);
  }

  /**
   * @author Johnson 使用场景：Entity、Bo、Vo层数据的复制，因为BeanUtils.copyProperties只能给目标对象的属性赋值，却不能在List集合下循环赋值，因此添加该方法
   * 如：List<AdminEntity> 赋值到 List<AdminVo> ，List<AdminVo>中的 AdminVo 属性都会被赋予到值 S: 数据源类 ，T:
   * 目标类::new(eg: AdminVo::new)
   */
  public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target,
      CustomBeanUtilsCallBack<S, T> callBack, String... ignorePropertyNames) {
    List<T> list = new ArrayList<>(sources.size());
    for (S source : sources) {
      T t = target.get();
      copyProperties(source, t, ignorePropertyNames);
      list.add(t);
      if (callBack != null) {
        // 回调
        callBack.callBack(source, t);
      }
    }
    return list;
  }

  /**
   * Bean拷贝工具,自动过滤属性为null的.
   *
   * @param source
   * @param target
   * @param ignoreProperties
   */
  public static void copyPropertiesIgnoreNull(Object source, Object target,
      String... ignoreProperties) {
    String[] nullPropertyNames = getNullPropertyNames(source, ignoreProperties);
    copyProperties(source, target, nullPropertyNames);
  }

  /**
   * Bean拷贝工具,自动过滤属性为null的和指定的属性名
   *
   * @param source
   * @param target
   * @param ignoreValue
   */
  public static void copyPropertiesIgnoreNullAndSpecify(Object source, Object target,
      Object... ignoreValue) {
    String[] strings = getNullAndSpecifyPropertyValue(source, ignoreValue);
    copyProperties(source, target, strings);
  }

  /**
   * 获取传入对象的null属性的 属性名称数组; 可用于BeanUtils.copyProperties的第三个参数，防止copy为null的属性
   *
   * @param source
   * @return
   */
  public static String[] getNullPropertyNames(Object source) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

    Set<String> emptyNames = new HashSet<String>();
    for (java.beans.PropertyDescriptor pd : pds) {
      Object srcValue = src.getPropertyValue(pd.getName());
      if (srcValue == null) {
        emptyNames.add(pd.getName());
      }
    }
    String[] result = new String[emptyNames.size()];
    return emptyNames.toArray(result);
  }

  /**
   * 获取传入对象的null属性的 属性名称数组; 可用于BeanUtils.copyProperties的第三个参数，防止copy为null的属性
   *
   * @param source
   * @param ignoreProperties 自定义过滤拷贝的属性名
   * @return
   */
  public static String[] getNullPropertyNames(Object source, String... ignoreProperties) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
    Set<String> emptyNames = new HashSet<String>();
    for (java.beans.PropertyDescriptor pd : pds) {
      Object srcValue = src.getPropertyValue(pd.getName());
      if (srcValue == null) {
        emptyNames.add(pd.getName());
      }
    }
    for (String propertyName : ignoreProperties) {
      emptyNames.add(propertyName);
    }
    String[] result = new String[emptyNames.size() + ignoreProperties.length];
    return emptyNames.toArray(result);
  }

  public static String[] getNullAndSpecifyPropertyValue(Object source, Object... ignoreValues) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
    Set<String> emptyNames = new HashSet<String>();
    for (java.beans.PropertyDescriptor pd : pds) {
      Object srcValue = src.getPropertyValue(pd.getName());
      if (srcValue == null) {
        emptyNames.add(pd.getName());
      } else {
        for (Object value : ignoreValues) {
          if (Objects.equals(srcValue, value)) {
            emptyNames.add(pd.getName());
          }
        }
      }
    }
    String[] result = new String[emptyNames.size()];
    return emptyNames.toArray(result);
  }

  public static <T> T copyBean(Object sourceObj, Class<T> targetCls) {
    return copyBean(sourceObj, targetCls, null);
  }

  /**
   * 复制属性
   */
  public static <S, T> T copyBean(S sourceObj, Class<T> targetCls, CustomBeanUtilsCallBack<S,
      T> callBack) {
    T targetObj = null;
    if (sourceObj != null) {
      try {
        targetObj = BeanUtils.instantiate(targetCls);
        BeanUtils.copyProperties(sourceObj, targetObj);
        if (callBack != null) {
          // 回调
          callBack.callBack(sourceObj, targetObj);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    return targetObj;
  }

}

