package com.allendowney.thinkdast;

import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import redis.clients.jedis.Jedis;


/**
 * Reprezentuje wyniki zapytania.
 *
 */
public class WikiSearch {

   // mapa odwzorowująca ciągi URL wskazujące strony, które zawierają słowo (lub słowa), na oceny trafności
   private Map<String, Integer> map;

   /**
    * Konstruktor.
    *
    * @param map
    */
   public WikiSearch(Map<String, Integer> map) {
      this.map = map;
   }

   /**
    * Odnajduje trafność danego ciągu URL.
    *
    * @param url
    * @return
    */
   public Integer getRelevance(String url) {
      Integer relevance = map.get(url);
      return relevance==null ? 0: relevance;
   }

   /**
    * Wyświetla zawartość w porządku zgodnym z częstotliwością występowania słów.
    *
    * @param
    */
   private  void print() {
      List<Entry<String, Integer>> entries = sort();
      for (Entry<String, Integer> entry: entries) {
         System.out.println(entry);
      }
   }

   /**
    * Oblicza sumę zbiorów wyników dwóch wyszukiwań.
    *
    * @param that
    * @return Nowy obiekt WikiSearch.
    */
   public WikiSearch or(WikiSearch that) {
      Map<String, Integer> union = new HashMap<String, Integer>(map);
      for (String term: that.map.keySet()) {
         int relevance = totalRelevance(this.getRelevance(term), that.getRelevance(term));
         union.put(term, relevance);
      }
      return new WikiSearch(union);
   }

   /**
    * Oblicza iloczyn zbiorów wyników dwóch wyszukiwań.
    *
    * @param that
    * @return Nowy obiekt WikiSearch.
    */
   public WikiSearch and(WikiSearch that) {
      Map<String, Integer> intersection = new HashMap<String, Integer>();
      for (String term: map.keySet()) {
         if (that.map.containsKey(term)) {
            int relevance = totalRelevance(this.map.get(term), that.map.get(term));
            intersection.put(term, relevance);
         }
      }
      return new WikiSearch(intersection);
   }

   /**
    * Oblicza różnicę zbiorów wyników dwóch wyszukiwań.
    *
    * @param that
    * @return Nowy obiekt WikiSearch.
    */
   public WikiSearch minus(WikiSearch that) {
      Map<String, Integer> difference = new HashMap<String, Integer>(map);
      for (String term: that.map.keySet()) {
         difference.remove(term);
      }
      return new WikiSearch(difference);
   }

   /**
    * Oblicza trafność wyszukiwania wielu słów.
    *
    * @param rel1: ocena trafności dla pierwszego wyszukiwania
    * @param rel2: ocena trafności dla drugiego wyszukiwania
    * @return
    */
   protected int totalRelevance(Integer rel1, Integer rel2) {
      // prosty przypadek na start: trafność jest sumą częstotliwości występowania słów.
      return rel1 + rel2;
   }

   /**
    * Sortuje wyniki według trafności.
    *
    * @return Lista wpisów z URL-ami i trafnością.
    */
   public List<Entry<String, Integer>> sort() {
      // UWAGA: cel ten można osiągnąć w bardziej zwięzły sposób w Javie 8.  Patrz:
      // http://stackoverflow.com/questions/109383/sort-a-mapkey-value-by-values-java

      // utwórz listę wpisów
      List<Entry<String, Integer>> entries = 
            new LinkedList<Entry<String, Integer>>(map.entrySet());
      
      // utwórz obiekt Comparator do sortowania
      Comparator<Entry<String, Integer>> comparator = new Comparator<Entry<String, Integer>>() {
            @Override
            public int compare(Entry<String, Integer> e1, Entry<String, Integer> e2) {
                return e1.getValue().compareTo(e2.getValue());
            }
        };
        
        // posortuj i zwróć wpisy
      Collections.sort(entries, comparator);
      return entries;
   }


   /**
    * Przeprowadza wyszukiwanie i tworzy obiekt WikiSearch.
    *
    * @param term
    * @param index
    * @return
    */
   public static WikiSearch search(String term, JedisIndex index) {
      Map<String, Integer> map = index.getCounts(term);
      return new WikiSearch(map);
   }

   public static void main(String[] args) throws IOException {

      // utwórz JedisIndex
      Jedis jedis = JedisMaker.make();
      JedisIndex index = new JedisIndex(jedis);

      // wyszukaj pierwsze słowo
      String term1 = "java";
      System.out.println("Zapytanie: " + term1);
      WikiSearch search1 = search(term1, index);
      search1.print();

      //  wyszukaj drugie słowo
      String term2 = "programming";
      System.out.println("Zapytanie: " + term2);
      WikiSearch search2 = search(term2, index);
      search2.print();

      // oblicz iloczyn zbiorów wyników wyszukiwań
      System.out.println("Zapytanie: " + term1 + " I " + term2);
      WikiSearch intersection = search1.and(search2);
      intersection.print();
   }
}