package com.prolog.upcloud.base.wh.operate.utils;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.prolog.framework.core.pojo.Page;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;

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

public class BeanConvertUtils extends BeanUtils {
	public static <S, T> T convertTo(S source, Supplier<T> targetSupplier) {
		return convertTo(source, targetSupplier, null);
	}

	/**
	 * 转换对象
	 *
	 * @param page           源对象
	 * @param targetSupplier 目标对象供应方
	 * @param callBack       回调方法
	 * @param <S>            源对象类型
	 * @param <T>            目标对象类型
	 * @return 目标对象
	 */
	public static <S, T> Page<T> convertPageTo(Page<S> page, Supplier<T> targetSupplier,
			ConvertCallBack<S, T> callBack) {
		if (null == page || null == targetSupplier) {
			return null;
		}

		Page<T> targetPage = convertTo(page, Page<T>::new);

		List<T> targetList = convertListTo(page.getList(), targetSupplier, callBack);
		targetPage.setList(targetList);

		return targetPage;
	}

	/**
	 * 转换对象
	 *
	 * @param source         源对象
	 * @param targetSupplier 目标对象供应方
	 * @param callBack       回调方法
	 * @param <S>            源对象类型
	 * @param <T>            目标对象类型
	 * @return 目标对象
	 */
	public static <S, T> T convertTo(S source, Supplier<T> targetSupplier, ConvertCallBack<S, T> callBack) {
		if (null == source || null == targetSupplier) {
			return null;
		}

		T target = targetSupplier.get();
		copyProperties(source, target);
		if (callBack != null) {
			callBack.callBack(source, target);
		}
		return target;
	}

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

	/**
	 * 转换对象
	 *
	 * @param sources        源对象list
	 * @param targetSupplier 目标对象供应方
	 * @param callBack       回调方法
	 * @param <S>            源对象类型
	 * @param <T>            目标对象类型
	 * @return 目标对象list
	 */
	public static <S, T> List<T> convertListTo(List<S> sources, Supplier<T> targetSupplier,
			ConvertCallBack<S, T> callBack) {
		if (null == sources || null == targetSupplier) {
			return null;
		}

		List<T> list = new ArrayList<>(sources.size());
		for (S source : sources) {
			T target = targetSupplier.get();
			copyProperties(source, target);
			if (callBack != null) {
				callBack.callBack(source, target);
			}
			list.add(target);
		}
		return list;
	}

	/**
	 * 回调接口
	 *
	 * @param <S> 源对象类型
	 * @param <T> 目标对象类型
	 */
	@FunctionalInterface
	public interface ConvertCallBack<S, T> {
		void callBack(S t, T s);
	}

	/**
	 * 获取一个类和其父类的所有属性
	 * 
	 * @param clazz
	 * @return
	 */
	public static List<Field> findAllFieldsOfSelfAndSuperClass(Class clazz) {
		Field[] fields = null;
		List fieldList = new ArrayList();
		while (true) {
			if (clazz == null) {
				break;
			} else {
				fields = clazz.getDeclaredFields();
				for (int i = 0; i < fields.length; i++) {
					fieldList.add(fields[i]);
				}
				clazz = clazz.getSuperclass();
			}
		}
		return fieldList;
	}

	/**
	 * 把一个Bean对象转换成Map对象</br>
	 *
	 * @param ignores
	 * @return
	 * @throws IllegalAccessException
	 */
	public static Map<String, Object> convertBean2Map(Object obj, String[] ignores) {
		Map<String, Object> map = new HashMap<>();
		Class<?> clazz = obj.getClass();
		List<Field> fieldList = findAllFieldsOfSelfAndSuperClass(clazz);
		Field field;
		try {
			for (Field item : fieldList) {
				field = item;
				// 定义fieldName是否在拷贝忽略的范畴内
				boolean flag = false;
				if (ignores != null && ignores.length != 0) {
					flag = isExistOfIgnores(field.getName(), ignores);
				}
				if (!flag) {
					Object value = getProperty(obj, field.getName());
					if (null != value && !StringUtils.EMPTY.equals(value.toString())) {
						map.put(field.getName(), getProperty(obj, field.getName()));
					}
				}
			}
		} catch (SecurityException | IllegalArgumentException e) {
			e.printStackTrace();
		}
		return map;
	}

	/**
	 * 判断fieldName是否是ignores中排除的
	 * 
	 * @param fieldName
	 * @param ignores
	 * @return
	 */
	private static boolean isExistOfIgnores(String fieldName, String[] ignores) {
		boolean flag = false;
		for (String str : ignores) {
			if (str.equals(fieldName)) {
				flag = true;
				break;
			}
		}
		return flag;
	}

	public static PropertyDescriptor getPropertyDescriptor(Class clazz, String propertyName) {
		StringBuffer sb = new StringBuffer();// 构建一个可变字符串用来构建方法名称
		Method setMethod = null;
		Method getMethod = null;
		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方法
				// 构建get 方法
				getMethod = clazz.getDeclaredMethod(sb.toString(), new Class[] {});
				// 构建一个属性描述器 把对应属性 propertyName 的 get 和 set 方法保存到属性描述器中
				pd = new PropertyDescriptor(propertyName, getMethod, setMethod);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return pd;
	}

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

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Object getProperty(Object obj, String propertyName) {
		Class clazz = obj.getClass();// 获取对象的类型
		PropertyDescriptor pd = getPropertyDescriptor(clazz, propertyName);// 获取 clazz
																			// 类型中的
																			// propertyName
																			// 的属性描述器
		Method getMethod = pd.getReadMethod();// 从属性描述器中获取 get 方法
		Object value = null;
		try {
			value = getMethod.invoke(obj, new Object[] {});// 调用方法获取方法的返回值
		} catch (Exception e) {
			e.printStackTrace();
		}
		return value;// 返回值
	}

	/**
	 * 把一个Bean对象转换成Map对象</br>
	 * 
	 * @param clazz
	 * @return
	 */
	public static Map convertBean2Map(Object obj) {
		return convertBean2Map(obj, null);
	}

	public static Map<String, Object> convertBean2MapForIngoreserialVersionUID(Object obj) {
		return convertBean2Map(obj, new String[] { "serialVersionUID" });
	}
}
