
package uk.me.mjt.ch;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;

public class BidirectionalTreeMap<K extends Comparable<K>,V> {
    private int mutationCounter = 0;
    private static final int VALIDATION_PERIOD_MUTATIONS = 0;
    
    private final TreeMap<K,V> forwardMap = new TreeMap<>();
    private final HashMap<V,K> reverseMap = new HashMap<>();
    
    public int size() {
        return forwardMap.size();
    }

    public void clear() {
        forwardMap.clear();
        reverseMap.clear();
        checkIfInteresting(null,null);
    }

    public void put(K key, V value) {
        if (key==null || value==null) {
            throw new IllegalArgumentException("Neither key nor value may be null;");
        }
        
        boolean forwardContains = forwardMap.containsKey(key);
        boolean reverseContains = reverseMap.containsKey(value);
        
        if (forwardContains || reverseContains) {
            throw new IllegalArgumentException("One or both values are already in the map?"
                    + " K:" + key + " " + forwardContains 
                    + " V:" + value + " " + reverseContains);
        }
        
        forwardMap.put(key, value);
        reverseMap.put(value, key);
        
        if (!forwardMap.containsKey(key) || !reverseMap.containsKey(value)) {
            throw new IllegalArgumentException("Add-retrieve failed?");
        }
        
        checkIfInteresting(key,value);
    }

    public boolean isEmpty() {
        return forwardMap.isEmpty();
    }

    public K lastKey() {
        return forwardMap.lastKey();
    }
    
    public Map.Entry<K,V> pollFirstEntry() {
        Map.Entry<K,V> result = forwardMap.pollFirstEntry();
        if (result != null) {
            reverseMap.remove(result.getValue());
            checkIfInteresting(result.getKey(),result.getValue());
        }
        return result;
    }

    /*public Map.Entry<K,V> pollLastEntry() {
        Map.Entry<K,V> result = forwardMap.pollLastEntry();
        if (result != null) {
            reverseMap.remove(result.getValue());
        }
        if (validateEverything) validateMaps();
        return result;
    }*/
    
    public K keyForValue(V value) {
        return reverseMap.get(value);
    }
    
    public V remove(K key) {
        if (!forwardMap.containsKey(key)) {
            throw new IllegalArgumentException("Attempt to remove absent item?");
        }
        
        V value = forwardMap.remove(key);
        reverseMap.remove(value);
        
        checkIfInteresting(key,value);
        return value;
    }
    
    public String printFirst(int count) {
        return entriesToString(forwardMap, count);
    }
    
    public String printLast(int count) {
        return entriesToString(forwardMap.descendingMap(), count);
    }
    
    private String entriesToString(NavigableMap<K,V> entries, int count) {
        StringBuilder sb = new StringBuilder();
        Iterator<Entry<K,V>> iter = entries.entrySet().iterator();
        while (iter.hasNext() && count-- > 0) {
            Entry<K,V> entry = iter.next();
            sb.append(entry.getKey()).append("->").append(entry.getValue()).append("\n");
        }
        return sb.toString();
    }
    
    private void checkIfInteresting(K key, V value) {
        mutationCounter++;
        if ( VALIDATION_PERIOD_MUTATIONS > 0 && mutationCounter%VALIDATION_PERIOD_MUTATIONS==0 ) {
            validateMaps();
        }
    }
    
    private void validateMaps() {
        if (forwardMap.size() != reverseMap.size()) {
            throw new IllegalStateException("Maps somehow became non-equal? "
                    + "Sizes don't match. Mutation Counter:" + mutationCounter);
        }
        
        for (Map.Entry<K,V> e : forwardMap.entrySet()) {
            K k1 = e.getKey();
            V v1 = k1==null?null:forwardMap.get(k1);
            K k2 = v1==null?null:reverseMap.get(v1);
            V v2 = k2==null?null:forwardMap.get(k2);
            
            if (k1==null || k2==null || v1==null || v2==null) {
                //System.out.println(forwardMap.getPathToKey(k1));
                //System.out.println(forwardMap.getPathFromEntry(e));
                throw new IllegalStateException("Maps somehow became non-equal?"
                        + " Mutation Counter:" + mutationCounter);
            }
            
            if (!k1.equals(k2) || !v1.equals(v2)) {
                throw new IllegalStateException("Maps somehow became non-equal?"
                        + " Mutation Counter:" + mutationCounter);
            }
        }
        
        
    }
    
    public String toString() {
        return forwardMap.toString();
    }
    
}

@@ -245,112 +245,8 @@
 %3E %7B%0A
-    private int mutationCounter = 0;%0A    private static final int VALIDATION_PERIOD_MUTATIONS = 0;%0A    %0A
     
@@ -519,47 +519,8 @@
 ();%0A
-        checkIfInteresting(null,null);%0A
     
@@ -1369,56 +1369,8 @@
   %7D%0A
-        %0A        checkIfInteresting(key,value);%0A
     
@@ -1718,386 +1718,39 @@
     
-    checkIfInteresting(result.getKey(),result.getValue());%0A        %7D%0A        return result;%0A    %7D%0A%0A    /*public Map.Entry%3CK,V%3E pollLastEntry() %7B%0A        Map.Entry%3CK,V%3E result = forwardMap.pollLastEntry();%0A        if (result != null) %7B%0A            reverseMap.remove(result.getValue());%0A        %7D%0A        if (validateEverything) validateMaps();%0A        return result;%0A    %7D*/%0A    
+%7D%0A        return result;%0A    %7D%0A
 %0A   
@@ -2094,47 +2094,8 @@
    %0A
-        checkIfInteresting(key,value);%0A
     
@@ -2773,1392 +2773,8 @@
    %0A
-    private void checkIfInteresting(K key, V value) %7B%0A        mutationCounter++;%0A        if ( VALIDATION_PERIOD_MUTATIONS %3E 0 && mutationCounter%25VALIDATION_PERIOD_MUTATIONS==0 ) %7B%0A            validateMaps();%0A        %7D%0A    %7D%0A    %0A    private void validateMaps() %7B%0A        if (forwardMap.size() != reverseMap.size()) %7B%0A            throw new IllegalStateException(%22Maps somehow became non-equal? %22%0A                    + %22Sizes don't match. Mutation Counter:%22 + mutationCounter);%0A        %7D%0A        %0A        for (Map.Entry%3CK,V%3E e : forwardMap.entrySet()) %7B%0A            K k1 = e.getKey();%0A            V v1 = k1==null?null:forwardMap.get(k1);%0A            K k2 = v1==null?null:reverseMap.get(v1);%0A            V v2 = k2==null?null:forwardMap.get(k2);%0A            %0A            if (k1==null %7C%7C k2==null %7C%7C v1==null %7C%7C v2==null) %7B%0A                //System.out.println(forwardMap.getPathToKey(k1));%0A                //System.out.println(forwardMap.getPathFromEntry(e));%0A                throw new IllegalStateException(%22Maps somehow became non-equal?%22%0A                        + %22 Mutation Counter:%22 + mutationCounter);%0A            %7D%0A            %0A            if (!k1.equals(k2) %7C%7C !v1.equals(v2)) %7B%0A                throw new IllegalStateException(%22Maps somehow became non-equal?%22%0A                        + %22 Mutation Counter:%22 + mutationCounter);%0A            %7D%0A        %7D%0A        %0A        %0A    %7D%0A    %0A
     

Remove disabled test code that only ran, to improve code coverage.