package _templates.javolution.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

import _templates.java.lang.UnsupportedOperationException;
import _templates.java.util.Collection;
import _templates.java.util.Iterator;
import _templates.java.util.Map;
import _templates.java.util.Set;
import _templates.javolution.lang.Reusable;

public class WeakFastMap/*<K,V>*/ implements Map/*<K,V>*/, Reusable
{
	private ReferenceQueue/*<K>*/ queue = new ReferenceQueue/*<K>*/();
	
	private FastMap/*<WeakKey<K>,V>*/ map;
	
	public WeakFastMap()
	{
		map = new FastMap/*<WeakKey<K>,V>*/();
	}
	
	public WeakFastMap(boolean shared)
	{
		this();
		if (shared)
			map.shared();
	}
	
	public WeakFastMap(int capacity, boolean shared)
	{
		map = new FastMap/*<WeakKey<K>,V>*/(capacity);
		if (shared)
			map.shared();
	}
	
	private Iterator/*<K>*/ keyIterator() {
		// remove garbage collected elements
		processQueue();

		// get an iterator of the superclass WeakHashSet
		final Iterator/*<WeakKey<K>>*/ i = map.keySet().iterator();

		return new Iterator/*<K>*/() {
			public boolean hasNext() {
				return i.hasNext();
			}

			public Object/*{K}*/ next() {
				// unwrap the element
				return getReferenceObject((WeakKey/*<K>*/) i.next());
			}

			public void remove() {
				// remove the element from the HashSet
				i.remove();
			}
		};
	}
	
	private Iterator/*<Entry<K,V>>*/ entryIterator() {
		// remove garbage collected elements
		processQueue();

		// get an iterator of the superclass WeakHashSet
		final Iterator/*<Entry<WeakKey<K>,V>>*/ i = map.entrySet().iterator();

		return new Iterator/*<Entry<K,V>>*/() {
			public boolean hasNext() {
				return i.hasNext();
			}

			public Object/*{Entry<K,V>}*/ next() {
				// unwrap the element
				return null;//new FastMap.Entry() //getReferenceObject((WeakKey/*<K>*/) i.next());
			}

			public void remove() {
				// remove the element from the HashSet
				i.remove();
			}
		};
	}
	
	
	@Override
	public void clear()
	{
		map.clear();
		while (queue.poll() != null)
			;
	}

	@Override
	public boolean containsKey(Object key)
	{
		processQueue();
		return map.containsKey(WeakKey.create(key));
	}

	@Override
	public boolean containsValue(Object value)
	{
		processQueue();
		return map.containsValue(value);
	}

	@Override
	public Object/*{V}*/ get(Object key)
	{
		processQueue();
		return map.get(WeakKey.create(key));
	}

	@Override
	public boolean isEmpty()
	{
		processQueue();
		return map.isEmpty();
	}

	@Override
	public Set keySet()
	{
		processQueue();
		final Set/*<WeakKey<K>>*/ set = map.keySet();
		return new Set/*<K>*/() {

			@Override
			public boolean add(Object o)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public boolean addAll(Collection c)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public void clear()
			{
				WeakFastMap.this.clear();
			}

			@Override
			public boolean contains(Object o)
			{
				return WeakFastMap.this.containsKey(o);
			}

			@Override
			public boolean containsAll(Collection/*<?>*/ c)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public boolean isEmpty()
			{
				return WeakFastMap.this.isEmpty();
			}

			@Override
			public Iterator/*<K>*/ iterator()
			{
				return WeakFastMap.this.keyIterator();
			}

			@Override
			public boolean remove(Object o)
			{
				return WeakFastMap.this.remove(o) != null;
			}

			@Override
			public boolean removeAll(Collection/*<?>*/ c)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public boolean retainAll(Collection/*<?>*/ c)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public int size()
			{
				return WeakFastMap.this.size();
			}

			@Override
			public Object[] toArray()
			{
				return toArray(new Object[size()]);
			}

			@Override
			public Object[] toArray(Object[] a)
			{
				throw new UnsupportedOperationException();
			}

		};
	}
	
