package com.packtpub.hibernatesearch.util;

import java.util.Arrays;
import java.util.HashSet;

import javax.servlet.ServletContextEvent;
import javax.servlet.annotation.WebListener;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.packtpub.hibernatesearch.domain.App;
import com.packtpub.hibernatesearch.domain.CustomerReview;
import com.packtpub.hibernatesearch.domain.Device;

/**
 * Klasa pomocniczna używana do zapełnienia wbudowanej bazy H2 testowymi danymi podczas startu aplikacji. Dzięki adnotacji @WebListener 
 * dostępnej w Servletach 3.0, metoda "contextInitialized" będzie automatycznie wywołana podczas startu kontenera servletów.
 */
@WebListener
public class StartupDataLoader implements javax.servlet.ServletContextListener {
	
	Logger logger = LoggerFactory.getLogger(StartupDataLoader.class);

	/**
	 * Nie powinna być używana bezpośrednio. Użyj openSession().
	 */
	private static SessionFactory sessionFactory;
	
	/**
	 * Konstruowanie nowej SessionFactory Hibernate dla każdego żądania HTTP powodowałoby problemy wydajnościowe. Ponieważ
	 * servlety Javy muszą być współbieżne, nie możemy zadeklarować by SessionFactory była publiczną statyczną zmienną.
	 * Ta metoda udostępnia współbieżny dostęp do SessionFactory, dzięki czemu metoda zapełniająca bazę danych przykładowymi danymi 
	 * i servlet wyszukujący mogą otwierać połączenia do bazy danych bardziej wydajnie. 
	 * 
	 * @return Session
	 */
	public static synchronized Session openSession() {
		if(sessionFactory == null) {
			Configuration configuration = new Configuration();
			configuration.configure();
			ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
			sessionFactory = configuration.buildSessionFactory(serviceRegistry);			
		}
		return sessionFactory.openSession();
	}

