package com.packtpub.hibernatesearch.servlet;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hibernate.Session;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.packtpub.hibernatesearch.domain.App;
import com.packtpub.hibernatesearch.util.StartupDataLoader;

/**
 * Servlet implementujący kontroler/model obsługujący przeszukiwanie i renderowanie wyników przy pomocy widoku w JSP/JSTL.
 * Adnotacja @Webservlet, dostępna w specyfikacji servletów w wersji 3.0, mapuje ten servlet do URLa "search (np. 
 *"http://localhost:8080/search"). We wcześniejszych wersjach specyfikacji ta konfiguracja znajdowałaby się w pliku "web.xml".
 * Podstawowa logika tej operacji wyszukiwania może być zrefaktoryzowana w celu adaptacji do aplikacji napisanych w Springu, JSFie
 * lub dowolnym innym Javowym frameworku do aplikacji internetowych.
 */
@SuppressWarnings("serial")
@WebServlet("search")
public class SearchServlet extends HttpServlet {

	/**
	 * W tej metodzie zaimplementowane zostały główne funkcjonalności wyszukiwania dla tego servletu. Metoda jest automatycznie wywoływana
	 * przy każdym wywołaniu żądania HTTP POST do zmapowanego adresu URL.
	 */
	@SuppressWarnings("unchecked")
	@Override	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		Logger logger = LoggerFactory.getLogger(SearchServlet.class);
		
		// Pobierz słowa kluczowe użytkownika pobrane ze zmiennych CGI
		String searchString = request.getParameter("searchString");
		logger.info("Otrzymałem [" + searchString + "]");

		// Rozpocznij sesję Hibernate.
		Session session = StartupDataLoader.openSession();
		
		// Utwórz wrapper Hibernate Search wokół czystej sesji Hibernate
		FullTextSession fullTextSession = Search.getFullTextSession(session);

		// Rozpocznij transkację. W tym przypadku nie jest to niezbędne, ale jest tzw. dobrą praktyką.
		fullTextSession.beginTransaction();

		// Utwórz obiekt typu QueryBuilder pochodzący od Hibernate Search i utwórz go dla odpowiedniego indeksu Lucene (w naszym przypadku indeksu "App").
		QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity( App.class ).get();
		
		// Użyj QueryBuildera by zbudować w Lucene zapytanie z użyciem słów kluczowych, porównujące słowa kluczowe wprowadzone przez użytkownika z polami 
		// name i description w klasie App, pola name powiązanych aplikacji oraz pola comments wbudowanych obiektów klasy CustomerReview.
		org.apache.lucene.search.Query luceneQuery = queryBuilder
			.keyword()
			.onFields("name", "description", "supportedDevices.name", "customerReviews.comments")
			.matching(searchString)
			.createQuery();
		org.hibernate.Query hibernateQuery = fullTextSession.createFullTextQuery(luceneQuery, App.class);
		
		List<App> apps = hibernateQuery.list();
		logger.info("Znalazłem " + apps.size() + " wyników wyszukiwania");

		// Odłącz wyniki z sesji Hibernate (by uniknąć niechcianej interakcji między warstwą prezentacji i Hibernate,
		// gdy powiązane urządzenia lub wbudowane obiekty typu CustomerReview są przeglądane).
		fullTextSession.clear();

		// Umieść wyniki wyszukiwania w obiekcie żądania HTTP. 
		request.setAttribute("apps", apps);

		// Opróżnij i zamknij sesję Hibernate.
		fullTextSession.getTransaction().commit();
		session.close();
		
		// Przekaż żądanie HTTP, wraz z wynikami wyszukiwania, do widoku zbudowanego JSP/JSTL w celu wyrenderowania strony z wynikami
		getServletContext().getRequestDispatcher("/WEB-INF/pages/search.jsp").forward(request, response);
	}

	/**
	 * Ta metoda jest wywoływana za każdym razem gdy zostanie wywołane żądanie HTTP GET do zmapowanego URLa.  Dla naszego servletu nie ma znaczenia, 
	 * czy zostanie wywołany metodą POST czy GET. W związku z tym przekierowujemy żądanie do metody "doPost()".
	 */
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {	
		this.doPost(request, response);
	}
	
}
