package org.jivesoftware.smack.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

public class MultiMap<K, V> {
   public static final int DEFAULT_MAP_SIZE = 6;
   private static final int ENTRY_SET_SIZE = 3;
   private final Map<K, Set<V>> map;

   public MultiMap() {
      this(6);
   }

   public MultiMap(int size) {
      this.map = new LinkedHashMap(size);
   }

   public int size() {
      int size = 0;

      Set set;
      for(Iterator i$ = this.map.values().iterator(); i$.hasNext(); size += set.size()) {
         set = (Set)i$.next();
      }

      return size;
   }

   public boolean isEmpty() {
      return this.map.isEmpty();
   }

   public boolean containsKey(Object key) {
      return this.map.containsKey(key);
   }

   public boolean containsValue(Object value) {
      Iterator i$ = this.map.values().iterator();

      Set set;
      do {
         if (!i$.hasNext()) {
            return false;
         }

         set = (Set)i$.next();
      } while(!set.contains(value));

      return true;
   }

   public V getFirst(Object key) {
      Set<V> res = this.getAll(key);
      return res.isEmpty() ? null : res.iterator().next();
   }

   public Set<V> getAll(Object key) {
      Set<V> res = (Set)this.map.get(key);
      if (res == null) {
         res = Collections.emptySet();
      }

      return res;
   }

   public boolean put(K key, V value) {
      Set<V> set = (Set)this.map.get(key);
      boolean keyExisted;
      if (set == null) {
         Set<V> set = new LinkedHashSet(3);
         set.add(value);
         this.map.put(key, set);
         keyExisted = false;
      } else {
         set.add(value);
         keyExisted = true;
      }

      return keyExisted;
   }

   public V remove(Object key) {
      Set<V> res = (Set)this.map.remove(key);
      if (res == null) {
         return null;
      } else {
         assert !res.isEmpty();

         return res.iterator().next();
      }
   }

   public boolean removeOne(Object key, V value) {
      Set<V> set = (Set)this.map.get(key);
      if (set == null) {
         return false;
      } else {
         boolean res = set.remove(value);
         if (set.isEmpty()) {
            this.map.remove(key);
         }

         return res;
      }
   }

   public void putAll(Map<? extends K, ? extends V> map) {
      Iterator i$ = map.entrySet().iterator();

      while(i$.hasNext()) {
         Entry<? extends K, ? extends V> entry = (Entry)i$.next();
         this.put(entry.getKey(), entry.getValue());
      }

   }

   public void clear() {
      this.map.clear();
   }

   public Set<K> keySet() {
      return this.map.keySet();
   }

   public List<V> values() {
      List<V> values = new ArrayList(this.size());
      Iterator i$ = this.map.values().iterator();

      while(i$.hasNext()) {
         Set<V> set = (Set)i$.next();
         values.addAll(set);
      }

      return values;
   }

   public Set<Entry<K, V>> entrySet() {
      Set<Entry<K, V>> entrySet = new LinkedHashSet(this.size());
      Iterator i$ = this.map.entrySet().iterator();

      while(i$.hasNext()) {
         Entry<K, Set<V>> entries = (Entry)i$.next();
         K key = entries.getKey();
         Iterator i$ = ((Set)entries.getValue()).iterator();

         while(i$.hasNext()) {
            V value = i$.next();
            entrySet.add(new MultiMap.SimpleMapEntry(key, value));
         }
      }

      return entrySet;
   }

   private static class SimpleMapEntry<K, V> implements Entry<K, V> {
      private final K key;
      private V value;

      private SimpleMapEntry(K key, V value) {
         this.key = key;
         this.value = value;
      }

      public K getKey() {
         return this.key;
      }

      public V getValue() {
         return this.value;
      }

      public V setValue(V value) {
         V tmp = this.value;
         this.value = value;
         return tmp;
      }

      // $FF: synthetic method
      SimpleMapEntry(Object x0, Object x1, Object x2) {
         this(x0, x1);
      }
   }
}
