/*		 
 * Copyright (C) 2002-2013 Sebastiano Vigna 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */


package PACKAGE;

import it.unimi.dsi.fastutil.objects.ObjectSet;
import it.unimi.dsi.fastutil.objects.ObjectSets;

import VALUE_PACKAGE.VALUE_COLLECTION;
import VALUE_PACKAGE.VALUE_COLLECTIONS;
#if ! #valueclass(Object)
import VALUE_PACKAGE.VALUE_SETS;
#endif

import java.util.Map;

/** A class providing static methods and objects that do useful things with type-specific maps.
 *
 * @see it.unimi.dsi.fastutil.Maps
 * @see java.util.Collections
 */

public class MAPS {

	private MAPS() {}


	/** An immutable class representing an empty type-specific map.
	 *
	 * <P>This class may be useful to implement your own in case you subclass
	 * a type-specific map.
	 */

	public static class EmptyMap KEY_VALUE_GENERIC extends FUNCTIONS.EmptyFunction KEY_VALUE_GENERIC implements MAP KEY_VALUE_GENERIC, java.io.Serializable, Cloneable {
	
		private static final long serialVersionUID = -7046029254386353129L;
	
		protected EmptyMap() {}
	
		public boolean containsValue( final VALUE_TYPE v ) { return false; }

		public void putAll( final Map<? extends KEY_GENERIC_CLASS, ? extends VALUE_GENERIC_CLASS> m ) { throw new UnsupportedOperationException(); }

		@SuppressWarnings("unchecked")
		public ObjectSet<MAP.Entry KEY_VALUE_GENERIC> ENTRYSET() { return ObjectSets.EMPTY_SET; }

		@SuppressWarnings("unchecked")
		public SET KEY_GENERIC keySet() { return SETS.EMPTY_SET; }

		@SuppressWarnings("unchecked")
		public VALUE_COLLECTION VALUE_GENERIC values() { return VALUE_SETS.EMPTY_SET; }

#if #values(primitive)
		public boolean containsValue( final Object ov ) { return false; }
#endif

        private Object readResolve() { return EMPTY_MAP; }

		public Object clone() { return EMPTY_MAP; }

		public boolean isEmpty() { return true; }

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public ObjectSet<Map.Entry<KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>> entrySet() { return (ObjectSet)ENTRYSET(); }

		public int hashCode() { return 0; }

		public boolean equals( final Object o ) {
			if ( ! ( o instanceof Map ) ) return false;

			return ((Map<?,?>)o).isEmpty();
		}	
		
		public String toString() { return "{}"; }
	}



	/** An empty type-specific map (immutable). It is serializable and cloneable. */
	 
	@SuppressWarnings("rawtypes")
	public static final EmptyMap EMPTY_MAP = new EmptyMap();


	/** An immutable class representing a type-specific singleton map.	 
	 *
	 * <P>This class may be useful to implement your own in case you subclass
	 * a type-specific map.
	 */

	public static class Singleton KEY_VALUE_GENERIC extends FUNCTIONS.Singleton KEY_VALUE_GENERIC implements MAP KEY_VALUE_GENERIC, java.io.Serializable, Cloneable {
	
		private static final long serialVersionUID = -7046029254386353129L;

		protected transient volatile ObjectSet<MAP.Entry KEY_VALUE_GENERIC> entries;
		protected transient volatile SET KEY_GENERIC keys;
		protected transient volatile VALUE_COLLECTION VALUE_GENERIC values;

		protected Singleton( final KEY_GENERIC_TYPE key, final VALUE_GENERIC_TYPE value ) {
			super( key, value );
		}
	
		public boolean containsValue( final VALUE_TYPE v ) { return VALUE_EQUALS( value, v ); }
#if #values(primitive)
		public boolean containsValue( final Object ov ) { return VALUE_EQUALS( VALUE_OBJ2TYPE( ov ), value ); }
#endif

		public void putAll( final Map<? extends KEY_GENERIC_CLASS, ? extends VALUE_GENERIC_CLASS> m ) { throw new UnsupportedOperationException(); }

		public ObjectSet<MAP.Entry KEY_VALUE_GENERIC> ENTRYSET() { if ( entries == null ) entries = ObjectSets.singleton( (MAP.Entry KEY_VALUE_GENERIC)new SingletonEntry() ); return entries; }
		public SET KEY_GENERIC keySet() { if ( keys == null ) keys = SETS.singleton( key ); return keys; }
		public VALUE_COLLECTION VALUE_GENERIC values() { if ( values == null ) values = VALUE_SETS.singleton( value ); return values; }

