/**
 *
 */
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 do przechowywania wpisów obiekt typu List,
 * dzięki czemu większość operacji odbywa się w czasie liniowym.
 *
 * @author downey
 * @param <K>
 * @param <V>
 *
 */
public class MyLinearMap<K, V> implements Map<K, V> {

   private List<Entry> entries = new ArrayList<Entry>();

   public class Entry implements Map.Entry<K, V> {
      private K key;
      private V value;

      public Entry(K key, V value) {
         this.key = key;
         this.value = value;
      }

      @Override
      public K getKey() {
         return key;
      }
      @Override
      public V getValue() {
         return value;
      }
      @Override
      public V setValue(V newValue) {
         value = newValue;
         return value;
      }
   }

   @Override
   public void clear() {
      entries.clear();
   }

   @Override
   public boolean containsKey(Object target) {
      return findEntry(target) != null;
   }

   /**
    * Zwraca wpis, który zawiera docelowy klucz, albo wartość null, jeśli nie ma takiego klucza.
    *
    * @param target
    */
   private Entry findEntry(Object target) {
      // TODO: UZUPEŁNIJ TEN KOD!
      return null;
   }

   /**
    * Porównuje dwa klucze lub dwie wartości, prawidłowo obsługując wartość null.
    *
    * @param target
    * @param obj
    * @return
    */
   private boolean equals(Object target, Object obj) {
      if (target == null) {
         return obj == null;
      }
      return target.equals(obj);
   }

   @Override
   public boolean containsValue(Object target) {
      for (Map.Entry<K, V> entry: entries) {
         if (equals(target, entry.getValue())) {
            return true;
         }
      }
      return false;
   }

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

   @Override
   public V get(Object key) {
      // TODO: UZUPEŁNIJ TEN KOD!
      return null;
   }

   @Override
   public boolean isEmpty() {
      return entries.isEmpty();
   }

   @Override
   public Set<K> keySet() {
      Set<K> set = new HashSet<K>();
      for (Entry entry: entries) {
         set.add(entry.getKey());
      }
      return set;
   }

   @Override
   public V put(K key, V value) {
      // TODO: UZUPEŁNIJ TEN KOD!
      return null;
   }

   @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) {
      // TODO: UZUPEŁNIJ TEN KOD!
      return null;
   }

   @Override
   public int size() {
      return entries.size();
   }

   @Override
   public Collection<V> values() {
      Set<V> set = new HashSet<V>();
      for (Entry entry: entries) {
         set.add(entry.getValue());
      }
      return set;
   }

   /**
    * @param args
    */
   public static void main(String[] args) {
      Map<String, Integer> map = new MyLinearMap<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));
      }
   }

   /**
    * Zwraca referencję do 'entries'.
    *
    * To nie jest element interfejsu Map; kod ten realizuje funkcję metody
    * 'entrySet' w znacznie prostszy sposób niż ten, który przyjmujemy za prawidłowy.
    *
    * @return
    */
   protected Collection<? extends java.util.Map.Entry<K, V>> getEntries() {
      return entries;
   }
}