package com.allendowney.thinkdast;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;


/**
 * Hermetyzuje odwzorowanie wyszukiwanego słowa na częstotliwość (liczbę wystąpień).
 *
 * @author downey
 *
 */
public class TermCounter {

   private Map<String, Integer> map;
   private String label;

   public TermCounter(String label) {
      this.label = label;
      this.map = new HashMap<String, Integer>();
   }

   public String getLabel() {
      return label;
   }

   /**
    * Zwraca sumę wszystkich liczb wystąpień.
    *
    * @return
    */
   public int size() {
      int total = 0;
      for (Integer value: map.values()) {
         total += value;
      }
      return total;
   }

   /**
    * Przyjmuje kolekcję Elements i zlicza zawarte w niej słowa.
    *
    * @param paragraphs
    */
   public void processElements(Elements paragraphs) {
      for (Node node: paragraphs) {
         processTree(node);
      }
   }

   /**
    * Znajduje obiekty klasy TextNode w drzewie DOM oraz zlicza zawarte w nich słowa.
    *
    * @param root
    */
   public void processTree(Node root) {
      // UWAGA: w celu znalezienia obiektów klasy TextNode moglibyśmy użyć metody select,
      // jednak skorzystamy z iteratora drzewa, ponieważ już nim dysponujemy.
      for (Node node: new WikiNodeIterable(root)) {
         if (node instanceof TextNode) {
            processText(((TextNode) node).text());
         }
      }
   }

   /**
    * Dzieli 'text' na słowa i zlicza je.
    *
    * @param text  Tekst do przetworzenia.
    */
   public void processText(String text) {
      // zastępuje znaki przestankowe spacjami, konwertuje tekst na małe litery oraz rozdziela, wykorzystując spacje
      String[] array = text.replaceAll("\\pP", " ").
                          toLowerCase().
                          split("\\s+");
      
      for (int i=0; i<array.length; i++) {
         String term = array[i];
         incrementTermCount(term);
      }
   }

   /**
    * Inkrementuej liczbę wystąpień związaną ze słowem przekazanym za pomocą argumentu 'term'.
    *
    * @param term
    */
   public void incrementTermCount(String term) {
      // System.out.println(term);
      put(term, get(term) + 1);
   }

   /**
    * Dodaje do mapy słowo z podanym licznikiem.
    *
    * @param term
    * @param count
    */
   public void put(String term, int count) {
      map.put(term, count);
   }

   /**
    * Zwraca liczbę wystąpień związaną z tym słowem lub 0, gdy słowo to nie występuje.
    *
    * @param term
    * @return
    */
   public Integer get(String term) {
      Integer count = map.get(term);
      return count == null ? 0 : count;
   }

   /**
    * Zwraca zbiór słów, które zostały policzone.
    *
    * @return
    */
   public Set<String> keySet() {
      return map.keySet();
   }

   /**
    * Wyświetla słowa i ich liczby wystąpień w przypadkowej kolejności.
    */
   public void printCounts() {
      for (String key: keySet()) {
         Integer count = get(key);
         System.out.println(key + ", " + count);
      }
      System.out.println("Suma wszystkich wystąpień = " + size());
   }

   /**
    * @param args
    * @throws IOException
    */
   public static void main(String[] args) throws IOException {
      String url = "https://en.wikipedia.org/wiki/Java_(programming_language)";

      WikiFetcher wf = new WikiFetcher();
      Elements paragraphs = wf.fetchWikipedia(url);

      TermCounter counter = new TermCounter(url.toString());
      counter.processElements(paragraphs);
      counter.printCounts();
   }
}