		protected class SingletonEntry implements MAP.Entry KEY_VALUE_GENERIC, Map.Entry<KEY_GENERIC_CLASS,VALUE_GENERIC_CLASS> {
			public KEY_GENERIC_CLASS getKey() { return KEY2OBJ( Singleton.this.key ); }
			public VALUE_GENERIC_CLASS getValue() { return VALUE2OBJ( Singleton.this.value ); }

#if #keys(primitive)
			public KEY_GENERIC_TYPE ENTRY_GET_KEY() { return Singleton.this.key; }
#endif

#if #values(primitive)
			public VALUE_GENERIC_TYPE ENTRY_GET_VALUE() { return Singleton.this.value; }
			public VALUE_GENERIC_TYPE setValue( final VALUE_GENERIC_TYPE value ) { throw new UnsupportedOperationException(); }
#endif

			public VALUE_GENERIC_CLASS setValue( final VALUE_GENERIC_CLASS value ) { throw new UnsupportedOperationException(); }
			
			public boolean equals( final Object o ) {
				if (!(o instanceof Map.Entry)) return false;
				Map.Entry<?,?> e = (Map.Entry<?,?>)o;
			
				return KEY_EQUALS( Singleton.this.key, KEY_OBJ2TYPE( e.getKey() ) ) && VALUE_EQUALS( Singleton.this.value, VALUE_OBJ2TYPE( e.getValue() ) );
			}
			
			public int hashCode() { return KEY2JAVAHASH( Singleton.this.key ) ^ VALUE2JAVAHASH( Singleton.this.value ); }
			public String toString() { return Singleton.this.key + "->" + Singleton.this.value; }
		}

		public boolean isEmpty() { return false; }

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public ObjectSet<Map.Entry<KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>> entrySet() { return (ObjectSet)ENTRYSET(); }

		public int hashCode() { return KEY2JAVAHASH( key ) ^ VALUE2JAVAHASH( value ); }

		public boolean equals( final Object o ) {
			if ( o == this ) return true;
			if ( ! ( o instanceof Map ) ) return false;

			Map<?,?> m = (Map<?,?>)o; 
			if ( m.size() != 1 ) return false; 
			return entrySet().iterator().next().equals( m.entrySet().iterator().next() ); 
		}	
		
		public String toString() { return "{" + key + "=>" + value + "}"; }
	}

	/** Returns a type-specific immutable map containing only the specified pair. The returned map is serializable and cloneable.
	 *
	 * <P>Note that albeit the returned map is immutable, its default return value may be changed.
	 *
	 * @param key the only key of the returned map.
	 * @param value the only value of the returned map.
	 * @return a type-specific immutable map containing just the pair <code>&lt;key,value></code>.
	 */

	public static KEY_VALUE_GENERIC MAP KEY_VALUE_GENERIC singleton( final KEY_GENERIC_TYPE key, VALUE_GENERIC_TYPE value ) {
		return new Singleton KEY_VALUE_GENERIC( key, value );
	}

#if #keys(primitive) || #values(primitive)

	/** Returns a type-specific immutable map containing only the specified pair. The returned map is serializable and cloneable.
	 *
	 * <P>Note that albeit the returned map is immutable, its default return value may be changed.
	 *
	 * @param key the only key of the returned map.
	 * @param value the only value of the returned map.
	 * @return a type-specific immutable map containing just the pair <code>&lt;key,value></code>.
	 */

	public static KEY_VALUE_GENERIC MAP KEY_VALUE_GENERIC singleton( final KEY_GENERIC_CLASS key, final VALUE_GENERIC_CLASS value ) {
		return new Singleton KEY_VALUE_GENERIC( KEY_CLASS2TYPE( key ), VALUE_CLASS2TYPE( value ) );
	}

#endif


	/** A synchronized wrapper class for maps. */

	public static class SynchronizedMap KEY_VALUE_GENERIC extends FUNCTIONS.SynchronizedFunction KEY_VALUE_GENERIC implements MAP KEY_VALUE_GENERIC, java.io.Serializable {

		private static final long serialVersionUID = -7046029254386353129L;

		protected final MAP KEY_VALUE_GENERIC map;

		protected transient volatile ObjectSet<MAP.Entry KEY_VALUE_GENERIC> entries;
		protected transient volatile SET KEY_GENERIC keys;
		protected transient volatile VALUE_COLLECTION VALUE_GENERIC values;

