package ink.wgink.util.map;

import ink.wgink.util.date.DateUtil;
import ink.wgink.util.string.WStringUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import javax.servlet.http.HttpServletRequest;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.*;
import java.util.Map.Entry;

/**
 * @author wenc
 * @className hashMap 工具类
 * @description hashMap 工具类
 * @date 2018年2月2日 下午4:59:38
 */
public class HashMapUtil {

    /**
     * 请求参数转Map
     *
     * @param request
     * @return
     */
    public static Map<String, Object> requestParamsToMap(HttpServletRequest request) {
        Enumeration<String> requestNames = request.getParameterNames();
        Map<String, Object> params = new HashMap<>(0);
        while (requestNames.hasMoreElements()) {
            String name = requestNames.nextElement();
            String value = request.getParameter(name);
            if (value.isEmpty()) {
                continue;
            }
            params.put(name, value);
        }
        return params;
    }

    /**
     * 类转Map
     *
     * @param object
     * @return
     */
    public static Map<String, Object> objectToMap(Object object) {
        Map<String, Object> result = null;
        if (object instanceof Map) {
            result = mapObjToMap(object);
        } else {
            try {
                result = beanToMap(object);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 类转Map<String,String>
     *
     * @param object
     * @return
     */
    public static Map<String, String> objectToMapString(Object object) {
        Map<String, String> result = null;
        if (object instanceof Map) {
            result = mapObjToMapString(object);
        } else {
            try {
                result = beanToMapString(object);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * map转map<String, String>
     *
     * @param object
     * @return
     */
    private static Map<String, String> mapObjToMapString(Object object) {
        Map<String, String> result = new HashMap<>(0);
        Map<?, ?> map = (Map<?, ?>) object;
        for (Entry<?, ?> entry : map.entrySet()) {
            String key = entry.getKey().toString();
            Object value = entry.getValue();
            result.put(key, value.toString());
        }
        return result;
    }

    /**
     * map转map
     *
     * @param object
     * @return
     */
    public static Map<String, Object> mapObjToMap(Object object) {
        Map<String, Object> result = new HashMap<>(0);
        Map<?, ?> map = (Map<?, ?>) object;
        for (Entry<?, ?> entry : map.entrySet()) {
            String key = entry.getKey().toString();
            Object value = entry.getValue();
            result.put(key, value);
        }
        return result;
    }

    /**
     * bean转map
     *
     * @param object
     * @return
     * @throws Exception
     */
    public static Map<String, Object> beanToMap(Object object) {
        try {
            Map<String, Object> result = null;
            BeanInfo beanInfo = null;
            beanInfo = Introspector.getBeanInfo(object.getClass());
            PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : properties) {
                if (null == result) {
                    result = new HashMap<>(0);
                }
                Method method = property.getReadMethod();
                String name = property.getName();
                if ("class".equals(name)) {
                    continue;
                }
                result.put(name, method.invoke(object));
            }
            return result;
        } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
            throw new TypeConversionException(e.getMessage(), e);
        }
    }

    /**
     * bean转Map<String, String>
     *
     * @param object
     * @return
     * @throws Exception
     */
    public static Map<String, String> beanToMapString(Object object) {
        try {
            Map<String, String> result = null;
            BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
            PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : properties) {
                if (null == result) {
                    result = new HashMap<>(0);
                }
                Method method = property.getReadMethod();
                String name = property.getName();
                if ("class".equals(name)) {
                    continue;
                }
                result.put(name, method.invoke(object).toString());
            }
            return result;
        } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
            throw new TypeConversionException(e.getMessage(), e);
        }
    }

    /**
     * map转obj，需要map的key和obj的属性对应，要有标准的set方法
     *
     * @param map
     * @param clazz
     * @return
     */
    public static <T> T mapToBean(Map<String, Object> map, Class<T> clazz) {
        try {
            Field[] fields = clazz.getDeclaredFields();
            Object instanceObj = clazz.newInstance();
            for (Entry<String, Object> kvs : map.entrySet()) {
                Object value = kvs.getValue();
                if (value == null) {
                    continue;
                }
                for (Field field : fields) {
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    if (StringUtils.equals(fieldName, kvs.getKey())) {
                        Method method = clazz.getMethod("set" + WStringUtil.firstToUpper(fieldName), field.getType());
                        if (field.getType() == int.class) {
                            method.invoke(instanceObj, Integer.parseInt(value.toString()));
                        } else if (field.getType() == String.class) {
                            method.invoke(instanceObj, value.toString());
                        } else if (field.getType() == Date.class) {
                            method.invoke(instanceObj, DateUtil.sdfTime.parse(value.toString()));
                        } else if (field.getType() == Double.class) {
                            method.invoke(instanceObj, Double.parseDouble(value.toString()));
                        } else if (field.getType() == Float.class) {
                            method.invoke(instanceObj, Float.parseFloat(value.toString()));
                        } else if (field.getType() == Long.class) {
                            method.invoke(instanceObj, Long.parseLong(value.toString()));
                        } else if (field.getType() == Boolean.class) {
                            method.invoke(instanceObj, Boolean.parseBoolean(value.toString()));
                        } else if (field.getType() == Short.class) {
                            method.invoke(instanceObj, Short.parseShort(value.toString()));
                        }
                        break;
                    }
                }
            }
            return clazz.cast(instanceObj);
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException | ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * map队列转bean队列
     *
     * @param listMap
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> mapListToBeanList(List<Map<String, Object>> listMap, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        if (listMap == null || listMap.isEmpty()) {
            return list;
        }
        for (Map<String, Object> map : listMap) {
            list.add(mapToBean(map, clazz));
        }
        return list;
    }

    /**
     * 冒泡排序
     *
     * @param sortMapList 待排序列表
     * @param sortKey     排序Key
     * @param isAsc       是否正向
     * @param isNumber    是否数字
     */
    public static void bubbleSort(List<Map<String, Object>> sortMapList, String sortKey, boolean isAsc, boolean isNumber) {
        Map<String, Object> tempMap;
        Map<String, Object> prevMap;
        Map<String, Object> nextMap;
        for (Map<String, Object> map : sortMapList) {
            for (int i = 0; i < sortMapList.size() - 1; i++) {
                int nextIndex = i + 1;
                prevMap = sortMapList.get(i);
                String prevSortKey = prevMap.get(sortKey) == null ? "" : prevMap.get(sortKey).toString();
                nextMap = sortMapList.get(nextIndex);
                String nextSortKey = nextMap.get(sortKey) == null ? "" : nextMap.get(sortKey).toString();
                if (isAsc) {
                    if (isNumber) {
                        if (NumberUtils.toDouble(prevSortKey, 0D) > NumberUtils.toDouble(nextSortKey, 0D)) {
                            tempMap = prevMap;
                            sortMapList.set(i, nextMap);
                            sortMapList.set(nextIndex, tempMap);
                        }
                    } else {
                        if (prevSortKey.compareTo(nextSortKey) > 0) {
                            tempMap = prevMap;
                            sortMapList.set(i, nextMap);
                            sortMapList.set(nextIndex, tempMap);
                        }
                    }
                } else {
                    if (isNumber) {
                        if (NumberUtils.toDouble(prevSortKey, 0D) < NumberUtils.toDouble(nextSortKey, 0D)) {
                            tempMap = prevMap;
                            sortMapList.set(i, nextMap);
                            sortMapList.set(nextIndex, tempMap);
                        }
                    } else {
                        if (prevSortKey.compareTo(nextSortKey) < 0) {
                            tempMap = prevMap;
                            sortMapList.set(i, nextMap);
                            sortMapList.set(nextIndex, tempMap);
                        }
                    }
                }
            }
        }
    }

    /**
     * 类型转换异常
     */
    public static class TypeConversionException extends RuntimeException {

        public TypeConversionException() {
        }

        public TypeConversionException(String message) {
            super(message);
        }

        public TypeConversionException(String message, Throwable cause) {
            super(message, cause);
        }

        public TypeConversionException(Throwable cause) {
            super(cause);
        }

        public TypeConversionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }

}
