package com.yanzuoguang.util.base;


import com.yanzuoguang.util.exception.ExceptionHelper;
import com.yanzuoguang.util.helper.StringHelper;

import java.lang.reflect.*;
import java.util.*;

/**
 * 对象操作类,包含字段反射
 *
 * @author 颜佐光
 */
public class ObjectHelper {
    public static final String METHOD_IS = "is";
    public static final String METHOD_GET = "get";
    public static final String METHOD_SET = "set";
    /**
     * 缓存的类型
     */
    private static Map<Class<?>, HashMap<String, MethodField>> mapCache = new HashMap<>();

    // --------------------------------------------- 类型判断 ---------------------------------------------------------

    /**
     * 判断类型间是否可以自动转换
     *
     * @param to   需要转换后的类型
     * @param from 转换前的类型
     * @return
     */
    public static boolean isAutoConvert(Class to, Class from) {
        return from != to && (Number.class.isAssignableFrom(to) || to.isEnum() || to == String.class);
    }

    /**
     * 判断from参数是否是to的子类
     *
     * @param to   父类
     * @param from 子类
     * @return 是否满足条件
     */
    public static boolean isSub(Class to, Class from) {
        return from == to || from.isAssignableFrom(to);
    }

    /**
     * 判断值是否能够写入当前类型的对象
     *
     * @param cls   类型
     * @param value 值
     * @return 是否能够写入
     */
    public static boolean isWriteType(Class<?> cls, Object value) {
        if (cls.isEnum() || cls.isAssignableFrom(Number.class)) {
            return true;
        }
        if (cls == String.class || cls == int.class || cls == boolean.class || cls == double.class || cls == float.class) {
            return true;
        }
        boolean isEmpty = StringHelper.isEmpty(value);
        if (isEmpty) {
            return true;
        }
        return cls.isAssignableFrom(value.getClass());
    }

    // --------------------------------------------- 类型判断 ---------------------------------------------------------

    /**
     * 获取指定对象中某个字段的整形值
     *
     * @param target 需要处理的对象
     * @param field  需要获取的字段的值
     * @return 返回值
     */
    public static int getInt(Object target, String field) {
        return StringHelper.toInt(get(target, field));
    }

    /**
     * 获取指定对象中某个字段字符串值
     *
     * @param target 需要处理的对象
     * @param field  需要获取的字段的值
     * @return 返回值
     */
    public static String getString(Object target, String field) {
        return get(String.class, target, field);
    }

    /**
     * 获取指定对象中某个字段的double值
     *
     * @param target 需要处理的对象
     * @param field  需要获取的字段的值
     * @return 返回值
     */
    public static double getDecimal(Object target, String field) {
        return StringHelper.toDouble(get(target, field));
    }

    /**
     * 类型转换(从obj中取出关键词为vName的值 , 转换为cls类型)
     *
     * @param cls    转换后的类型
     * @param target 要转换对象
     * @param field  要转换对象的关键词
     * @return 返回值
     */
    public static <T> T get(Class<T> cls, Object target, String field) {
        return get(cls, target, field, null);
    }

    /**
     * 类型转换(从obj中取出关键词为vName的值 , 转换为cls类型)
     *
     * @param cls    转换后的类型
     * @param target 要转换对象
     * @param field  要转换对象的关键词
     * @return 返回值
     */
    public static <T> T get(Class<T> cls, Object target, String field, T def) {
        Object val = get(target, field);
        T to = StringHelper.to(cls, val);
        to = to == null ? def : to;
        return to;
    }

    /**
     * 获取对象中的某个字段值,根据不同对象输出结果
     *
     * @param target 需要获取的对象
     * @param field  需要获取的字段
     * @return 获取之后的值
     */
    public static Object get(Object target, String field) {
        Object ret = null;
        if (target == null) {
            return ret;
        }

        Class vType = target.getClass();
        if (target instanceof Number || vType.isEnum()) {
            ret = null;
        } else if (target instanceof Map) {
            ret = ((Map) target).get(field);
        } else {
            ret = getByType(target.getClass(), target, field);
        }
        return ret;
    }

    /**
     * 获取对象的值(从obj中获取关键词为vName的值)
     *
     * @param targetCls 要获取的对象的Class类型
     * @param target    要获取的对象
     * @param field     要获取的对象的关键词
     * @return 获取后的值
     */
    public static Object getByType(Class<?> targetCls, Object target, String field) {
        MethodField item = getMethodField(targetCls, field);
        if (item == null) {
            return null;
        }
        try {
            if (item.getField() != null && Modifier.isPublic(item.getField().getModifiers())) {
                return item.getField().get(target);
            } else if (item.getGetMethod() != null && Modifier.isPublic(item.getGetMethod().getModifiers())) {
                return item.getGetMethod().invoke(target);
            }
        } catch (Exception ex) {
            ExceptionHelper.handleException(ObjectHelper.class, ex, field);
        }
        return null;
    }