		protected SynchronizedMap( final MAP KEY_VALUE_GENERIC m, final Object sync ) {
			super( m, sync );
			this.map = m;
		}

		protected SynchronizedMap( final MAP KEY_VALUE_GENERIC m ) {
			super( m );
			this.map = m;
		}

		public int size() { synchronized( sync ) { return map.size(); } }
		public boolean containsKey( final KEY_TYPE k ) { synchronized( sync ) { return map.containsKey( k ); } }
		public boolean containsValue( final VALUE_TYPE v ) { synchronized( sync ) { return map.containsValue( v ); } }

		public VALUE_GENERIC_TYPE defaultReturnValue()  { synchronized( sync ) { return map.defaultReturnValue(); } }
		public void defaultReturnValue( final VALUE_GENERIC_TYPE defRetValue )  { synchronized( sync ) { map.defaultReturnValue( defRetValue ); } }

		public VALUE_GENERIC_TYPE put( final KEY_GENERIC_TYPE k, final VALUE_GENERIC_TYPE v ) { synchronized( sync ) { return map.put( k, v ); } }

		//public void putAll( final MAP KEY_VALUE_EXTENDS_GENERIC c ) { synchronized( sync ) { map.putAll( c ); } }
		public void putAll( final Map<? extends KEY_GENERIC_CLASS, ? extends VALUE_GENERIC_CLASS> m ) { synchronized( sync ) { map.putAll( m ); } }

		public ObjectSet<MAP.Entry KEY_VALUE_GENERIC> ENTRYSET() { if ( entries == null ) entries = ObjectSets.synchronize( map.ENTRYSET(), sync ); return entries; }
		public SET KEY_GENERIC keySet() { if ( keys == null ) keys = SETS.synchronize( map.keySet(), sync ); return keys; }
		public VALUE_COLLECTION VALUE_GENERIC values() { if ( values == null ) return VALUE_COLLECTIONS.synchronize( map.values(), sync ); return values; }

		public void clear() { synchronized( sync ) { map.clear(); } }
		public String toString() { synchronized( sync ) { return map.toString(); } }

#if #keys(primitive) || #values(primitive)
		public VALUE_GENERIC_CLASS put( final KEY_GENERIC_CLASS k, final VALUE_GENERIC_CLASS v ) { synchronized( sync ) { return map.put( k, v ); } }
#endif

#if #keys(primitive)
		public VALUE_GENERIC_TYPE remove( final KEY_GENERIC_TYPE k ) { synchronized( sync ) { return map.remove( k ); } }
		public VALUE_GENERIC_TYPE get( final KEY_GENERIC_TYPE k ) { synchronized( sync ) { return map.get( k ); } }
		public boolean containsKey( final Object ok ) { synchronized( sync ) { return map.containsKey( ok ); } }
#endif

#if #values(primitive)
		public boolean containsValue( final Object ov ) { synchronized( sync ) { return map.containsValue( ov ); } }
#endif

#if #keys(reference)
		public VALUE_GENERIC_TYPE REMOVE_VALUE( final Object k ) { synchronized( sync ) { return map.REMOVE_VALUE( k ); } }
		public VALUE_GENERIC_TYPE GET_VALUE( final Object k ) { synchronized( sync ) { return map.GET_VALUE( k ); } }
#endif

		public boolean isEmpty() { synchronized( sync ) { return map.isEmpty(); } }
		public ObjectSet<Map.Entry<KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>> entrySet() { synchronized( sync ) { return map.entrySet(); } }

		public int hashCode() { synchronized( sync ) { return map.hashCode(); } }
		public boolean equals( final Object o ) { synchronized( sync ) { return map.equals( o  ); } }
	}

	/** Returns a synchronized type-specific map backed by the given type-specific map.
	 *
	 * @param m the map to be wrapped in a synchronized map.
	 * @return a synchronized view of the specified map.
	 * @see java.util.Collections#synchronizedMap(Map)
	 */
	public static KEY_VALUE_GENERIC MAP KEY_VALUE_GENERIC synchronize( final MAP KEY_VALUE_GENERIC m ) { return new SynchronizedMap KEY_VALUE_GENERIC( m ); }

	/** Returns a synchronized type-specific map backed by the given type-specific map, using an assigned object to synchronize.
	 *
	 * @param m the map to be wrapped in a synchronized map.
	 * @param sync an object that will be used to synchronize the access to the map.
	 * @return a synchronized view of the specified map.
	 * @see java.util.Collections#synchronizedMap(Map)
	 */

