package yi.ds;

import java.util.*;
import java.io.*;

/**
 * HashMapHashMap implments a hashmap with hashmaps as value. eg. an entry may
 * looks like:
 * "Yi LI" -> {"math"->100, "english"->99, ...}
 */

public class HashMapHashMap extends HashMap{

  static final long serialVersionUID = -2076252353937617214L;
  public HashMapHashMap() {
  }

  /**
   * Put in an entry, like "Yi LI", "Math", 100
   * @param first_key
   * @param second_key
   * @param value
   */
  public void put(Object first_key, Object second_key, Object value) {
    HashMap entry = null;
    // check whether the key is there already
    if (containsKey(first_key)) {
      entry = (HashMap)get(first_key);
    } else {
      entry = new HashMap();
      put(first_key, entry);
    }
    entry.put(second_key, value);
  }

  public Object get(Object first_key, Object second_key) {
    if (!containsKey(first_key)) return null;
    return ((HashMap)get(first_key)).get(second_key);
  }

  public Object remove(Object first_key, Object second_key) {
    HashMap map1 = (HashMap)get(first_key);
    if (map1 == null) return null;
    return map1.remove(second_key);
  }


  /**
   * The values deep in the second layers will be added in.  eg. if
   * "Yi LI" -> {"math"->100, "english"->99, ...} will be put in, those "100"
   * "99" will be replaced, instead of the whole {"math"->100, "english"->99, ...}
   * @param map
   */
  public void putAllInDeep(HashMapHashMap map) {
    for (Iterator i= map.keySet().iterator(); i.hasNext(); ) {
      Object key1 = i.next();
      HashMap map2 = (HashMap) map.get(key1);
      for (Iterator j= map2.keySet().iterator(); j.hasNext(); ) {
        Object key2 = j.next();
        Object value = map2.get(key2);
        put(key1, key2, value);
      }
    }
  }

  /**
   * Return the mapping size total from all the hashmaps
   * @return
   */
  public int deepSize() {
    int size = 0;
    for (Iterator i= keySet().iterator(); i.hasNext(); ) {
      Object key1 = i.next();
      HashMap map2 = (HashMap) get(key1);
      size += map2.size();
    }
    return size;
  }

  public void clear() {
    for (Iterator i= keySet().iterator(); i.hasNext(); ) {
      HashMap map2 = (HashMap) get(i.next());
      map2.clear();
    }
    super.clear();
  }

  /**
   * Swap the keys (first_key <-> second_key).  For example:
   * "Yi LI" -> {"math"->100, "english"->99, ...} will be swap to
   * "math" -> {"Yi Li"->100, "San Zhang"->50, ...}
   * @param hmhm
   * @return
   */
  public static HashMapHashMap swapKeys(HashMapHashMap hmhm) {
    // the swapped result
    HashMapHashMap swapped = new HashMapHashMap();

    // loop through first keys
    for (Iterator i = hmhm.keySet().iterator(); i.hasNext(); ) {
      Object first_key = i.next();
      // loop through second keys
      HashMap entry = (HashMap) hmhm.get(first_key);
      for (Iterator j = entry.keySet().iterator(); j.hasNext(); ) {
        Object second_key = j.next();
        // swap their positions
        swapped.put(second_key, first_key, entry.get(second_key));
      }
    }
    return swapped;
  }


  public String toString() {
    StringWriter str = new StringWriter();
    PrintWriter out = new PrintWriter(str);

    ArrayList keys = new ArrayList(keySet());
    Collections.sort(keys);
    for (Iterator i=keys.iterator(); i.hasNext(); ) {
      Object key = i.next();
      out.print(key.toString() + "->{");
      HashMap second_map = (HashMap)get(key);
      ArrayList second_keys = new ArrayList(second_map.keySet());
      Collections.sort(second_keys);
      boolean first = true;
      for (Iterator j=second_keys.iterator(); j.hasNext(); ) {
        if (first) {
          first = false;
        } else {
          out.print(";");
        }
        Object key2 = j.next();
        out.print(key2 + "->" + second_map.get(key2));
      }
      out.println("}");
    }
    return str.toString();
  }
}