	/**
	 * Ta metoda jest uruchamiana automatycznie gdy uruchomiony zostaje mechanizm servletów.
	 */
	public void contextInitialized(ServletContextEvent event) {

		//
		// Utwórz sesję Hibernate i rozpocznij nową transakcję bazodanową.
		//
		FullTextSession fullTextSession = Search.getFullTextSession( openSession() );
		fullTextSession.beginTransaction();
		
		//
		// Utwórz 5 urządzeń
		//
		Device xPhone = new Device("Orange", "xPhone", null);
		Device xTablet = new Device("Orange", "xTablet", null);
		Device solarSystem = new Device("Song-Sung", "Solar System Phone", null);
		Device flame = new Device("Jungle", "Flame Book Reader", null);
		Device pc = new Device(null, "Pecet", null);
		
		//
		// Utwórz i zapisz 12 aplikacji z urządzeniami i komentarzami
		//
		App theCloud = new App(
				"Chmura!", 
				"cloud.jpg", 
				"Aplikacja Chmura! jest miejscem, w którym dzieją się cuda. Firmy działają w chmurach. Programiści nie potrzebują administratorów, jedzenia i picia w zasadzie też nie. Możesz oglądać telewizję na tablecie, siedzać na kanapie; nie musisz w ogóle patrzeć na telewizor! Ściągnij aplikację Chmura! z chmury i doświadcz tej cudownej potęgi!",
				"Biznes",
				7.99f);
		theCloud.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { xPhone, xTablet })) );
		CustomerReview theCloudReview1 = new CustomerReview("fanboy1984", 5, "Ta aplikacja sprawia, że mój xPhone jest jeszcze bardziej stylowy i trendy!");
		CustomerReview theCloudReview2 = new CustomerReview("anty.hipster", 1, "Nie rozumiem o co chodzi z tą 'Chmurą'.  Dla mnie to raczej marketingowy bełkot...");
		theCloud.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { theCloudReview1, theCloudReview2 })) );
		fullTextSession.save(theCloud);
		fullTextSession.index(theCloud);
		logger.info("Zapisuję " + theCloud.getName());

		App salesCloser = new App(
				"Zamykacz transakcji", 
				"pointing.jpg", 
				"Wysokoenergetyczna aplikacja wspomagająca energicznych sprzedawców. Twórz pełne energii kalendarze i arkusze. Gdy jesteś w mieście i energicznie nawiązujesz kontakty, chcesz prezentować swe naenergetyzowane foldery, pokazujące, że też masz w sobie mnóstwo energii.",
				"Biznes",
				5.99f);
		salesCloser.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { xPhone, solarSystem })) );
		CustomerReview salesCloserReview = new CustomerReview("GdzieJestKasa", 5, "Świetna aplikacja!  Jeżeli używałeś kiedyś 'Sales Commandera 2000', szybko połapiesz się w tym interfejsie.");
		salesCloser.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { salesCloserReview })) );
		fullTextSession.save(salesCloser);
		fullTextSession.index(salesCloser);
		logger.info("Zapisuję " + salesCloser.getName());

		App football = new App(
				"Międzynarodowy turniej piłkarski", 
				"ball.jpg", 
				"Ta mała aplikacja oferuje Ci radość gry w piłkę nożną, poza tym, że grasz na ekranie dotykowym, zamiast biegać i kopać albo w ogóle się ruszać. Poza tym zapewnia podobne wrażenia.",
				"Gry",
				1.99f);
		football.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { xTablet, flame })) );
		CustomerReview footballReview = new CustomerReview("PrawdziwyAmerykanin", 2, "Podpucha... To nie jest amerykański futbol!");
		football.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { footballReview })) );
		fullTextSession.save(football);
		fullTextSession.index(football);
		logger.info("Zapisuję " + football.getName());
		
		App crystal = new App(
				"Kolejna gra o kryształach", 
				"brilliant.jpg", 
				"Olśniewająca aplikacja, w której łączysz kryształy tego samego koloru, by zniknęły. Coś jak Tetris. W sumie przypomina tuzin innych gier, w których łączysz kryształy tego samego koloru.",
				"Gry",
				0.99f);
		crystal.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { flame, pc })) );
		CustomerReview crystalReview = new CustomerReview("KolejnyGracz", 3, "czemu ta gra jest dostępna tylko na dwóch urządzeniach?  Z tuzin klonów tej gry jest dostępny dla wszystkich urządzeń.  Powinniście deaktywować tą aplikację do czasu, gdy wszystkie urządzenia będą wspierane...");
		crystal.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { crystalReview })) );
		crystal.setActive(false);
		fullTextSession.save(crystal);
		
		// UWAGA:  Flaga "active" ma ustawioną wartość "false", encja nie powinna być indeksowana w Lucene.  Niestety FullTextSession.index() 
		// nie przestrzega zasad warunkowego indeksowania zdefiniowanych w EntityIndexingInterceptorze.  Musimy programowo zagwarantować, że 
		// FullTextSession.index() nie zostało wywołane dla obiektów których nie chcemy indeksować.  MassIndexer przestrzega warunkowego indeksowania.
		//fullTextSession.index(crystal);
		
		logger.info("Zapisuję " + crystal.getName());
		
		App pencilSharpener = new App(
				"Temperówka", 
				"pencil.jpg", 
				"Ostrz ołówki, wkładając je do gniazda Bluetooth i wciskając przycisk. Ta aplikacja wyciska ze sprzętu wszystko!",
				"Biznes",
				2.99f);
		pencilSharpener.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { xPhone, solarSystem })) );
		CustomerReview pencilSharpenerReview1 = new CustomerReview("brakuje.cyferek", 1, "Ta aplikacja jest niebezpieczna!  Myślę o pozwie.");
		CustomerReview pencilSharpenerReview2 = new CustomerReview("Prawnik", 5, "@brakuje.cyferek  Napisz do mnie.  Pogadamy...");
		pencilSharpener.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { pencilSharpenerReview1, pencilSharpenerReview2 })) );
		fullTextSession.save(pencilSharpener);
		fullTextSession.index(pencilSharpener);
		logger.info("Zapisuję " + pencilSharpener.getName());
		
		App staplerTracker = new App(
				"Namierzacz zszywaczy", 
				"stapler.jpg", 
				"Czy ktoś ciągle pożycza sobie Twój zszywacz? To typowy problem w biurach. Nasza aplikacja biznesowa sprawi, że już nigdy nie będziesz musiał się zastanawiać, gdzie podziewa się Twój zszywacz.",
				"Biznes",
				0.99f);
		staplerTracker.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { pc })) );
		CustomerReview staplerTrackerReview = new CustomerReview("mike.bolton", 3, "'PC LOAD LETTER'?  O co w ogóle chodzi?!?");
		staplerTracker.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { staplerTrackerReview })) );
		fullTextSession.save(staplerTracker);
		fullTextSession.index(staplerTracker);
		logger.info("Persisting " + staplerTracker.getName());
		
		App frustratedFlamingos = new App(
				"Sfrustrowane Flamingi", 
				"flamingo.jpg", 
				"Mała aplikacja pozwalająca na ciskanie wielkimi ptakami na lewo i prawo bez powodu. Chyba nie zastanawiasz się czemu są sfrustrowane?",
				"Gry",
				0.99f);
		frustratedFlamingos.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { xPhone, xTablet, solarSystem, flame, pc })) );
		CustomerReview frustratedFlamingosReview = new CustomerReview("Procarz", 4, "LOL, uwielbiam katapulotwać flamingi w krowy! Trochę mnie wkurza, że reklama trochę zasłania obszar gry.");
		frustratedFlamingos.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { frustratedFlamingosReview })) );
		fullTextSession.save(frustratedFlamingos);
		fullTextSession.index(frustratedFlamingos);
		logger.info("Zapisuję " + frustratedFlamingos.getName());
		
		App grype = new App(
				"Wideokonferencje Grype", 
				"laptop.jpg", 
				"Dzwoń za darmo lokalnie i za granicę, również z transmisją obrazu, używając domowego połączenia internetowego i naszego produktu. W sumie nasz aplikacja zadziała jeszcze lepiej jeśli skorzystasz z połączenia internetowego Twojego pracodawcy!",
				"Internet",
				3.99f);
		grype.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { xPhone, xTablet, solarSystem, pc })) );
		CustomerReview grypeReview = new CustomerReview("biurowy.luzak", 4, "Szkoda, że dodali obsługę wideo do najnowszej wersji. Wcześniej nie musiałem się ubierać.");
		grype.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { grypeReview })) );
		fullTextSession.save(grype);
		fullTextSession.index(grype);
		logger.info("Zapisuję " + grype.getName());
		
		App eReader = new App(
				"Czytnik e-booków", 
				"book.jpg", 
				"Nasza aplikacja sprawi, że będziesz mógł czytać książki na komputerze albo na dowolnym urządzeniu mobilnym.  Polecamy \"Hibernate Search by Example\".",
				"Media",
				1.99f);
		eReader.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { xPhone, xTablet, solarSystem, flame, pc })) );
		CustomerReview eReaderReview = new CustomerReview("StevePerkins", 5, "Książka 'Hibernate Search by Example' jest super!  Dzięki za rekomendacje!");
		eReader.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { eReaderReview })) );
		fullTextSession.save(eReader);
		fullTextSession.index(eReader);
		logger.info("Zapisuję " + eReader.getName());
		
		App domeBrowser = new App(
				"Przeglądarka pod kopułą", 
				"orangeswirls.jpg", 
				"Ta cudowna aplikacja pozwala nam śledzić Twoją aktywność w Internecie. Możemy odgadnąć, gdzie mieszkasz, co jadłeś dzisiaj na śniadanie, albo jakie są Twoje największe sekrety. Aha, w aplikację jest jeszcze wbudowana przeglądarka stron WWW.",
				"Internet",
				0);
		domeBrowser.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { solarSystem, flame, pc })) );
		CustomerReview domeBrowserReview = new CustomerReview("oni.wiedza", 1, "Odinstalowałem tą aplikację.  Jeżeli udało im się sfałszować lądowanie na księżycu, z pewnością wykorzystają historię mojej przeglądarki przeciwko mnie.");
		domeBrowser.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { domeBrowserReview })) );
		fullTextSession.save(domeBrowser);
		fullTextSession.index(domeBrowser);
		logger.info("Zapisuję " + domeBrowser.getName());
		
		App athenaRadio = new App(
				"Internetowe radio Atena", 
				"jamming.jpg", 
				"Słuchaj swoich ulubionych piosenek w radiu internetowym! Kiedy polubisz jakiś utwór, nasza aplikacja zagra Ci więcej podobnych kompozycji. A przynajmniej zagra więcej piosenek ... Żeby być szczerym, czasem wcale nie są takie podobne  :(",
				"Media",
				3.99f);
		athenaRadio.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { xPhone, xTablet, solarSystem, flame, pc })) );
		CustomerReview athenaRadioReview = new CustomerReview("lskinner", 5, "Chciałem'Free Bird', dostałem 'Free Bird'.  Czego chcieć więcej?");
		athenaRadio.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { athenaRadioReview })) );
		fullTextSession.save(athenaRadio);
		fullTextSession.index(athenaRadio);
		logger.info("Zapisuję " + athenaRadio.getName());
		
		App mapJourney = new App(
				"Mapa podróży", 
				"compass.jpg", 
				"Potrzebujesz wskazówek, by dotrzeć do celu?  Nasza aplikacja GPS na pewno wygeneruje wystarczająco dużo propozycji, byś dotarł do celu!  Prędzej czy później.",
				"Podróże",
				0.99f);
		mapJourney.setSupportedDevices( new HashSet<Device>(Arrays.asList(new Device[] { xPhone, solarSystem, pc })) );
		CustomerReview mapJourneyReview = new CustomerReview("Zagubiony", 3, "Nic szczególnego... ale ciągle o niebo lepsze od map Orange.");
		mapJourney.setCustomerReviews( new HashSet<CustomerReview>(Arrays.asList(new CustomerReview[] { mapJourneyReview })) );
		fullTextSession.save(mapJourney);
		fullTextSession.index(mapJourney);
		logger.info("Zapisuję " + mapJourney.getName());
		
		fullTextSession.getTransaction().commit();

		// Ten fragment jest nadmiarowy ponieważ indeksowaliśmy encje indywidualnie za pomocą "fullTextSession.index()".  
		// Ale to dobra okazja żeby zaprezentować jak grupowo indeksować za pomocą MassIndexera
		try {
			fullTextSession.createIndexer().startAndWait();
		} catch (InterruptedException e) {
			logger.error("MassIndexer interrupted: " + e.getClass().getName() + ", " + e.getMessage());
		}
		
		//
		// Zamknij i wyczyść sesję Hibernate
		//
		fullTextSession.close();
	} 

	/**
	 * Ta metoda jest wywoływana gdy mechanizm servletów jest wyłączany. Zamyka Hibernate SessionFactory jeżeli jeszcze jest otwarta.
	 */
	public void contextDestroyed(ServletContextEvent event) {
		if(!sessionFactory.isClosed()) {
			sessionFactory.close();
		}
	}

}
