package net.waltzstudio.base.framework.object;

import static net.waltzstudio.base.framework.util.SyntaxSugar.*;
import java.util.List;
import java.util.Map;

public class ObjectCache {
	protected static interface LoadFunctor<E> {
		E loadEntity();
	}
	protected static interface LoadWithCreateFunctor<E> {
		E loadEntity();
		E createEntity();
	}
	public static class Statistic {
		private long access = 0;
		private long load = 0;
		private long create = 0;
		private long hit = 0;

		private void addAccess() {
			access++;
		}
		private void addLoad() {
			load++;
		}
		private void addCreate() {
			create++;
		}
		private void addHit() {
			hit++;
		}

		public long getAccess() {
			return access;
		}
		public long getLoad() {
			return load;
		}
		public long getCreate() {
			return create;
		}
		public long getHit() {
			return hit;
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static final Map<String, Map> caches = M();

	@SuppressWarnings("unchecked")
	private static final Map<String, Statistic> statistics = M();

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private <K, V> Map<K, V> getCache(Long groupId, String cacheName) {
		String key = S(groupId, ":", cacheName);
		Map cache = caches.get(key);
		if (cache == null) {
			cache = M();
			caches.put(key, cache);
			statistics.put(key, new Statistic());
		}
		return cache;
	}

	protected <K, E> E getValueByKey(Long groupId, String cacheName, K key, LoadFunctor<E> functor) {
		Map<K, E> cache = getCache(groupId, cacheName);
		Statistic s = statistics.get(key);
		E e = cache.get(key);
		s.addAccess();
		if (e == null) {
			e = functor.loadEntity();
			s.addLoad();
			if (e != null) {
				cache.put(key, e);
			}
		} else {
			s.addHit();
		}
		return e;
	}

	protected <K, E> E getValueByKey(Long groupId, String cacheName, K key, LoadWithCreateFunctor<E> functor) {
		Map<K, E> cache = getCache(groupId, cacheName);
		Statistic s = statistics.get(key);
		E e = cache.get(key);
		s.addAccess();
		if (e == null) {
			e = functor.loadEntity();
			s.addLoad();
			if (e == null) {
				e = functor.createEntity();
				s.addCreate();
			}
			cache.put(key, e);
		} else {
			s.addHit();
		}
		return e;
	}

	@SuppressWarnings("unchecked")
	public void clearCache(Long groupId) {
		String key = S(groupId, ":");
		List<String> removes = L();
		for (String k : caches.keySet()) {
			if (k.startsWith(key)) {
				removes.add(k);
			}
		}
		for (String remove : removes) {
			caches.remove(remove);
			statistics.remove(remove);
		}
	}

	@SuppressWarnings("unchecked")
	public Map<String, Statistic> getMetrics() {
		Map<String, Statistic> s = M();
		for (Map.Entry<String, Statistic> entry : statistics.entrySet()) {
			s.put(entry.getKey(), entry.getValue());
		}
		return s;
	}
	@SuppressWarnings("unchecked")
	public Map<String, Statistic> getMetrics(Long groupId) {
		Map<String, Statistic> s = M();
		String key = S(groupId, ":");
		for (Map.Entry<String, Statistic> entry : statistics.entrySet()) {
			if (entry.getKey().startsWith(key)) {
				s.put(entry.getKey(), entry.getValue());
			}
		}
		return s;
	}

}
