package core.util;

import java.lang.ref.SoftReference;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;

/**
 * マップユーティリティ
 *
 * @author Tadashi Nakayama
 */
public final class MapUtil {

	/**
	 * コンストラクタ
	 */
	private MapUtil() {
		throw new AssertionError();
	}

	/**
	 * キャッシュリファレンスからマップを取得
	 *
	 * @param <K> ジェネリクス
	 * @param <V> ジェネリクス
	 * @param cache キャッシュリファレンス
	 * @return マップ
	 */
	public static <K, V> ConcurrentMap<K, V> getCacheMap(
			final AtomicReference<SoftReference<ConcurrentMap<K, V>>> cache) {
		ConcurrentMap<K, V> map = null;
		while (map == null && cache != null) {
			final var ref = cache.get();
			final var o = Optional.ofNullable(ref).flatMap(r -> Optional.ofNullable(r.get()));
			if (o.isPresent()) {
				map = o.get();
			} else {
				final var m = new ConcurrentHashMap<K, V>();
				if (cache.compareAndSet(ref, new SoftReference<>(m))) {
					map = m;
				}
			}
		}
		return map;
	}

	/**
	 * キャッシュリファレンスからマップを取得
	 *
	 * @param <K> ジェネリクス
	 * @param <V> ジェネリクス
	 * @param <E> ジェネリクス
	 * @param key キー
	 * @param cache キャッシュリファレンスマップ
	 * @return マップ
	 */
	public static <K, V, E> ConcurrentMap<K, V> getCacheMap(final E key,
			final ConcurrentMap<E, SoftReference<ConcurrentMap<K, V>>> cache) {
		ConcurrentMap<K, V> map = null;
		while (map == null && cache != null) {
			final var ref = cache.get(key);
			final var o = Optional.ofNullable(ref).flatMap(r -> Optional.ofNullable(r.get()));
			if (o.isPresent()) {
				map = o.get();
			} else {
				final var m = new ConcurrentHashMap<K, V>();
				if (ref == null) {
					if (cache.putIfAbsent(key, new SoftReference<>(m)) == null) {
						map = m;
					}
				} else {
					if (cache.replace(key, ref, new SoftReference<>(m))) {
						map = m;
					}
				}
			}
		}
		return map;
	}
}
