package basics.collections;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import basics.testing.Units;

/**
 * This is a map. The map associates a key K with a set of value V. 
 * In fact the set is implemented as a List
 * for better handling, so it it does not implement the map interface.
 * Usage: 
 * put(a,1);
 * put(b,2);
 * size -> 2
 * put(a,2) 
 * size -> 2
 * get(a) -> List<?>.size()==2
 * @author kst
 * @param <K>
 * @param <V>
 */
public class MapSet<K, V>
{
private Map<K, List<V>> map_KListV = new HashMap<K, List<V>>();

public void put(K k, V v)
{
   List<V> setV = containsKey(k) ? get(k) : new ArrayList<V>();
   if(!setV.contains(v))
   {
      setV.add(v);
      map_KListV.put(k, setV);
   }
}

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

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

public Iterator<K> keyIterator()
{
   return map_KListV.keySet().iterator();
}

public List<V> get(K key)
{
   return (List<V>)map_KListV.get(key);
}

public boolean containsKey(K key)
{
   return map_KListV.containsKey(key);
}

public boolean contains(K k1, V k2)
{
   return map_KListV.containsKey(k1) && ((List<V>)map_KListV.get(k1)).contains(k2);
}

public String toString()
{
   StringBuilder s = new StringBuilder();
   if(map_KListV.size() == 0)
   {
      s.append("Map is empty.");
   }
   else
   {
      for(Iterator<K> I = keyIterator(); I.hasNext();)
      {
         K k = I.next();
         List<V> v = get(k);
         Iterator<V> II = v.iterator();
         while(II.hasNext())
         {
            s.append(k);
            s.append(":");
            s.append(II.next());
            s.append(";");
         }
      }
   }
   return s.toString();
}

public Map<K, List<V>> getMap_KListV()
{
   return map_KListV;
}

public void setMap_KListV(Map<K, List<V>> map_KListV)
{
   this.map_KListV = map_KListV;
}

public void remove(K k)
{
   this.map_KListV.remove(k);
}

public void remove(K k, V v)
{
   if(!map_KListV.containsKey(k))
      return;
   List<V> list = map_KListV.get(k);
   for(int i = list.size() - 1; i >= 0; i--)
   {
      if(list.get(i).equals(v))
         list.remove(i);
   }
   map_KListV.put(k, list);
}

public static void unittest()
{
   MapSet<String, Integer> sim = new MapSet<String, Integer>();
   sim.put("a", 1);
   sim.put("b", 2);
   sim.put("c", 3);
   Units.assertTrue(sim.size() == 3, "Size should be 3");
   List<Integer> r = sim.get("a");
   Units.assertTrue(r.size() == 1, "key a should have 1 results");
   Units.assertTrue(r.get(0) == 1, "1.st element of key a should be 1");
   sim.put("a", 4);
   Units.assertTrue(sim.size() == 3, "Size should be 3");
   Units.assertTrue(r.size() == 2, "key a should have 2 results");
   sim.remove("a", 4);
   Units.assertTrue(sim.size() == 3, "Size should be 3");
   sim.remove("b");
   Units.assertTrue(sim.size() == 2, "Size should be 2");
   sim.clear();
   Units.assertTrue(sim.size() == 0, "Size should be 0");
}
}
