package gtPlusPlus.api.objects.data;

import java.util.*;

public class ReverseAutoMap<N> extends AutoMap<N> {

	/**
	 * The Internal Map
	 */
	private Map<N, Integer> mInternalMapReverseLookup = new HashMap<N, Integer>();

	/**
	 * The Internal ID
	 */
	private int mInternalID = 0;
	private static final long serialVersionUID = 3771412318075131790L;

	@Override
	public Iterator<N> iterator() {		
		return values().iterator();
	}

	public synchronized boolean setValue(N object){
		int mOriginalID = this.mInternalID;
		put(object);		
		if (this.mInternalMap.get(mOriginalID).equals(object) || mOriginalID > this.mInternalID){
			return true;
		}
		else {
			return false;
		}
	}

	@Override
	public synchronized N put(N object){
		return set(object);
	}

	@Override
	public synchronized N set(N object){
		int newID = getNextFreeMapID();
		mInternalMapReverseLookup.put(object, newID);
		return mInternalMap.put(newID, object);
	}

	public synchronized int putToInternalMap(N object){
		return setInternalMap(object);
	}

	public synchronized int setInternalMap(N object){
		int newID = getNextFreeMapID();			
		mInternalMap.put(newID, object);		
		mInternalMapReverseLookup.put(object, newID);
		return newID;
	}

	public synchronized boolean injectCleanDataToAutoMap(Integer g, N object){
		if (!mInternalMap.containsKey(g) && !mInternalMapReverseLookup.containsKey(object)) {
			int a1 = 0, a2 = 0, a11 = 0, a22 = 0;
			a1 = mInternalMap.size();
			a2 = mInternalMapReverseLookup.size();
			a11 = a1;
			a22 = a2;
			mInternalMap.put(g, object);	
			a1 = mInternalMap.size();
			mInternalMapReverseLookup.put(object, g);
			a2 = mInternalMapReverseLookup.size();			
			if (a1 > a11 && a2 > a22)			
				return true;
		}
		return false;
	}

	public synchronized boolean injectDataToAutoMap(Integer g, N object){
		int a1 = 0, a2 = 0, a11 = 0, a22 = 0;
		a1 = mInternalMap.size();
		a2 = mInternalMapReverseLookup.size();
		a11 = a1;
		a22 = a2;
		mInternalMap.put(g, object);	
		a1 = mInternalMap.size();
		mInternalMapReverseLookup.put(object, g);
		a2 = mInternalMapReverseLookup.size();			
		if (a1 > a11 && a2 > a22)			
			return true;		
		return false;
	}

	private boolean raiseInternalID() {
		int mOld = mInternalID;
		mInternalID++;
		return mInternalID > mOld;
	}

	public synchronized int getNextFreeMapID() {
		if (raiseInternalID()) {
			return mInternalID;
		}
		return Short.MIN_VALUE;
	}

	@Override
	public synchronized N get(int id){		
		return mInternalMap.get(id);
	}

	public synchronized int get(N key) {		
		return mInternalMapReverseLookup.get(key);
	}

	@Override
	public synchronized Collection<N> values(){
		return mInternalMap.values();
	}

	public synchronized Collection<Integer> keys(){
		return mInternalMapReverseLookup.values();
	}

	@Override
	public synchronized int size(){
		return mInternalMap.size();
	}

	@Override
	public synchronized int hashCode(){
		return mInternalMap.hashCode()+mInternalMapReverseLookup.hashCode();
	}

	@Override
	public synchronized boolean containsKey(int key){
		return mInternalMap.containsKey(key);
	}

	@Override
	public synchronized boolean containsValue(N value){
		return mInternalMap.containsValue(value);
	}

	public synchronized boolean containsKey(N key){
		return mInternalMapReverseLookup.containsKey(key);
	}

	public synchronized boolean containsValue(int value){
		return mInternalMapReverseLookup.containsValue(value);
	}

	@Override
	public synchronized boolean isEmpty(){
		return mInternalMap.isEmpty() && mInternalMapReverseLookup.isEmpty();
	}

	@Override
	public synchronized void clear(){
		this.mInternalID = 0;
		this.mInternalMap.clear();
		this.mInternalMapReverseLookup.clear();
		return;
	}

	@Override
	public synchronized N[] toArray() {		
		Collection<N> col = this.mInternalMap.values();
		@SuppressWarnings("unchecked")
		N[] val = (N[]) col.toArray();		
		return val;
	}

	public synchronized Integer[] toArrayInternalMap() {		
		Collection<Integer> col = this.mInternalMapReverseLookup.values();
		Integer[] val = col.toArray(new Integer[col.size()]);	
		return val;
	}

}