package com.packtpub.hibernatesearch.startup;

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

import javax.servlet.ServletContextEvent;

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 pomocnicza dla instancji uruchamianej jako węzeł podrzędny w klastrze.  Wyłącznie węzeł nadrzędny może bezpośrednio 
 * aktualizować nadrzędne indeksy Lucene.  Węzły podrzędne mogą aktualizować indeksy wyłącznie przesyłając wiadomości przez
 * JMS do węzła nadrzędnego, gdzie są następnie przetwarzane.
 * 
 * Dzięki implementacji interfejsu ServletContextListener, kontener servletów autoamtycznie wywoła metodę "contextInitialized" podczas
 * startu.  Wewątrz metody utworzymy i zindeksujemy około połowy danych testowych (tak aby zademonstować utworzenie drugiej połowy 
 * przez węzeł podrzędny). 
 * 
 * Poprzednie wersje tej klasy (nazywane "StartupDataLoader") miały adnotację @WebListener. Niemniej jednak w tej wersji 
 * polegamy na pliku "web.xml" definiującym właściwy listener... ponieważ nie jest wskazane, żeby aplikacja jednocześnie 
 * uruchomiła MasterNodeInitializer *oraz* SlaveNodeInitializer.
 */
public class SlaveNodeInitializer implements javax.servlet.ServletContextListener {
	
	Logger logger = LoggerFactory.getLogger(SlaveNodeInitializer.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("/hibernate-slave.cfg.xml");
			ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
			sessionFactory = configuration.buildSessionFactory(serviceRegistry);
		}
		return sessionFactory.openSession();
	}
	
	public void contextInitialized(ServletContextEvent event) {
		
		event.getServletContext().setAttribute("mode", "slave");

		// W celach demonstracyjnych utworzomy połowę danych podczas startu węzła nadrzędnego... a drugą połowę podczas startu węzła 
		// podrzędnego.  Po kilku sekundach oba węzły odświeżą lokalne kopie swoich indeksów używając nadrzędnego indeksu...  
		// i wszystkie encje App będą wyszukiwalne na wszystkich węzłach.  
		FullTextSession fullTextSession = Search.getFullTextSession( openSession() );
		fullTextSession.beginTransaction();
		
		//
		// Pobierz referencje do 5 urządzeń, które powinny być już utworzone w bazie danych przez węzeł nadrzędny
		//
		Device xPhone = (Device) fullTextSession.createQuery( "from Device as device where device.name = ?" ).setString(0, "xPhone").uniqueResult();
		Device xTablet = (Device) fullTextSession.createQuery( "from Device as device where device.name = ?" ).setString(0, "xTablet").uniqueResult();
		Device solarSystem = (Device) fullTextSession.createQuery( "from Device as device where device.name = ?" ).setString(0, "Solar System Phone").uniqueResult();
		Device flame = (Device) fullTextSession.createQuery( "from Device as device where device.name = ?" ).setString(0, "Flame Book Reader").uniqueResult();
		Device pc = (Device) fullTextSession.createQuery( "from Device as device where device.name = ?" ).setString(0, "Pecet").uniqueResult();
		
		//
		// Utwórz i zapisz 6 z 12 aplikacji razem z ich urządzeniami i komentarzami
		//		
		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);
		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 nasza 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);
		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);
		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);
		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);
		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);
		logger.info("Zapisuję " + mapJourney.getName());
		
		//
		// Zamknij i wyczyść sesję Hibernate
		//
		fullTextSession.getTransaction().commit();
		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();
		}
	}
	
}
