package com.liquidnet.commons.lang.util;

import org.springframework.util.LinkedMultiValueMap;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Predicate;

public class CollectionUtil {
    private static final HashMap<String, String> STRING_STRING_HASH_MAP = new HashMap<>();
    private static final HashMap<String, Object> STRING_OBJECT_HASH_MAP = new HashMap<>();
    private static final HashMap<String, List<String>> STRING_LIST_HASH_MAP = new HashMap<>();
    private static final HashMap<String, Integer> STRING_INTEGER_HASH_MAP = new HashMap<>();
    private static final HashMap<String, BigDecimal> STRING_BIG_DECIMAL_HASH_MAP = new HashMap<>();
    private static final HashMap<String, Map<String, Object>> STRING_MAP_HASH_MAP = new HashMap<>();
    private static final LinkedList<String> STRING_LINKED_LIST = new LinkedList<>();
    private static final LinkedList<Object[]> OBJECTS_LINKED_LIST = new LinkedList<>();
    private static final LinkedMultiValueMap<String, String> STRING_STRING_LINKED_MULTI_VALUE_MAP = new LinkedMultiValueMap<>();
    private static final ArrayList<String> STRING_ARRAY_LIST = new ArrayList<>();
    private static final HashSet<String> STRING_ARRAY_SET = new HashSet<>();
    private static final ArrayList<Integer> INTEGER_ARRAY_LIST = new ArrayList<>();
    private static final ArrayList<Object> OBJECT_ARRAY_LIST = new ArrayList<>();
    private static final ArrayList<BigDecimal> BIG_DECIMALS_ARRAY_LIST = new ArrayList<>();
    private static final ArrayList<BigDecimal> BIGDECIMAL_ARRAY_LIST = new ArrayList<>();

    public static HashMap<String, String> mapStringString() {
        return (HashMap<String, String>) STRING_STRING_HASH_MAP.clone();
    }
    public static HashMap<String, List<String>> mapStringList() {
        return (HashMap<String, List<String>>) STRING_LIST_HASH_MAP.clone();
    }

    public static HashMap<String, Object> mapStringObject() {
        return (HashMap<String, Object>) STRING_OBJECT_HASH_MAP.clone();
    }

    public static HashMap<String, Integer> mapStringInteger() {
        return (HashMap<String, Integer>) STRING_INTEGER_HASH_MAP.clone();
    }

    public static HashMap<String, BigDecimal> mapStringBigDecimal() {
        return (HashMap<String, BigDecimal>) STRING_BIG_DECIMAL_HASH_MAP.clone();
    }

    public static HashMap<String, Map<String, Object>> mapHashMap() {
        return (HashMap<String, Map<String, Object>>) STRING_MAP_HASH_MAP.clone();
    }

    public static LinkedList<String> linkedListString() {
        return (LinkedList<String>) STRING_LINKED_LIST.clone();
    }

    public static LinkedList<Object[]> linkedListObjectArr() {
        return (LinkedList<Object[]>) OBJECTS_LINKED_LIST.clone();
    }

    public static ArrayList<BigDecimal> arrayListBigDeciaml() {
        return (ArrayList<BigDecimal>) BIGDECIMAL_ARRAY_LIST.clone();
    }

    public static LinkedMultiValueMap<String, String> linkedMultiValueMapStringString() {
        return (LinkedMultiValueMap<String, String>) STRING_STRING_LINKED_MULTI_VALUE_MAP.clone();
    }

    public static ArrayList<String> arrayListString() {
        return (ArrayList<String>) STRING_ARRAY_LIST.clone();
    }

    public static HashSet<String> getStringArraySet() {
        return (HashSet<String>) STRING_ARRAY_SET.clone();
    }

    public static ArrayList<Integer> arrayListInteger() {
        return (ArrayList<Integer>) INTEGER_ARRAY_LIST.clone();
    }

    public static ArrayList<BigDecimal> arrayListBigDecimal() {
        return (ArrayList<BigDecimal>) BIG_DECIMALS_ARRAY_LIST.clone();
    }


    public static ArrayList<Object> arrayListObject() {
        return (ArrayList<Object>) OBJECT_ARRAY_LIST.clone();
    }


    @SuppressWarnings("unchecked")
    public static <T> T get(Map<?, ?> map, Object obj, Class<T> clazz) {
        return (T) map.get(obj);
    }

    public static <E> boolean isEmpty(Collection<E> c) {
        return c == null || c.isEmpty();
    }

    /**
     * 移除集合中符合条件的元素
     *
     * @param list        List集合
     * @param test        移除条件
     * @param breakIfFind 是否在第一次移除后就停止查找
     * @param <T>
     */
    public static <T> void remove(List<T> list, Predicate<T> test, boolean breakIfFind) {
        if (list == null) {
            return;
        }
        Iterator<T> it = list.iterator();
        while (it.hasNext()) {
            if (test.test(it.next())) {
                it.remove();
                if (breakIfFind) {
                    break;
                }
            }
        }
    }

    /**
     * 遍历全部元素,并移除其中符合条件的元素
     *
     * @param list List集合
     * @param test 移除条件
     * @param <T>
     */
    public static <T> void remove(List<T> list, Predicate<T> test) {
        remove(list, test, false);
    }

    /**
     * 遍历全部元素,并移除其中与目标对象相同的元素
     *
     * @param list          List集合
     * @param targetElement 目标对象
     * @param <T>
     */
    public static <T> void remove(List<T> list, T targetElement) {
        remove(list, (Predicate<T>) t -> t.equals(targetElement));
    }

    /**
     * 遍历全部元素,并移除其中的null元素
     *
     * @param list List集合
     * @param <T>
     */
    public static <T> void removeNull(List<T> list) {
        remove(list, (Predicate<T>) t -> t == null);
    }


    /**
     * 遍历map中的全部value,并且移除符合条件的元素
     *
     * @param map  Map集合
     * @param test 移除条件
     * @param <V>
     */
    public static <V> void remove(Map<?, V> map, Predicate<V> test) {
        if (map == null) {
            return;
        }
        Iterator<V> it = map.values().iterator();
        while (it.hasNext()) {
            if (test.test(it.next())) {
                it.remove();
            }
        }
    }

    /**
     * 遍历map中的全部value,并且移除与目标对象相同的元素
     *
     * @param map           Map集合
     * @param targetElement 目标对象
     * @param <V>
     */
    public static <V> void remove(Map<?, V> map, V targetElement) {
        remove(map, (Predicate<V>) v -> v.equals(targetElement));
    }

    /**
     * 遍历map中的全部value,并且移除其中的null值元素
     *
     * @param map Map集合
     * @param <V>
     */
    public static <V> void removeNull(Map<?, V> map) {
        remove(map, (Predicate<V>) v -> v == null);
    }

}
