package cn.net.caoyao.cpzshandset.common.bitmap.core;

import java.util.LinkedHashMap;
import java.util.Map;

public class LruMemoryCache<K, V> {
	private final LinkedHashMap<K, V> map;
	
	private int size;
	private int maxSize;
	private int putCount;
	private int createCount;
	private int evictionCount;
	private int hitCount;
	private int missCount;

	public LruMemoryCache(int maxSize) {
		if (maxSize <= 0) {
			throw new IllegalArgumentException("maxSize <= 0");
		}
		this.maxSize = maxSize;
		this.map = new LinkedHashMap(0, 0.75F, true);
	}

	public final V get(K key) {
		if (key == null) {
			throw new NullPointerException("key == null");
		}

		synchronized (this) {
			Object mapValue = this.map.get(key);
			if (mapValue != null) {
				this.hitCount += 1;
				//--我修改的
//				return mapValue;
				return (V) mapValue;
			}
			this.missCount += 1;
		}
		Object mapValue;
		Object createdValue = create(key);
		if (createdValue == null) {
			return null;
		}

		synchronized (this) {
			this.createCount += 1;
			//--我修改的
//			mapValue = this.map.put(key, createdValue);
			mapValue = this.map.put(key, (V) createdValue);

			if (mapValue != null) {
				//--我修改的
//				this.map.put(key, mapValue);
				this.map.put(key, (V) mapValue);
			} else
				//--我修改的
//				this.size += safeSizeOf(key, createdValue);
				this.size += safeSizeOf(key, (V) createdValue);
		}

		if (mapValue != null) {
			//--我修改的
//			entryRemoved(false, key, createdValue, mapValue);
//			return mapValue;
			entryRemoved(false, key, (V)createdValue, (V)mapValue);
			return (V)mapValue;
		}
		trimToSize(this.maxSize);
		//--我修改的
//		return createdValue;
		return (V) createdValue;
	}

	@SuppressWarnings("unused")
	public final V put(K key, V value) {
		if ((key == null) || (value == null)) {
			throw new NullPointerException("key == null || value == null");
		}

		synchronized (this) {
			this.putCount += 1;
			this.size += safeSizeOf(key, value);
			Object previous = this.map.put(key, value);
			if (previous != null)
				//--我修改的
//				this.size -= safeSizeOf(key, previous);
			this.size -= safeSizeOf(key, (V) previous);
		}
		Object previous = null;
		if (previous != null) {
			//--我修改的
//			entryRemoved(false, key, previous, value);
			entryRemoved(false, key, (V) previous, value);
		}

		trimToSize(this.maxSize);
		//--我修改的
//		return previous;
		return (V) previous;
	}

	@SuppressWarnings("unchecked")
	private void trimToSize(int maxSize) {
		while (true) {
			synchronized (this) {
				if ((this.size < 0) || ((this.map.isEmpty()) && (this.size != 0))) {
					throw new IllegalStateException(
							getClass().getName() + ".sizeOf() is reporting inconsistent results!");
				}

				if ((this.size > maxSize) && (this.map.isEmpty())) {
					break;
				}
				Map.Entry toEvict = (Map.Entry) this.map.entrySet().iterator().next();
				Object key = toEvict.getKey();
				Object value = toEvict.getValue();
				this.map.remove(key);
				//--我修改的
//				this.size -= safeSizeOf(key, value);
				this.size -= safeSizeOf((K)key, (V)value);
				this.evictionCount += 1;
			}
			Object value = null;
			Object key = null;
			//--我修改的
//			entryRemoved(true, key, value, null);
			entryRemoved(true, (K)key, (V)value, null);
		}
	}

	@SuppressWarnings("unused")
	public final V remove(K key) {
		if (key == null) {
			throw new NullPointerException("key == null");
		}

		synchronized (this) {
			Object previous = this.map.remove(key);
			if (previous != null)
				//--我修改的
//				this.size -= safeSizeOf(key, previous);
			this.size -= safeSizeOf(key, (V) previous);
		}
		Object previous = null;
		if (previous != null) {
			//--我修改的
//			entryRemoved(false, key, previous, null);
			entryRemoved(false, key, (V) previous, null);
		}
		//--我修改的
//		return previous;
		return (V) previous;
	}

	protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {
	}

	protected V create(K key) {
		return null;
	}

	private int safeSizeOf(K key, V value) {
		int result = sizeOf(key, value);
		if (result < 0) {
			throw new IllegalStateException("Negative size: " + key + "=" + value);
		}
		return result;
	}

	protected int sizeOf(K key, V value) {
		return 1;
	}

	public final void evictAll() {
		trimToSize(-1);
	}

	public final synchronized int size() {
		return this.size;
	}

	public final synchronized int maxSize() {
		return this.maxSize;
	}

	public final synchronized int hitCount() {
		return this.hitCount;
	}

	public final synchronized int missCount() {
		return this.missCount;
	}

	public final synchronized int createCount() {
		return this.createCount;
	}

	public final synchronized int putCount() {
		return this.putCount;
	}

	public final synchronized int evictionCount() {
		return this.evictionCount;
	}

	public final synchronized Map<K, V> snapshot() {
		return new LinkedHashMap(this.map);
	}

	public final synchronized String toString() {
		int accesses = this.hitCount + this.missCount;
		int hitPercent = accesses != 0 ? 100 * this.hitCount / accesses : 0;
		return String.format("LruMemoryCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
				new Object[] { Integer.valueOf(this.maxSize), Integer.valueOf(this.hitCount),
						Integer.valueOf(this.missCount), Integer.valueOf(hitPercent) });
	}
}