package com.codvision.commoncore.common;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ExpiryMap<K, V> implements Map<K, V> {

    private boolean isRefresh = false;
    private ScheduledExecutorService scheduledExecutorService;
    private static final ConcurrentHashMap WORK_MAP = new ConcurrentHashMap();
    private static final ConcurrentHashMap<Object, Long> EXPIRY_MAP = new ConcurrentHashMap<>();

    public ExpiryMap() {
        super();
    }

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

    public void closeMap() {
        scheduledExecutorService.shutdown();
    }

    /**
     * 定时清除失效key
     */
    public void startClearKey() {
        scheduledExecutorService = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build());
        scheduledExecutorService.scheduleAtFixedRate(
                new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("===" + Thread.currentThread().getName() + "===");
                        removeInvalidKeys();
                    }
                }
                , 0, 3600, TimeUnit.SECONDS);
    }

    private static void removeInvalidKeys() {
        EXPIRY_MAP.keySet().forEach(key -> {
            if (EXPIRY_MAP.get(key) < System.currentTimeMillis()) {
                EXPIRY_MAP.remove(key);
                WORK_MAP.remove(key);
            }
        });
        System.gc();
    }

    /**
     * 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
            EXPIRY_MAP.put(key, System.currentTimeMillis() + expiry);
        }
        WORK_MAP.put(key, value);
        return value;
    }

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

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

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

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

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

    @Deprecated
    @Override
    public V put(K key, V value) {
        throw new RuntimeException("此方法已废弃！请加上key失效时间");
    }

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

    }

    @Deprecated
    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        throw new RuntimeException("此方法已废弃！");
    }

    @Override
    public void clear() {
        EXPIRY_MAP.clear();
        WORK_MAP.clear();
    }

    @Override
    public Set<K> keySet() {
        removeInvalidKeys();
        return WORK_MAP.keySet();
    }

    @Override
    public Collection<V> values() {
        removeInvalidKeys();
        return WORK_MAP.values();
    }

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

    public static void main(String[] args) {
        //ExpiryMap emap=new ExpiryMap();
        ExpiryMap emap = new ExpiryMap(true);
        emap.put("key1", "value1", 2 * 1000);
        emap.put("key2", "value2", 5 * 1000);
        emap.put("key3", "value3", 15 * 1000);
        System.out.println(emap.size());
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(emap.size());
        System.out.println(emap.keySet());
        System.out.println(emap.values());
        System.out.println(emap.entrySet());
        emap.put("key4", "value4", 8 * 1000);
        System.out.println(emap.size());
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(emap.size());
        System.out.println(emap.keySet());
        System.out.println(emap.values());
        System.out.println(emap.entrySet());

        emap.closeMap();
    }
}
