package com.allendowney.thinkdast;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Implementacja interfejsu Map wykorzystująca kolekcję MyLinearMap i metode
 * 'hashCode' w celu określenia, w której mapie powinien się znaleźć każdy klucz.
 *
 * @author downey
 * @param <K>
 * @param <V>
 *
 */
public class MyBetterMap<K, V> implements Map<K, V> {

   // klasa MyBetterMap wykorzystuje kolekcję MyLinearMap
   protected List<MyLinearMap<K, V>> maps;

   /**
    * Inicjalizuje mapę za pomocą dwóch podmap.
    *
    */
   public MyBetterMap() {
      makeMaps(2);
   }

   /**
    * Tworzy kolekcję 'k' klas MyLinearMap
    *
    * @param k
    */
   protected void makeMaps(int k) {
      maps = new ArrayList<MyLinearMap<K, V>>(k);
      for (int i=0; i<k; i++) {
         maps.add(new MyLinearMap<K, V>());
      }
   }

   @Override
   public void clear() {
      // wyczyść podmapy
      for (int i=0; i<maps.size(); i++) {
         maps.get(i).clear();
      }
   }

   /**
    * Korzysta z metody hashCode w celu odnalezienia mapy, która powinna zawierać dany klucz.
    *
    * @param key
    * @return
    */
   protected MyLinearMap<K, V> chooseMap(Object key) {
      int index = key==null ? 0 : Math.abs(key.hashCode()) % maps.size();
      return maps.get(index);
   }

   @Override
   public boolean containsKey(Object target) {
      // aby znaleźć klucz, trzeba przeszukać tylko jedna mapę
      // TODO: UZUPEŁNIJ TEN KOD!
      return false;
   }

   @Override
   public boolean containsValue(Object target) {
      // aby znaleźć wartość, trzeba przeszukać wszystkie mapy
      // TODO: UZUPEŁNIJ TEN KOD!
      return false;
   }

   @Override
   public Set<Map.Entry<K, V>> entrySet() {
      throw new UnsupportedOperationException();
   }

   @Override
   public V get(Object key) {
      MyLinearMap<K, V> map = chooseMap(key);
      return map.get(key);
   }

   @Override
   public boolean isEmpty() {
      return size() == 0;
   }

   @Override
   public Set<K> keySet() {
      // dodawanie keySetów należących do podmap
      Set<K> set = new HashSet<K>();
      for (MyLinearMap<K, V> map: maps) {
         set.addAll(map.keySet());
      }
      return set;
   }

   @Override
   public V put(K key, V value) {
      MyLinearMap<K, V> map = chooseMap(key);
      return map.put(key, value);
   }

   @Override
   public void putAll(Map<? extends K, ? extends V> map) {
      for (Map.Entry<? extends K, ? extends V> entry: map.entrySet()) {
         put(entry.getKey(), entry.getValue());
      }
   }

   @Override
   public V remove(Object key) {
      MyLinearMap<K, V> map = chooseMap(key);
      return map.remove(key);
   }

   @Override
   public int size() {
      // dodawanie rozmiarów podmap
      int total = 0;
      for (MyLinearMap<K, V> map: maps) {
         total += map.size();
      }
      return total;
   }

   @Override
   public Collection<V> values() {
      // dodawanie valueSetów należących do podmap
      Set<V> set = new HashSet<V>();
      for (MyLinearMap<K, V> map: maps) {
         set.addAll(map.values());
      }
      return set;
   }

   /**
    * @param args
    */
   public static void main(String[] args) {
      Map<String, Integer> map = new MyBetterMap<String, Integer>();
      map.put("Słowo1", 1);
      map.put("Słowo2", 2);
      Integer value = map.get("Słowo1");
      System.out.println(value);

      for (String key: map.keySet()) {
         System.out.println(key + ", " + map.get(key));
      }
   }
}