    /**
     * 设置对象中某个字段的值,根据不同类型设置不同值
     *
     * @param target 需要设置的对象
     * @param field  需要设置的字段
     * @param value  需要设置的值
     */
    public static void set(Object target, String field, Object value) {
        if (target == null) {
            return;
        }
        Class vType = target.getClass();
        if (target instanceof Number || vType.isEnum()) {
            return;
        } else if (target instanceof Map) {
            ((Map) target).put(field, value);
        } else {
            try {
                setByType(vType, target, field, value);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    /**
     * 设置对象的值
     *
     * @param targetCls 要赋值的类型
     * @param target    要赋值的对象
     * @param field     要赋值的方法
     * @param value     值
     */
    public static void setByType(Class<?> targetCls, Object target, String field, Object value) {
        MethodField item = getMethodField(targetCls, field);
        if (item == null) {
            return;
        }
        try {
            setByType(target, item, value);
        } catch (Exception ex) {
            ExceptionHelper.handleException(ObjectHelper.class, ex, item.getName());
        }
    }

    public static void setByType(Object target, MethodField item, Object value) throws IllegalAccessException, InvocationTargetException {
        if (item.getField() != null && Modifier.isPublic(item.getField().getModifiers())) {
            Class toType = item.getField().getType();
            Object toValue = StringHelper.to(toType, value);
            item.getField().set(target, toValue);
        } else if (item.getSetMethod() != null && Modifier.isPublic(item.getSetMethod().getModifiers())) {
            Class toType = item.getSetMethod().getParameterTypes()[0];
            Object toValue = StringHelper.to(toType, value);
            item.getSetMethod().invoke(target, toValue);
        }
    }

    // --------------------------------------------- 对象转换、复制、字段复制 ---------------------------------------------------------

    /**
     * 将对象复制成一个新的对象
     *
     * @param from 需要复制的对象
     * @param <T>  复制的对象的类型
     * @return 新的对象
     */
    public static <T extends Object> T clone(T from) {
        if (from == null) {
            return from;
        }
        Class cls = from.getClass();
        try {
            Object to = cls.newInstance();
            writeWithFromClass(to, from);
            return (T) to;
        } catch (Exception e) {
            throw new RuntimeException("对象" + cls.getName() + "不能复制", e);
        }
    }

    /***
     * 获取缓存中的字段
     * @param typeCache 类型
     * @param fromName 来源名称
     * @param toName 目标名称
     * @return
     */
    private static MethodField getField(HashMap<String, MethodField> typeCache, String fromName, String toName) {
        if (!typeCache.containsKey(toName)) {
            MethodField newObj = new MethodField();
            typeCache.put(toName, newObj);
            newObj.setName(fromName);
            newObj.setNameSimple(toName);
        }
        return typeCache.get(toName);
    }

    /**
     * 获取简单名称
     *
     * @param fromName
     * @return
     */
    private static String getSimpleName(String fromName) {
        String toName = getSimpleFieldName(fromName);
        if (toName.startsWith(METHOD_IS)) {
            toName = toName.substring(METHOD_IS.length());
        } else if (toName.startsWith(METHOD_GET)) {
            toName = toName.substring(METHOD_GET.length());
        } else if (toName.startsWith(METHOD_SET)) {
            toName = toName.substring(METHOD_SET.length());
        } else {
            return toName;
        }
        return toName;
    }

    /**
     * 获取字段处理名称
     *
     * @param fromName 字段名称
     * @return 属性名称
     */
    private static String getSimpleFieldName(String fromName) {
        String toName = fromName.toLowerCase().replace("_", "");
        return toName;
    }

    /**
     * 获取实体的字段
     *
     * @param cls 需要获取的类型
     * @return 获取字段之间的对应关系
     */
    private static HashMap<String, MethodField> getInitTypeField(Class<?> cls) {
        HashMap<String, MethodField> typeCache = new LinkedHashMap<String, MethodField>();

        List<Field> fields = new ArrayList<Field>();
        List<Method> methods = new ArrayList<Method>();

        Class<?> tempClass = cls;
        //当父类为null的时候说明到达了最上层的父类(Object类).
        while (tempClass != null) {
            fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            methods.addAll(Arrays.asList(tempClass.getDeclaredMethods()));
            //得到父类,然后赋给自己
            tempClass = tempClass.getSuperclass();
        }

        // 忽略大小写、忽略下划线  _
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            String toName = getSimpleFieldName(field.getName());
            MethodField obj = getField(typeCache, field.getName(), toName);
            if (obj.getField() == null) {
                obj.setField(field);
            }
        }
        for (Method method : methods) {
            if (Modifier.isStatic(method.getModifiers())) {
                continue;
            }
            String methodNameSource = method.getName();
            String methodNameSimple = methodNameSource.toLowerCase();
            String toName = getSimpleName(methodNameSource);
            if ("getClass".equals(methodNameSource)) {
                continue;
            } else if (methodNameSimple.startsWith("set")) {
                if (method.getParameterTypes().length != 1) {
                    continue;
                }
                MethodField obj = getField(typeCache, methodNameSource, toName);
                if (obj.getSetMethod() == null) {
                    obj.setSetMethod(method);
                }
            } else if (methodNameSimple.startsWith("get")) {
                if (method.getReturnType() == null) {
                    continue;
                }
                MethodField obj = getField(typeCache, methodNameSource, toName);
                if (obj.getGetMethod() == null) {
                    obj.setGetMethod(method);
                }
            } else if (methodNameSimple.startsWith("is")) {
                if (method.getReturnType() == null) {
                    continue;
                }
                MethodField obj = getField(typeCache, methodNameSource, toName);
                if (obj.getGetMethod() == null) {
                    obj.setGetMethod(method);
                }
            }
        }
        return typeCache;
    }

    /**
     * 获取一个表中的字段
     *
     * @param type
     * @return
     */
    public static HashMap<String, MethodField> getTypeField(Class<?> type) {
        HashMap<String, MethodField> typeCache;
        if (mapCache.containsKey(type)) {
            typeCache = mapCache.get(type);
        } else {
            typeCache = getInitTypeField(type);
            mapCache.put(type, typeCache);
        }
        return typeCache;
    }

    /**
     * 获取字段的名称
     *
     * @param type
     * @param name
     * @return
     */
    private static MethodField getMethodField(Class<?> type, String name) {
        HashMap<String, MethodField> typeCache = getTypeField(type);
        name = getSimpleFieldName(name);
        return typeCache.containsKey(name) ? typeCache.get(name) : null;
    }


    /**
     * 将集合的类容转换为对象
     *
     * @param vToClass 转换后的对象
     * @param vTo      转换后的对象的集合
     * @param vFrom    要转换的对象
     * @param vField   关键词
     * @throws Exception 需要跑出的异常
     */
    public static <T> void addList(Class<T> vToClass, ArrayList<T> vTo, Object vFrom, String vField) throws InstantiationException, IllegalAccessException {
        List vFroms = ObjectHelper.get(List.class, vFrom, vField);
        if (vFroms == null || vTo == null) {
            return;
        }

        for (Object vFromItem : vFroms) {
            T vToItem = ObjectHelper.convert(vToClass, vFromItem);
            vTo.add(vToItem);
        }
    }

    /**
     * 将来源类型的对象,转换为目标类型的对象
     *
     * @param toCls 需要转换后的目标类型
     * @param from  来源类型
     * @param <T>   目标类型
     * @return 转换后的值
     * @throws IllegalAccessException 异常信息
     * @throws InstantiationException 异常信息
     */
    public static <T> T convert(Class<T> toCls, Object from) throws IllegalAccessException, InstantiationException {
        if (from == null) {
            return null;
        }
        T to = StringHelper.to(toCls, from);
        if (to != null) {
            return to;
        }
        to = toCls.newInstance();
        writeWithToClass(to, from);
        return to;
    }

    /**
     * 对象转换(一般将map转为javabean)
     *
     * @param to   转换后的对象
     * @param from 要转换的对象
     * @return 转换后的值 to
     */
    public static Object writeWithToClass(Object to, Object from) {
        return writeWithToClass(false, to, from);
    }


    /**
     * 对象转换(一般将map转为javabean)
     *
     * @param emptyWrite 为空时是否写入
     * @param to         转换后的对象
     * @param from       要转换的对象
     * @return 转换后的值 to
     */
    public static Object writeWithToClass(boolean emptyWrite, Object to, Object from) {
        if (from == null || to == null) {
            return to;
        }
        HashMap<String, MethodField> mapField = getInitTypeField(to.getClass());
        return writeWithClass(emptyWrite, to, from, mapField);
    }


    /**
     * 根据来源数据,往目标中写入数据
     *
     * @param tos   目标对象
     * @param froms 来源对象
     */
    public static void writeArrayWithFrom(List tos, Object... froms) {
        for (Object to : tos) {
            writeWithFrom(to, froms);
        }
    }

    /**
     * 根据来源数据,往目标中写入数据
     *
     * @param tos   目标对象
     * @param froms 来源对象
     */
    public static void writeArrayWithFrom(Object[] tos, Object... froms) {
        for (Object to : tos) {
            writeWithFrom(to, froms);
        }
    }

    /**
     * 根据来源数据,往目标中写入数据
     *
     * @param to    目标对象
     * @param froms 来源对象
     */
    public static void writeWithFrom(Object to, Object... froms) {
        for (Object from : froms) {
            if (from instanceof Map) {
                writeWithFromMap(to, (Map) from);
            } else {
                ObjectHelper.writeWithFromClass(to, from);
            }
        }
    }

    /**
     * 将值吸入到到目标数据
     *
     * @param to   目标类型的数据
     * @param from 来源类型的数据
     * @return 写入之后的值
     */
    public static Object writeWithFromMap(Object to, Map from) {
        return writeWithFromMap(false, to, from);
    }

    /**
     * 将值吸入到到目标数据
     *
     * @param emptyWrite 为空时是否写入
     * @param to         目标类型的数据
     * @param from       来源类型的数据
     * @return 写入之后的值
     */
    public static Object writeWithFromMap(boolean emptyWrite, Object to, Map from) {
        for (Object key : from.keySet()) {
            Object fromValue = from.get(key);
            if (StringHelper.isEmpty(fromValue)) {
                continue;
            }
            ObjectHelper.set(to, StringHelper.toString(key), fromValue);
        }
        return to;
    }

    /**
     * 根据来源类型的字段,将数据写入目标类型的数据,为空时不写入
     *
     * @param to   目标类型的数据
     * @param from 来源类型的数据
     * @return 写入之后的值
     */
    public static Object writeWithFromClass(Object to, Object from) {
        return writeWithFromClass(false, to, from);
    }

    /**
     * 根据来源类型的字段,将数据写入目标类型的数据
     *
     * @param emptyWrite 为空时是否写入
     * @param to         目标类型的数据
     * @param from       来源类型的数据
     * @return 写入之后的值
     */
    public static Object writeWithFromClass(boolean emptyWrite, Object to, Object from) {
        if (from == null || to == null) {
            return to;
        }
        HashMap<String, MethodField> mapField = getInitTypeField(from.getClass());
        return writeWithClass(emptyWrite, to, from, mapField);
    }

    /**
     * 根据字段类型写入值
     *
     * @param emptyWrite 为空时是否写入
     * @param to         目标类型的数据
     * @param from       来源类型的数据
     * @param mapField   需要写入的字段
     * @return 写入之后的值
     */
    private static Object writeWithClass(boolean emptyWrite, Object to, Object from, HashMap<String, MethodField> mapField) {
        if (from == null || to == null) {
            return to;
        }
        for (Map.Entry<String, MethodField> field : mapField.entrySet()) {
            String name = field.getValue().getName();
            Object fromValue = ObjectHelper.get(from, name);
            if (StringHelper.isEmpty(fromValue)) {
                continue;
            }
            ObjectHelper.set(to, name, fromValue);
        }
        return to;
    }

    /**
     * 将对象转换为指定列表
     *
     * @param cls   需要转换后的结果类型
     * @param froms 需要转换后的值
     * @param <T>   转换的类型
     * @return 转换后的结果
     */
    public static <T> ArrayList<T> getList(Class<T> cls, Object froms) {
        ArrayList<T> tos = new ArrayList<T>();
        if (froms instanceof List) {
            List vCodeFrom = (List) froms;
            for (Object from : vCodeFrom) {
                if (StringHelper.isEmpty(from)) {
                    continue;
                }
                T to = StringHelper.to(cls, from);
                tos.add(to);
            }
        } else {
            if (!StringHelper.isEmpty(froms)) {
                T to = StringHelper.to(cls, froms);
                tos.add(to);
            }
        }
        return tos;
    }


    /**
     * 将对象转换为数组
     *
     * @param froms 需要转换的数组
     * @return 数组类型
     */
    public static <T extends Object> T[] getArray(T... froms) {
        return froms;
    }

    /**
     * 动态创建数组
     *
     * @param cls    数组类型
     * @param length 长度
     * @param <T>    泛型
     * @return 数组长度
     */
    public static <T extends Object> T[] createArray(Class<?> cls, int length) {
        Object array = Array.newInstance(cls, length);
        return (T[]) array;
    }
}