/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
 * <a>http://www.crunii.com</a>
 */
package com.crunii.micro.common.concurrent.map;

import lombok.extern.slf4j.Slf4j;

import javax.annotation.concurrent.ThreadSafe;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * </pre>适合读多写少的一些场景</pre>
 *
 * @author 田平 create 2022/1/28 14:08
 */
@ThreadSafe
@Slf4j
public class MultiValueMapImpl<K, V> implements MultiValueMap<K, V> {
	private ConcurrentHashMap<K, List<V>> currMap;

	public MultiValueMapImpl() {
		currMap = new ConcurrentHashMap<>();
	}

	public MultiValueMapImpl(int initialCapacity) {
		currMap = new ConcurrentHashMap<>(initialCapacity);
	}

	public MultiValueMapImpl(int initialCapacity, int factor) {
		currMap = new ConcurrentHashMap<>(initialCapacity, factor);
	}

	public ConcurrentMap<K, List<V>> getCurrMap() {
		return currMap;
	}

	public boolean containsKey(K key, V value) {
		if (currMap.get(key) == null) {
			return false;
		} else {
			List<V> values = currMap.getOrDefault(key, new CopyOnWriteArrayList<V>());
			return values.contains(value);
		}
	}

	@Override
	public boolean put(K key, V value) {
		currMap.compute(key, (k, v) -> {
			if (v == null) {
				v = new CopyOnWriteArrayList<V>();
			}
			v.add(value);
			return v;
		});
		return true;
	}

	@Override
	public List<V> get(K key) {
		return currMap.get(key);
	}

	@Override
	public boolean remove(K key, V value) {
		currMap.computeIfPresent(key, (k, v) -> {
			v.remove(value);
			if (v.isEmpty()) {
				return null;
			} else {
				return v;
			}
		});
		return true;
	}

	@Override
	public List<V> remove(K key) {
		return currMap.remove(key);
	}

	@Override
	public int valueCount(K key) {
		List<V> values = currMap.get(key);
		if (values == null) {
			return 0;
		} else {
			return values.size();
		}
	}

	@Override
	public int size() {
		return (int) currMap.values().stream().flatMap(l -> l.stream()).count();
	}

	public static void main(String[] args) {
		MultiValueMap<Integer, String> map = new MultiValueMapImpl<>();
		map.put(1, "a");
		map.put(1, "b");
		System.out.println(map.valueCount(1));
		System.out.println(map.remove(1, "a"));
		System.out.println(map.size());
		System.out.println(map.valueCount(1));
		System.out.println(map.remove(1, "b"));
		System.out.println(map.get(1));
		System.out.println(map.valueCount(1));
		System.out.println(map.size());
		System.out.println(map.remove(1));
		System.out.println(map.size());

		//Multimap<Integer, String> map = Multimaps.synchronizedMultimap(ArrayListMultimap.create());
//		int doSize = 100000;
//		ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 0,
//				TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(doSize));
//		CountDownLatch startSignal = new CountDownLatch(1);
//		CountDownLatch doneSignal = new CountDownLatch(doSize);
//		AtomicInteger atomic = new AtomicInteger();
//		Date date = new Date();
//		for (int i = 0; i < doSize; i++) {
//			executor.execute(() -> {
//				try {
//					startSignal.await();
//				} catch (InterruptedException e) {
//					log.error("startSignal.await error!", e);
//				}
//				try {
//					int k = atomic.getAndIncrement() % 2000;
//					map.put(k, NanoIdUtil.randomLowerNanoId());
//				} finally {
//					doneSignal.countDown();
//				}
//			});
//		}
//		startSignal.countDown();
//		try {
//			doneSignal.await();
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
//		System.out.println(map.size());
//		System.out.println(DateUtil.getUseTime(date));
//		//读写对比
//		Date second = new Date();
//		CountDownLatch startSignal1 = new CountDownLatch(1);
//		CountDownLatch doneSignal1 = new CountDownLatch(doSize);
//		atomic.set(0);
//		for (int i = 0; i < doSize; i++) {
//			executor.execute(() -> {
//				try {
//					startSignal1.await();
//				} catch (InterruptedException e) {
//					log.error("startSignal.await error!", e);
//				}
//				try {
//					int k = atomic.getAndIncrement() % 2000;
//					map.get(k);
//				} finally {
//					doneSignal1.countDown();
//				}
//			});
//		}
//		startSignal1.countDown();
//		try {
//			doneSignal1.await();
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
//		System.out.println(DateUtil.getUseTime(second));
//		//map.getCurrMap().keySet().forEach(System.out::println);
//		executor.shutdown();
	}
}