	public Set/*<Entry<K, V>>*/ entrySet()
	{
		processQueue();
		final Set/*Entry<<WeakKey<K>>>*/ set = map.entrySet();
		return new Set/*<K>*/() {

			@Override
			public boolean add(Object o)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public boolean addAll(Collection c)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public void clear()
			{
				WeakFastMap.this.clear();
			}

			@Override
			public boolean contains(Object o)
			{
				return WeakFastMap.this.containsKey(o);
			}

			@Override
			public boolean containsAll(Collection/*<?>*/ c)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public boolean isEmpty()
			{
				return WeakFastMap.this.isEmpty();
			}

			@Override
			public Iterator/*<Entry<K, V>>*/ iterator()
			{
				return WeakFastMap.this.keyIterator();
			}

			@Override
			public boolean remove(Object o)
			{
				return WeakFastMap.this.remove(o) != null;
			}

			@Override
			public boolean removeAll(Collection/*<?>*/ c)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public boolean retainAll(Collection/*<?>*/ c)
			{
				throw new UnsupportedOperationException();
			}

			@Override
			public int size()
			{
				return WeakFastMap.this.size();
			}

			@Override
			public Object[] toArray()
			{
				return toArray(new Object[size()]);
			}

			@Override
			public Object[] toArray(Object[] a)
			{
				throw new UnsupportedOperationException();
			}

		};
	}

	@Override
	public Object/*{V}*/ put(Object/*{K}*/ key, Object/*{V}*/ value)
	{
		processQueue();
		return map.put(WeakKey.create(key, queue), value);
	}

	@Override
	public void putAll(Map/*<? extends K, ? extends V>*/ t)
	{
		for (Iterator i = t.entrySet().iterator(); i.hasNext();)
		{
			Map.Entry/*<K,V>*/e = (Map.Entry/*<K,V>*/) i.next();
			put(e.getKey(), e.getValue());
		}
	}

	@Override
	public Object/*{V}*/ remove(Object key)
	{
		processQueue();
		return map.remove(WeakKey.create(key));
	}

	@Override
	public int size()
	{
		processQueue();
		return map.size();
	}

	@Override
	public Collection/*<V>*/ values()
	{
		processQueue();
		return map.values();
	}

	@Override
	public void reset()
	{
		clear();
	}
	
	private final Object/*{K}*/ getReferenceObject(WeakKey/*<K>*/ ref) {
		return (ref == null) ? null : ref.get();
	}
	
	private final void processQueue() {
		WeakKey/*<K>*/ we = null;

		while ((we = (WeakKey/*<K>*/) this.queue.poll()) != null) {
			map.remove(we);
		}
	}
	
	static private class WeakKey/*<K>*/ extends WeakReference/*<K>*/ {
		private int hash; /* Hashcode of key, stored here since the key
                               may be tossed by the GC */

		private WeakKey(Object/*{K}*/ o) {
			super(o);
			hash = o.hashCode();
		}

		private WeakKey(Object/*{K}*/ o, ReferenceQueue/*<K>*/ q) {
			super(o, q);
			hash = o.hashCode();
		}

		private static /*<K>*/ WeakKey/*<K>*/ create(Object/*{K}*/ o) {
			return (o == null) ? null : new WeakKey/*<K>*/(o);
		}

		private static /*<K>*/ WeakKey/*<K>*/ create(Object/*{K}*/ o, ReferenceQueue/*<K>*/ q) {
			return (o == null) ? null : new WeakKey/*<K>*/(o, q);
		}

		/* A WeakElement is equal to another WeakElement iff they both refer to objects
               that are, in turn, equal according to their own equals methods */
		@Override
		public boolean equals(Object o) {
			if (this == o)
				return true;
			if (!(o instanceof WeakKey))
				return false;
			Object t = this.get();
			Object u = ((WeakKey/*<K>*/) o).get();
			if (t == u) 
				return true;
			if ((t == null) || (u == null))
				return false;
			return t.equals(u);
		}

		@Override
		public int hashCode() {
			return hash;
		}
	}

}