	public static KEY_VALUE_GENERIC MAP KEY_VALUE_GENERIC synchronize( final MAP KEY_VALUE_GENERIC m, final Object sync ) { return new SynchronizedMap KEY_VALUE_GENERIC( m, sync ); }



	/** An unmodifiable wrapper class for maps. */

	public static class UnmodifiableMap KEY_VALUE_GENERIC extends FUNCTIONS.UnmodifiableFunction KEY_VALUE_GENERIC implements MAP KEY_VALUE_GENERIC, java.io.Serializable {

		private static final long serialVersionUID = -7046029254386353129L;

		protected final MAP KEY_VALUE_GENERIC map;

		protected transient volatile ObjectSet<MAP.Entry KEY_VALUE_GENERIC> entries;
		protected transient volatile SET KEY_GENERIC keys;
		protected transient volatile VALUE_COLLECTION VALUE_GENERIC values;

		protected UnmodifiableMap( final MAP KEY_VALUE_GENERIC m ) {
			super( m );
			this.map = m;
		}

		public int size() { return map.size(); }
		public boolean containsKey( final KEY_TYPE k ) { return map.containsKey( k ); }
		public boolean containsValue( final VALUE_TYPE v ) { return map.containsValue( v ); }

		public VALUE_GENERIC_TYPE defaultReturnValue()  { throw new UnsupportedOperationException(); }
		public void defaultReturnValue( final VALUE_GENERIC_TYPE defRetValue )  { throw new UnsupportedOperationException(); }

		public VALUE_GENERIC_TYPE put( final KEY_GENERIC_TYPE k, final VALUE_GENERIC_TYPE v ) { throw new UnsupportedOperationException(); }

		//public void putAll( final MAP KEY_VALUE_EXTENDS_GENERIC c ) { throw new UnsupportedOperationException(); }
		public void putAll( final Map<? extends KEY_GENERIC_CLASS, ? extends VALUE_GENERIC_CLASS> m ) { throw new UnsupportedOperationException(); }

		public ObjectSet<MAP.Entry KEY_VALUE_GENERIC> ENTRYSET() { if ( entries == null ) entries = ObjectSets.unmodifiable( map.ENTRYSET() ); return entries; }
		public SET KEY_GENERIC keySet() { if ( keys == null ) keys = SETS.unmodifiable( map.keySet() ); return keys; }
		public VALUE_COLLECTION VALUE_GENERIC values() { if ( values == null ) return VALUE_COLLECTIONS.unmodifiable( map.values() ); return values; }

		public void clear() { throw new UnsupportedOperationException(); }
		public String toString() { return map.toString(); }

#if #keys(primitive) && #values(primitive)
		public VALUE_GENERIC_CLASS put( final KEY_GENERIC_CLASS k, final VALUE_GENERIC_CLASS v ) { throw new UnsupportedOperationException(); }
#endif

#if #keys(primitive)
		public VALUE_GENERIC_TYPE remove( final KEY_GENERIC_TYPE k ) { throw new UnsupportedOperationException(); }
		public VALUE_GENERIC_TYPE get( final KEY_GENERIC_TYPE k ) { return map.get( k ); }
		public boolean containsKey( final Object ok ) { return map.containsKey( ok ); }
#endif

#if #values(primitive)
		public boolean containsValue( final Object ov ) { return map.containsValue( ov ); }
#endif

#if #keys(reference) || #values(reference)
		public VALUE_GENERIC_TYPE REMOVE_VALUE( final Object k ) { throw new UnsupportedOperationException(); }
		public VALUE_GENERIC_TYPE GET_VALUE( final Object k ) { return map.GET_VALUE( k ); }
#endif

		public boolean isEmpty() { return map.isEmpty(); }
		public ObjectSet<Map.Entry<KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>> entrySet() { return ObjectSets.unmodifiable( map.entrySet() ); }		
	}

	/** Returns an unmodifiable type-specific map backed by the given type-specific map.
	 *
	 * @param m the map to be wrapped in an unmodifiable map.
	 * @return an unmodifiable view of the specified map.
	 * @see java.util.Collections#unmodifiableMap(Map)
	 */
	public static KEY_VALUE_GENERIC MAP KEY_VALUE_GENERIC unmodifiable( final MAP KEY_VALUE_GENERIC m ) { return new UnmodifiableMap KEY_VALUE_GENERIC( m ); }

}
