package ec.gp.semantic.library;

import java.util.*;

import library.generator.*;
import library.space.*;

final class Cache
{
	private final static int MAX_SIZE = 1000;
	private final Map<Object, CacheEntry> cache = new HashMap<Object, CacheEntry>();
	private long clock = 0;
	private final TreeSet<CacheEntry> LRU = new TreeSet<CacheEntry>(new Comparator<CacheEntry>() {

		@Override
		public int compare(CacheEntry o1, CacheEntry o2) {
			return Long.compare(o1.clock, o2.clock);
		}

	});

	/*private long hits = 0;
	private long misses = 0;*/

	public List<SearchResult<TreeNode>> get(final Object query, final Integer maxHeight)
	{
		CacheEntry entry = this.cache.get(query);

		if (entry != null)
		{
			this.LRU.remove(entry);
			entry.clock = this.clock++;
			this.LRU.add(entry);

			/*++hits;
			System.out.println(String.format("Cache hit! Hit ratio: %.3f Cache size: %d", (double) hits
					/ (hits + misses), cache.size()));*/
			return entry.data.get(maxHeight);
		}

		//++misses;
		return null;
	}

	public void set(final Object query, final List<SearchResult<TreeNode>> result, final Integer maxHeight)
	{
		/*if (1 == 1)
			return;*/

		assert query != null;

		CacheEntry entry = this.cache.get(query);

		if (entry == null) {
			entry = new CacheEntry();
			entry.key = query;
			entry.data = new HashMap<Integer, List<SearchResult<TreeNode>>>();
			entry.clock = this.clock++;
			this.cache.put(query, entry);
			this.LRU.add(entry);

			while (this.cache.size() > MAX_SIZE) {
				CacheEntry first = this.LRU.first(); // the least recently used entry
				this.LRU.remove(first);
				this.cache.remove(first.key);

				//System.out.println(String.format("Removing entry last used at %d, current clock: %d", first.clock, this.clock));
			}
		} else {
			entry.clock = this.clock++;
		}

		entry.data.put(maxHeight, result);
	}

	private class CacheEntry {
		public Object key;
		public HashMap<Integer, List<SearchResult<TreeNode>>> data;
		public long clock = 0;
	}
}
