package aQute.lib.collections;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class DoubleKeyMap<K1, K2, V> extends HashMap<K1,Map<K2,V>>implements Map<K1,Map<K2,V>> {
	private static final long	serialVersionUID	= 1L;
	private final Class< ? >	k1Class;
	private final Class< ? >	k2Class;
	private final Class< ? >	valueClass;

	public DoubleKeyMap() {
		k1Class = Object.class;
		k2Class = Object.class;
		valueClass = Object.class;
	}

	public DoubleKeyMap(Class<K1> k1Class, Class<K2> k2Class, Class<V> valueClass) {
		this.k1Class = k1Class;
		this.k2Class = k2Class;
		this.valueClass = valueClass;
	}

	public DoubleKeyMap(Map<K1,Map<K2,V>> other) {
		this();
		for (java.util.Map.Entry<K1,Map<K2,V>> e : other.entrySet()) {
			putAll(e.getKey(), e.getValue());
		}
	}

	public DoubleKeyMap(DoubleKeyMap<K1,K2,V> other) {
		k1Class = other.k1Class;
		k2Class = other.k2Class;
		valueClass = other.valueClass;

		for (java.util.Map.Entry<K1,Map<K2,V>> e : other.entrySet()) {
			putAll(e.getKey(), e.getValue());
		}
	}

	@SuppressWarnings("unchecked")
	public V put(K1 key1, K2 key2, V value) {
		assert k1Class.isInstance(key1);
		assert k2Class.isInstance(key2);
		assert valueClass.isInstance(value);

		Map<K2,V> map = get(key1);
		if (map == null) {
			map = new HashMap<K2,V>();
			if (valueClass != Object.class) {
				map = Collections.checkedMap(map, (Class<K2>) k2Class, (Class<V>) valueClass);
			}
			put(key1, map);
		}
		return map.put(key2, value);
	}

	public V get(K1 key1, K2 key2) {
		Map<K2,V> map = get(key1);
		if (map == null)
			return null;

		return map.get(key2);
	}

	public boolean containsKeys(K1 key1, K2 key2) {
		Map<K2,V> map = get(key1);
		if (map == null)
			return false;

		return map.containsKey(key2);
	}

	public void putAll(K1 key1, Map<K2,V> map) {
		assert k1Class.isInstance(key1);

		for (Map.Entry<K2,V> e : map.entrySet()) {
			put(key1, e.getKey(), e.getValue());
		}
	}

	public boolean removeValue(K1 key1, K2 key2, V value) {
		assert k1Class.isInstance(key1);
		assert k2Class.isInstance(key2);
		assert valueClass.isInstance(value);

		Map<K2,V> set = get(key1);
		if (set == null) {
			return false;
		}
		boolean result = set.values().remove(value);
		if (set.isEmpty())
			remove(key1);
		return result;
	}

	public V remove(K1 key1, K2 key2) {
		assert k1Class.isInstance(key1);
		assert k2Class.isInstance(key2);

		Map<K2,V> set = get(key1);
		if (set == null) {
			return null;
		}
		V result = set.remove(key2);
		if (set.isEmpty())
			remove(key1);
		return result;
	}

	public Iterator<Map.Entry<K2,V>> iterate(K1 key) {
		assert k1Class.isInstance(key);
		Map<K2,V> set = get(key);
		if (set == null)
			return new Iterator<Map.Entry<K2,V>>() {

				@Override
				public boolean hasNext() {
					return false;
				}

				@Override
				public Map.Entry<K2,V> next() {
					throw new UnsupportedOperationException();
				}

				@Override
				public void remove() {
					throw new UnsupportedOperationException();
				}

			};
		return set.entrySet().iterator();
	}

	public Iterator<V> all() {
		return new Iterator<V>() {
			Iterator<java.util.Map.Entry<K1,Map<K2,V>>> master = entrySet().iterator();
			Iterator<Map.Entry<K2,V>> current = null;

			public boolean hasNext() {
				if (current == null || !current.hasNext()) {
					if (master.hasNext()) {
						current = master.next().getValue().entrySet().iterator();
						return current.hasNext();
					}
					return false;
				}
				return true;
			}

			public V next() {
				return current.next().getValue();
			}

			public void remove() {
				current.remove();
			}

		};
	}

}

@@ -2346,16 +2346,19 @@
 V remove
+Key
 (K1 key1

Fix Java 8 conflict with DoubleKeyMap