package com.qy.worksheetsystem.util;


import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ：lhbj
 * @date ：Created in 2021/4/8 18:03
 * @description：带有效期的Map。
 * 每个key存入该map时，都需要设置key的有效期！当超期后，该key失效！
 * 此外，该map加入了一个定时器，每间隔一段时间，就自动扫描该map一次，清除失效的key
 * @modified By：
 * @version: $
 */
public class ExpiryMap<K, V> implements Map<K, V> {
    private boolean isRefresh = false;
    private static ConcurrentHashMap workMap = new ConcurrentHashMap();
    private static ConcurrentHashMap<Object, Long> expiryMap = new ConcurrentHashMap<>();

    public ExpiryMap() {
        super();
    }

    public ExpiryMap(boolean isRefresh) {
        this.isRefresh = isRefresh;
    }

    /**
     * 定时清除失效key

    static {
        int interval = 60;//间隔时间，单位：分
        int threshold = 10000;//map的容量阈值，达到该值后，将较频繁的执行key扫描工作！
        new Timer().schedule(new TimerTask() {
            int i = 0;
            @Override
            public void run() {
                boolean isRun = ++i % interval == 0 || expiryMap.keySet().size() > threshold;
                if (isRun) {
                    removeInValidKeys();
                }
            }
        }, 60*1000, 60*1000);//每隔1分钟启动一次
    }
     */
    private void removeInValidKeys(){
        for(Object key : expiryMap.keySet()){
            if(expiryMap.get(key) < System.currentTimeMillis()){
                expiryMap.remove(key);
                workMap.remove(key);
            }
        }
//        expiryMap.keySet().forEach(key->{
//
//        });
//        System.gc();
    }
    private void removeInValidKeys(Object key){
        if(null!=expiryMap.get(key)&&expiryMap.get(key) < System.currentTimeMillis()){
            expiryMap.remove(key);
            workMap.remove(key);
        }
    }
    /**
     * put方法，需要设置key 的有效期！单位为：毫秒
     * @param key
     * @param value
     * @param expiry key的有效期，单位：毫秒
     * @return
     */
    public V put(K key, V value, long expiry) {
        if (!containsKey(key) || isRefresh) {//更新value，只有需要刷新时间时才需要操作expiryMap
            expiryMap.put(key, System.currentTimeMillis() + expiry);
        }
        workMap.put(key, value);
        return value;
    }
    /**
     * survival方法，获取key 剩余存活时间单位为：毫秒
     * @param key
     * @return
     */
    public long survival(Object key){
        Long survival = (expiryMap.get(key)==null?0L:expiryMap.get(key));
        return survival>0?survival-System.currentTimeMillis():-1;
    }

    @Override
    public int size() {
        return keySet().size();
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    @Override
    public boolean containsKey(Object key) {
        if (key!=null &&expiryMap.containsKey(key)) {
            boolean flag = expiryMap.get(key) > System.currentTimeMillis();
            return flag;
        }
        return false;
    }

    @Override
    public boolean containsValue(Object value) {
        Collection values = workMap.values();
        if(values!=null){
            return values.contains(value);
        }
        return false;
    }

    @Override
    public V get(Object key) {
        removeInValidKeys(key);
        if (containsKey(key)) {
            return (V) workMap.get(key);
        }
        return null;
    }

    /**
     * put方法，默认设置key的有效期为1小时！单位为：毫秒
     * @param key
     * @param value
     * @return
     */
    @Deprecated
    @Override
    public V put(K key, V value) {
        if (!containsKey(key) || isRefresh) {//更新value，只有需要刷新时间时才需要操作expiryMap
            expiryMap.put(key, System.currentTimeMillis() + (1000*60*60L));
        }
        workMap.put(key, value);
        return value;
    }

    @Override
    public V remove(Object key) {
        boolean containKey=containsKey(key);
        expiryMap.remove(key);
        if(containKey){
            return (V) workMap.remove(key);
        }else{
            return null;
        }

    }
    /**
     * putAll方法，默认设置key的有效期为1小时！单位为：毫秒
     * @param m
     * @return
     */
    @Deprecated
    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        for (Entry<? extends K, ? extends V> e : m.entrySet()) {
            expiryMap.put(e.getKey(), System.currentTimeMillis() + (1000*60*60L));
        }
        workMap.putAll(m);
    }

    @Override
    public void clear() {
        expiryMap.clear();
        workMap.clear();
    }

    @Override
    public Set<K> keySet() {
        removeInValidKeys();
        return workMap.keySet();
    }

    @Override
    public Collection<V> values() {
        removeInValidKeys();
        return workMap.values();
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        removeInValidKeys();
        return workMap.entrySet();
    }



}
