package com.packtpub.hibernatesearch.domain;

import java.io.Serializable;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;

import org.codehaus.jackson.annotate.JsonIgnore;
import org.hibernate.search.annotations.ContainedIn;
import org.hibernate.search.annotations.Field;

/**
 * Prosta klasa encji reprezentująca sprzęt elektroniczny... posiada nazwę, nazwę producenta, oraz kolekcję aplikacji 
 * wspieranych przez urządzenie.
 * 
 * Adnotacja @Entity informuje Hibernate by traktowało tę klasę jako samodzielną encję, z cyklem życia nie uzależnionym
 * od innych klas encji. Niemniej jednak, w związku z tym, że ta klasa NIE JEST adnotowana @Indexed, Hibernate 
 * Search nie utworzy dla niej indeksu Lucene. Wszystkie informacje wskazane do indeksowania trafią do indeksu 
 * Lucene obiektu w którym się zawiera.
 */
@Entity
public class Device implements Serializable {

	private static final long serialVersionUID = 1L;
	
	/**
	 * Automatycznie generowany klucz główny, nie ma potrzeby nadawania mu wartości. 
	 */	
	@Id
  @GeneratedValue
  private Long id;
	
	/**
	 * Nazwa producenta urządzenia.  Adnotacja @Column informuje Hibernate by zmapował to pole do 
	 * kolumny w bazie danych, natomiast adnotacja @Field nazakuje Hibernate Search by zmapował je jako pole 
	 * w indeksie Lucene.
	 */	
	@Column
	@Field
  private String manufacturer;
	
	/**
	 * Nazwa urządzenia.  Adnotacja @Column informuje Hibernate by zmapował to pole do 
	 * kolumny w bazie danych, natomiast adnotacja @Field nazakuje Hibernate Search by zmapował je jako pole 
	 * w indeksie Lucene.
	 */	
	@Column
	@Field
  private String name;

	/**
	  * Kolekcja powiązanych encji typu App, reprezentujących aplikacje wspierane przez to urządzenie.  
	 * 
	 * Adnotacja @ManyToMany informuje Hibernate, że aplikacja może być wspierana przez wiele urządzeń oraz, że urządzenie 
	 * może wspierać wiele aplikacji.  
	 * 
	 *      Pomimo, że relacja między aplikacjami i urządzeniami jest dwustronna, zakładamy że klasa App będzie nadrzędną
	 *      encją.  Element "mappedBy" informuje Hibernate która zmienna (np. App.supportedDevices) odwołuje się do tej encji.
	 * 
	 *      Elementy "fetch" oraz "cascade" są ustawione identycznie jak dla App.supportedDevices.
	 *      By poznać więcej szczegółów przejrzyj komentarze do klasy App.java.
	 * 
	 * Adnotacja Hibernate Search @ContainedIn jest odpowiednikiem @IndexedEmbedded (użytej dla App.SupportedDevices). 
	 * Instruuje Hibernate Search by dołączyło informacje o urządzeniach w indeksie Lucene encji App. 
	 * 
	 * Adnotacja @JsonIgnore jest używana przez Jersey i Jackson w RESTowym serwisie pobierającym informacje o aplikacji 
	 * do zapobiegania cyklicznym zależnościom podczas konwersji obiektu App do JSONowej reprezentacji. 
	 * "App.supportedDevices" byłoby przekonwertowane do tablicy obiektów typu Device, każdy z nich ma pole "supportedApps"
	 * które byłoby odwzorowane na tablicę aplikacji, które zawierają tablicę urządzeń ...
	 * Za pomocą tej adnotacji sprawiamy, że "Device.supportedApps" nie jest konwertowane 
	 * dzięki czemu zapobiegniemy cyklicznej zależności jednocześnie nie zaburzając wyświetlania wyników.
	 */
	@ManyToMany(mappedBy="supportedDevices", fetch=FetchType.EAGER, cascade = { CascadeType.ALL })
	@ContainedIn
	@JsonIgnore
	private Set<App> supportedApps;

	/**
	 * Domyślny, pusty konstruktor.
	 */	
	public Device() {
	}
	
	/**
	 * Wygodny konstruktor ustawiający wartości wszystkich pól jednocześnie.  
	 */
	public Device(String manufacturer, String name, Set<App> supportedApps) {
		this.manufacturer = manufacturer;
		this.name = name;
		this.supportedApps = supportedApps;
	}
	
	//
	// GETTERY I SETTERY
	//
	
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getManufacturer() {
		return manufacturer;
	}

	public void setManufacturer(String manufacturer) {
		this.manufacturer = manufacturer;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Set<App> getSupportedApps() {
		return supportedApps;
	}

	public void setSupportedApps(Set<App> supportedApps) {
		this.supportedApps = supportedApps;
	}

}
