package commons.collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import commons.collections.interfaces.Sized;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
public class MapMap<T> implements Sized
{

private Map<String, Map<String, T>> outer = new HashMap<String, Map<String, T>>();

/**
 * Uses synchronized block for thread safe input.
 */
public void put (String k1, String k2, T val)
{
   synchronized (outer)
   {
      Map<String, T> imap = outer.get(k1);
      // if (outer.containsKey(k1))
      if (imap != null)
      {
         imap.put(k2, val);
      }
      else
      {
         Map<String, T> i = new HashMap<String, T>();
         i.put(k2, val);
         outer.put(k1, i);
      }
   }
}

private void put_directly (String k1, String k2, T val)
{
   Map<String, T> i = (Map<String, T>) outer.get(k1);
   i.put(k2, val);
   outer.put(k1, i);
}

/**
 * Not thread safe!
 */
public void putMutualExclusive (String k1, String k2, T val)
{
   if (contains(k1, k2)) put_directly(k1, k2, val);
   else if (contains(k2, k1)) put_directly(k2, k1, val);
   else
   {
      Map<String, T> i = null;
      if (contains(k1))
      {
         i = get(k1);
         i.put(k2, val);
         outer.put(k1, i);
      }
      else if (contains(k2))
      {
         i = get(k2);
         i.put(k1, val);
         outer.put(k2, i);
      }
      else
      {
         i = new HashMap<String, T>();
         i.put(k2, val);
         outer.put(k1, i);
      }
   }
}

public int size ()
{
   return outer.size();
}

/**
 * rough approximation
 */
public long signs ()
{
   long signs = 0;
   Iterator<String> I = keyIterator();
   // calc size of one inner map
   if (I.hasNext())
   {
      String t = I.next();
      signs += t.length();
      for (Iterator<String> II = innerKeyIterator(t); II.hasNext();)
         signs += II.next().length();
   }
   // multiply with outer map size
   return signs * outer.size();
}

public int size (String outerKey)
{
   return outer.containsKey(outerKey) ? outer.get(outerKey).size() : 0;
}

public void clear ()
{
   outer.clear();
}

public Iterator<String> keyIterator ()
{
   return outer.keySet().iterator();
}

/**
 * This iterator is not respecting the sorting order
 * but fastly accessing a subset through a map interface!! 
 * @param outerKey
 * @return
 */
public Iterator<String> innerKeyIterator (final String outerKey)
{
   return new Iterator<String>()
   {

      Iterator<String> innerIterator = null;

      public boolean hasNext ()
      {
         if (innerIterator == null)
         {
            if (!outer.containsKey(outerKey)) return false;
            else innerIterator = get(outerKey).keySet().iterator();
         }
         return innerIterator.hasNext();
      }

      public String next ()
      {
         return innerIterator.next();
      }

      public void remove ()
      {
         throw new NotImplementedException();
      }
   };
}

public Map<String, T> get (String key)
{
   return (Map<String, T>) outer.get(key);
}

public boolean contains (String key)
{
   return outer.containsKey(key);
}

public void remove (String key)
{
   if (outer.containsKey(key)) outer.remove(key);
}

public synchronized boolean contains (String k1, String k2)
{
   return outer.containsKey(k1) && ((Map<String, T>) outer.get(k1)).containsKey(k2);
}

public boolean containsMutual (String k1, String k2)
{
   Map<String, T> im;
   im = outer.get(k1);
   if (im != null && im.containsKey(k2)) return true;
   im = outer.get(k2);
   if (im != null && im.containsKey(k1)) return true;
   return false;
}

public void remove (String k1, String k2)
{
   if (outer.containsKey(k1))
   {
      Map<String, T> inner = outer.get(k1);
      if (inner.containsKey(k2))
      {
         inner.remove(k2);
         outer.put(k1, inner);
      }
   }
}

public T get (String k1, String k2)
{
   Map<String, T> im;
   im = outer.get(k1);
   return im == null ? null : im.get(k2);
}
}
